Author: axel Date: 2010-06-03 22:07:47 +0200 (Thu, 03 Jun 2010) New Revision: 1683
Added: trunk/bmx/ trunk/bmx/Makefile trunk/bmx/allocate.c trunk/bmx/allocate.h trunk/bmx/avl.c trunk/bmx/avl.h trunk/bmx/bmx.c trunk/bmx/bmx.h trunk/bmx/control.c trunk/bmx/control.h trunk/bmx/cyassl/ trunk/bmx/cyassl/config.h trunk/bmx/cyassl/misc.c trunk/bmx/cyassl/misc.h trunk/bmx/cyassl/sha.c trunk/bmx/cyassl/sha.h trunk/bmx/cyassl/types.h trunk/bmx/hna.c trunk/bmx/hna.h trunk/bmx/iid.c trunk/bmx/iid.h trunk/bmx/lib/ trunk/bmx/lib/Makefile trunk/bmx/lib/bmx_uci_config/ trunk/bmx/lib/bmx_uci_config/Makefile trunk/bmx/lib/bmx_uci_config/uci_config.c trunk/bmx/lib/bmx_uci_config/uci_config.h trunk/bmx/list.c trunk/bmx/list.h trunk/bmx/msg.c trunk/bmx/msg.h trunk/bmx/plugin.c trunk/bmx/plugin.h trunk/bmx/route.c trunk/bmx/route.h trunk/bmx/schedule.c trunk/bmx/schedule.h Modified: trunk/batman/Makefile Log: starting BMX-v0.4 development cycle, (incompatible with BatMan-eXperimental v0.3 branch)
Modified: trunk/batman/Makefile =================================================================== --- trunk/batman/Makefile 2010-06-02 15:47:07 UTC (rev 1682) +++ trunk/batman/Makefile 2010-06-03 20:07:47 UTC (rev 1683) @@ -29,7 +29,7 @@ #NO_POLICY_ROUTING = -DNO_POLICY_ROUTING
CC = gcc -CFLAGS += -pedantic -Wall -W -O1 -g3 -std=gnu99 +CFLAGS += -pedantic -Wall -W -Os -g3 -std=gnu99 EXTRA_CFLAGS = -DDEBUG_MALLOC -DMEMORY_USAGE -DPROFILE_DATA $(NO_POLICY_ROUTING) -DREVISION_VERSION=$(REVISION_VERSION) LDFLAGS += -lpthread
Added: trunk/bmx/Makefile =================================================================== --- trunk/bmx/Makefile (rev 0) +++ trunk/bmx/Makefile 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,168 @@ +# Copyright (C) 2010 BMX contributors: +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 of the GNU General Public +# License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA + +REVISION_VERSION = 0$(shell if [ -d .svn ]; then which svn > /dev/null && which sed > /dev/null && which awk > /dev/null && { svn info | grep "Rev:" | sed -e '1p' -n | awk '{print $$4}';} fi ) + + +##CFLAGS ?= -I./lua-5.1.4/src -I./cyassl-1.4.0/include -O0 -ggdb3 +##LDFLAGS ?= -L./lua-5.1.4/src -L./cyassl-1.4.0/src/.libs + +#CFLAGS ?= -I$(shell pwd)/cyassl-1.4.0/ctaocrypt/include -DHAVE_CONFIG_H +#LDFLAGS ?= -L./cyassl-1.4.0/src/.libs + + +CFLAGS += -pedantic -Wall -W -Wno-unused-parameter -Os -g3 -std=gnu99 -I./ \ + -DHAVE_CONFIG_H -DREVISION_VERSION=$(REVISION_VERSION) -DDEBUG_MALLOC -DMEMORY_USAGE + +LDFLAGS += -Wl,-export-dynamic -ldl -g3 -lm + +#-lcyassl +#LDFLAGS += -shared -lssl -lcrypto +# -static +# -pg + + +#CFLAGS += -DTESTDEBUG +#CFLAGS += -DAVL_DEBUG -DAVL_TEST +#CFLAGS += -DPROFILING -DEXTREME_PARANOIA -D + +#EXTRA_CFLAGS += +#EXTRA_LDFLAGS += + +# Recommended defines and approximate binary sizes with gcc-x86 +# -static +# -pedantic -Wall -W -Wno-unused-parameter -O1 -g3 -std=gnu99 +# -pg # "-pg" with openWrt toolchain results in "gcrt1.o: No such file" ?! +# + +# compared to -O1 stripped: +# -Os - ~29k + +# -DDEBUG_MALLOC + ~0k +# -DMEMORY_USAGE + ~1k + +# optional defines (you may disable these features if you dont need it): +# -DNOTRAILER - ~3K +# -DNODEBUGALL - ~13k +# -DLESS_OPTIONS - ~8K +# +# -DNOTUNNEL (only affects this node) - ~23k +# -DNOSRV (only affects this node) - ~3k +# -DNOVIS (only affects this node) - ~2k + +# -DNODEPRECATED (for backward compatibility) - ~2k + +# experimental or advanced defines (please dont touch): +# -DNOHNA (affects all nodes in network) - ~6k +# -DNO_PARANOIA (makes bug-hunting impossible) - ~2k +# -DEXTREME_PARANOIA +# -DEXTDEBUG (this eats your cpu) + ~0k +# -DTESTDEBUG (testing syntax of __VA_ARGS__ dbg...() macros) +# -DWITHUNUSED (includes yet unused stuff) +# -DPROFILING (no static functions) + + +SBINDIR = $(INSTALL_PREFIX)/usr/sbin + +LOG_BRANCH= trunk/bmx2 + +SRC_FILES= "(.c)|(.h)|(Makefile)|(INSTALL)|(LIESMICH)|(README)|(THANKS)|(./posix)|(./linux)|(./man)|(./doc)" + +SRC_C= bmx.c msg.c plugin.c list.c allocate.c avl.c iid.c hna.c control.c schedule.c route.c cyassl/sha.c +SRC_H= bmx.h msg.h plugin.h list.h allocate.h avl.h iid.h hna.h control.h schedule.h route.h cyassl/sha.h +OBJS= $(SRC_C:.c=.o) + +# +# + + +PACKAGE_NAME= bmxd +BINARY_NAME= bmxd + + + +SOURCE_VERSION_HEADER= bmx.h + + +BAT_VERSION= $(shell grep "^#define SOURCE_VERSION " $(SOURCE_VERSION_HEADER) | sed -e '1p' -n | awk -F '"' '{print $$2}' | awk '{print $$1}') +FILE_NAME= $(PACKAGE_NAME)_$(BAT_VERSION)-rv$(REVISION_VERSION)_$@ + +NOW= $(shell date +%Y%m%d%H%M)-$(REVISION_VERSION) + +IPKG_DEPENDS= "kmod-tun" + +SNAPSHOT_DIR= ../bmx-snapshots + + +all: + $(MAKE) $(BINARY_NAME) + # further make targets: help, libs, build_all, strip[_libs|_all], install[_libs|_all], clean[_libs|_all] + +libs: all + $(MAKE) -C lib all CORE_CFLAGS='$(CFLAGS)' + + + +$(BINARY_NAME): $(OBJS) Makefile + $(CC) $(OBJS) -o $@ $(LDFLAGS) $(EXTRA_LDFLAGS) + +%.o: %.c %.h Makefile $(SRC_H) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@ + +%.o: %.c Makefile $(SRC_H) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@ + + +strip: all + strip $(BINARY_NAME) + +strip_libs: all libs + $(MAKE) -C lib strip + + + + +install: all + mkdir -p $(SBINDIR) + install -m 0755 $(BINARY_NAME) $(SBINDIR) + +install_libs: all + $(MAKE) -C lib install CORE_CFLAGS='$(CFLAGS)' + + + +clean: + rm -f $(BINARY_NAME) *.o posix/*.o linux/*.o cyassl/*.o + +clean_libs: + $(MAKE) -C lib clean + + +clean_all: clean clean_libs +build_all: all libs +strip_all: strip strip_libs +install_all: install install_libs + + +help: + # further make targets: + # help show this help + # all compile bmxd core only + # libs compile bmx plugins + # build_all compile bmxd and plugins + # strip / strip_libs / strip_all strip bmxd / plugins / all + # install / install_libs / install_all install bmxd / plugins / all + # clean / clean_libs / clean_all clean bmxd / libs / all +
Added: trunk/bmx/allocate.c =================================================================== --- trunk/bmx/allocate.c (rev 0) +++ trunk/bmx/allocate.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2006 B.A.T.M.A.N. contributors: + * Thomas Lopatic, Corinna 'Elektra' Aichele, Axel Neumann, Marek Lindner + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include "bmx.h" + + +#define MAGIC_NUMBER 0x12345678 + +#if defined DEBUG_MALLOC + + +struct chunkHeader *chunkList = NULL; + +struct chunkHeader +{ + struct chunkHeader *next; + uint32_t length; + int32_t tag; + uint32_t magicNumber; +}; + +struct chunkTrailer +{ + uint32_t magicNumber; +}; + + + +#if defined MEMORY_USAGE + +struct memoryUsage *memoryList = NULL; + + +struct memoryUsage +{ + struct memoryUsage *next; + uint32_t length; + uint32_t counter; + int32_t tag; +}; + + +void addMemory( uint32_t length, int32_t tag ) { + + struct memoryUsage *walker; + + for ( walker = memoryList; walker != NULL; walker = walker->next ) { + + if ( walker->tag == tag ) { + + walker->counter++; + break; + } + } + + if ( walker == NULL ) { + + walker = malloc( sizeof(struct memoryUsage) ); + + walker->length = length; + walker->tag = tag; + walker->counter = 1; + + walker->next = memoryList; + memoryList = walker; + } + +} + + +void removeMemory( int32_t tag, int32_t freetag ) { + + struct memoryUsage *walker; + + for ( walker = memoryList; walker != NULL; walker = walker->next ) { + + if ( walker->tag == tag ) { + + if ( walker->counter == 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, + "Freeing more memory than was allocated: malloc tag = %d, free tag = %d", + tag, freetag ); + cleanup_all( -500069 ); + + } + + walker->counter--; + break; + + } + + } + + if ( walker == NULL ) { + + dbg( DBGL_SYS, DBGT_ERR, + "Freeing memory that was never allocated: malloc tag = %d, free tag = %d", + tag, freetag ); + cleanup_all( -500070 ); + } +} + +#endif + +#if defined MEMORY_USAGE + +void debugMemory( struct ctrl_node *cn ) { + + struct memoryUsage *memoryWalker; + + dbg_printf( cn, "\nMemory usage information:\n" ); + + for ( memoryWalker = memoryList; memoryWalker != NULL; memoryWalker = memoryWalker->next ) { + + if ( memoryWalker->counter != 0 ) + dbg_printf( cn, " tag: %4i, num malloc: %4i, bytes per malloc: %4i, total: %6i\n", + memoryWalker->tag, memoryWalker->counter, memoryWalker->length, + memoryWalker->counter * memoryWalker->length ); + + } + dbg_printf( cn, "\n" ); + +} + +#endif + + +void checkIntegrity(void) +{ + struct chunkHeader *walker; + struct chunkTrailer *chunkTrailer; + unsigned char *memory; + +// dbgf_all(DBGT_INFO, " "); + + for (walker = chunkList; walker != NULL; walker = walker->next) + { + if (walker->magicNumber != MAGIC_NUMBER) + { + dbgf( DBGL_SYS, DBGT_ERR, + "invalid magic number in header: %08x, malloc tag = %d", + walker->magicNumber, walker->tag ); + cleanup_all( -500073 ); + } + + memory = (unsigned char *)walker; + + chunkTrailer = (struct chunkTrailer *)(memory + sizeof(struct chunkHeader) + walker->length); + + if (chunkTrailer->magicNumber != MAGIC_NUMBER) + { + dbgf( DBGL_SYS, DBGT_ERR, + "invalid magic number in trailer: %08x, malloc tag = %d", + chunkTrailer->magicNumber, walker->tag ); + cleanup_all( -500075 ); + } + } + +} + +void checkLeak(void) +{ + struct chunkHeader *walker; + + if ( chunkList != NULL ) { + + openlog( "bmx", LOG_PID, LOG_DAEMON ); + + + for (walker = chunkList; walker != NULL; walker = walker->next) { + syslog( LOG_ERR, "Memory leak detected, malloc tag = %d\n", walker->tag ); + + fprintf( stderr, "Memory leak detected, malloc tag = %d \n", walker->tag ); + + } + + closelog(); + } + +} + +void *_debugMalloc(uint32_t length, int32_t tag) { + + unsigned char *memory; + struct chunkHeader *chunkHeader; + struct chunkTrailer *chunkTrailer; + unsigned char *chunk; + + memory = malloc(length + sizeof(struct chunkHeader) + sizeof(struct chunkTrailer)); + + if (memory == NULL) + { + dbg( DBGL_SYS, DBGT_ERR, "Cannot allocate %u bytes, malloc tag = %d", + (unsigned int)(length + sizeof(struct chunkHeader) + sizeof(struct chunkTrailer)), tag ); + cleanup_all( -500076 ); + } + + chunkHeader = (struct chunkHeader *)memory; + chunk = memory + sizeof(struct chunkHeader); + chunkTrailer = (struct chunkTrailer *)(memory + sizeof(struct chunkHeader) + length); + + chunkHeader->length = length; + chunkHeader->tag = tag; + chunkHeader->magicNumber = MAGIC_NUMBER; + + chunkTrailer->magicNumber = MAGIC_NUMBER; + + chunkHeader->next = chunkList; + chunkList = chunkHeader; + +#if defined MEMORY_USAGE + + addMemory( length, tag ); + +#endif + + return chunk; +} + +void *_debugRealloc(void *memoryParameter, uint32_t length, int32_t tag) { + + unsigned char *memory; + struct chunkHeader *chunkHeader=NULL; + struct chunkTrailer *chunkTrailer; + unsigned char *result; + uint32_t copyLength; + + if (memoryParameter) { /* if memoryParameter==NULL, realloc() should work like malloc() !! */ + memory = memoryParameter; + chunkHeader = (struct chunkHeader *)(memory - sizeof(struct chunkHeader)); + + if (chunkHeader->magicNumber != MAGIC_NUMBER) + { + dbgf( DBGL_SYS, DBGT_ERR, + "invalid magic number in header: %08x, malloc tag = %d", + chunkHeader->magicNumber, chunkHeader->tag ); + cleanup_all( -500078 ); + } + + chunkTrailer = (struct chunkTrailer *)(memory + chunkHeader->length); + + if (chunkTrailer->magicNumber != MAGIC_NUMBER) + { + dbgf( DBGL_SYS, DBGT_ERR, + "invalid magic number in trailer: %08x, malloc tag = %d", + chunkTrailer->magicNumber, chunkHeader->tag ); + cleanup_all( -500079 ); + } + } + + + result = _debugMalloc(length, tag); + if (memoryParameter) { + copyLength = length; + + if (copyLength > chunkHeader->length) + copyLength = chunkHeader->length; + + memcpy(result, memoryParameter, copyLength); + debugFree(memoryParameter, -300000); + } + + return result; +} + +void _debugFree(void *memoryParameter, int tag) { + + unsigned char *memory; + struct chunkHeader *chunkHeader; + struct chunkTrailer *chunkTrailer; + struct chunkHeader *walker; + struct chunkHeader *previous; + + memory = memoryParameter; + chunkHeader = (struct chunkHeader *)(memory - sizeof(struct chunkHeader)); + + if (chunkHeader->magicNumber != MAGIC_NUMBER) + { + dbgf( DBGL_SYS, DBGT_ERR, + "invalid magic number in header: %08x, malloc tag = %d, free tag = %d", + chunkHeader->magicNumber, chunkHeader->tag, tag ); + cleanup_all( -500080 ); + } + + previous = NULL; + + for (walker = chunkList; walker != NULL; walker = walker->next) + { + if (walker == chunkHeader) + break; + + previous = walker; + } + + if (walker == NULL) + { + dbg( DBGL_SYS, DBGT_ERR, "Double free detected, malloc tag = %d, free tag = %d", + chunkHeader->tag, tag ); + cleanup_all( -500081 ); + } + + if (previous == NULL) + chunkList = walker->next; + + else + previous->next = walker->next; + + + chunkTrailer = (struct chunkTrailer *)(memory + chunkHeader->length); + + if (chunkTrailer->magicNumber != MAGIC_NUMBER) + { + dbgf( DBGL_SYS, DBGT_ERR, + "invalid magic number in trailer: %08x, malloc tag = %d, free tag = %d", + chunkTrailer->magicNumber, chunkHeader->tag, tag ); + cleanup_all( -500082 ); + } + +#if defined MEMORY_USAGE + + removeMemory( chunkHeader->tag, tag ); + +#endif + + free(chunkHeader); + + +} + +#else + +void checkIntegrity(void) +{ +} + +void checkLeak(void) +{ +} + +void debugMemory( struct ctrl_node *cn ) +{ +} + +void *_debugMalloc(uint32_t length, int32_t tag) +{ + void *result; + + result = malloc(length); + + if (result == NULL) + { + dbg( DBGL_SYS, DBGT_ERR, "Cannot allocate %u bytes, malloc tag = %d", length, tag ); + cleanup_all( -500072 ); + } + + return result; +} + +void *_debugRealloc(void *memory, uint32_t length, int32_t tag) +{ + void *result; + + result = realloc(memory, length); + + if (result == NULL) { + dbg( DBGL_SYS, DBGT_ERR, "Cannot re-allocate %u bytes, malloc tag = %d", length, tag ); + cleanup_all( -500071 ); + } + + return result; +} + +void _debugFree(void *memory, int32_t tag) +{ + free(memory); +} + +#endif
Added: trunk/bmx/allocate.h =================================================================== --- trunk/bmx/allocate.h (rev 0) +++ trunk/bmx/allocate.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2006 B.A.T.M.A.N. contributors: + * Thomas Lopatic, Corinna 'Elektra' Aichele, Axel Neumann, Marek Lindner + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + + +#ifndef _ALLOCATE_H +#define _ALLOCATE_H 1 +#include <stdint.h> + + +// currently used memory tags: -300000 .. -300228 +#define debugMalloc( l,t ) _debugMalloc( (l), (t) ) +#define debugRealloc( m,l,t ) _debugRealloc( (m), (l), (t) ) +#define debugFree( m,t ) _debugFree( (m), (t) ) +void *_debugMalloc(uint32_t length, int32_t tag); +void *_debugRealloc(void *memory, uint32_t length, int32_t tag); +void _debugFree(void *memoryParameter, int32_t tag); + +void checkIntegrity(void); +void checkLeak(void); +void debugMemory( struct ctrl_node *cn ); + + +#endif
Added: trunk/bmx/avl.c =================================================================== --- trunk/bmx/avl.c (rev 0) +++ trunk/bmx/avl.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,684 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* + * avl code inspired by: + * http://eternallyconfuzzled.com/tuts/datastructures/jsw_tut_avl.aspx + * where Julienne Walker said (web page from 28. 2. 2010 12:55): + * ...Once again, all of the code in this tutorial is in the public domain. + * You can do whatever you want with it, but I assume no responsibility + * for any damages from improper use. ;-) + */ + + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + + +#include "bmx.h" + +#include "avl.h" + + +struct avl_node *avl_find( struct avl_tree *tree, void *key ) +{ + struct avl_node *an = tree->root; + int cmp; + + // Search for a dead path or a matching entry +// while ( an && ( cmp = memcmp( an->key, key, tree->key_size) ) ) + while ( an && ( cmp = memcmp( AVL_NODE_KEY( tree, an ), key, tree->key_size) ) ) + an = an->link[ cmp < 0 ]; + + return an; +} + +void *avl_find_item(struct avl_tree *tree, void *key) +{ + struct avl_node *an; + + return ((an = avl_find(tree, key)) ? an->item : NULL); + +} + +struct avl_node *avl_next(struct avl_tree *tree, void *key) +{ + struct avl_node *an = tree->root; + struct avl_node *best = NULL; + int cmp; + + while (an) { + + cmp = (memcmp(AVL_NODE_KEY(tree, an), key, tree->key_size) <= 0); + + if (an->link[cmp]) { + best = cmp ? best : an; + an = an->link[cmp]; + } else { + return cmp ? best : an; + } + + } + return NULL; +} + +void * avl_next_item( struct avl_tree *tree, void *key) +{ + struct avl_node *an = avl_next(tree, key); + + return an ? an->item : NULL; +} + + +struct avl_node *avl_iterate(struct avl_tree *tree, struct avl_node *an ) +{ + + if ( !an || an->link[1] ) { + + an = an ? an->link[1] : tree->root; + + while ( an && an->link[0] ) + an = an->link[0]; + + return an; + } + + struct avl_node *prev = an; + + while ((an = an->up)) { + + if ( an->link[0] == prev ) + return an; + + prev = an; + } + + return NULL; +} + +void *avl_iterate_item(struct avl_tree *tree, struct avl_node **an ) +{ + + if ( !(*an) || (*an)->link[1] ) { + + (*an) = (*an) ? (*an)->link[1] : tree->root; + + while ( (*an) && (*an)->link[0] ) + (*an) = (*an)->link[0]; + + return (*an) ? (*an)->item : NULL; + } + + struct avl_node *prev = (*an); + + while (((*an) = (*an)->up)) { + + if ( (*an)->link[0] == prev) + return (*an)->item; + + prev = (*an); + } + + return NULL; +} + + + +static struct avl_node *avl_create_node(void *node, int32_t tag) +{ + struct avl_node *an = debugMalloc(sizeof (struct avl_node), tag); + + memset( an, 0, sizeof( struct avl_node) ); + an->item = node; + + return an; +} + +static struct avl_node *avl_rotate_single(struct avl_node *root, int dir) +{ + struct avl_node *save; + int rlh, rrh, slh; + + /* Rotate */ + save = root->link[!dir]; + + root->link[!dir] = save->link[dir]; + + if (root->link[!dir]) + root->link[!dir]->up = root; + + save->link[dir] = root; + + if ( root ) + root->up = save; + + /* Update balance factors */ + rlh = avl_height(root->link[0]); + rrh = avl_height(root->link[1]); + slh = avl_height(save->link[!dir]); + + root->balance = avl_max(rlh, rrh) + 1; + save->balance = avl_max(slh, root->balance) + 1; + + return save; +} + +static struct avl_node *avl_rotate_double(struct avl_node *root, int dir) +{ + root->link[!dir] = avl_rotate_single(root->link[!dir], !dir); + + if (root->link[!dir]) + root->link[!dir]->up = root; + + return avl_rotate_single(root, dir); +} + + +void avl_insert(struct avl_tree *tree, void *node, int32_t tag) { + + if (tree->root) { + + struct avl_node *it = tree->root; + struct avl_node *up[AVL_MAX_HEIGHT]; + int upd[AVL_MAX_HEIGHT], top = 0; + int done = 0; + + /* Search for an empty link, save the path */ + for (;;) { + /* Push direction and node onto stack */ +// upd[top] = memcmp(it->key, key, tree->key_size) <= 0; + upd[top] = memcmp(AVL_NODE_KEY(tree, it), AVL_ITEM_KEY(tree, node), tree->key_size) <= 0; + + up[top++] = it; + + if (it->link[upd[top - 1]] == NULL) + break; + + it = it->link[upd[top - 1]]; + } + + /* Insert a new node at the bottom of the tree */ + it->link[upd[top - 1]] = avl_create_node(node, tag); + it->link[upd[top - 1]]->up = it; + + paranoia(-500178, (it->link[upd[top - 1]] == NULL)); + + /* Walk back up the search path */ + while (--top >= 0 && !done) { + + int lh, rh, max; + + lh = avl_height(up[top]->link[upd[top]]); + rh = avl_height(up[top]->link[!upd[top]]); + + /* Terminate or rebalance as necessary */ + if (lh - rh == 0) + done = 1; + if (lh - rh >= 2) { + struct avl_node *a = up[top]->link[upd[top]]->link[upd[top]]; + struct avl_node *b = up[top]->link[upd[top]]->link[!upd[top]]; + + if (avl_height(a) >= avl_height(b)) + up[top] = avl_rotate_single(up[top], !upd[top]); + else + up[top] = avl_rotate_double(up[top], !upd[top]); + + /* Fix parent */ + if (top != 0) { + up[top - 1]->link[upd[top - 1]] = up[top]; + up[top]->up = up[top - 1]; + } else { + tree->root = up[0]; + up[0]->up = NULL; + + } + + done = 1; + } + + /* Update balance factors */ + lh = avl_height(up[top]->link[upd[top]]); + rh = avl_height(up[top]->link[!upd[top]]); + max = avl_max(lh, rh); + + up[top]->balance = max + 1; + } + + } else { + + tree->root = avl_create_node(node, tag); + } + + tree->items++; + + return; +} + + + +void *avl_remove(struct avl_tree *tree, void *key, int32_t tag) +{ + struct avl_node *it = tree->root; + struct avl_node *up[AVL_MAX_HEIGHT]; + int upd[AVL_MAX_HEIGHT], top = 0, cmp; + + if (!it) + return NULL; + +// while ((cmp = memcmp(it->key, key, tree->key_size)) ) { + while ((cmp = memcmp(AVL_NODE_KEY(tree, it), key, tree->key_size)) || + (it->link[0] && !memcmp(AVL_NODE_KEY(tree, it->link[0]), key, tree->key_size))) { + + // Push direction and node onto stack + upd[top] = (cmp < 0); + up[top] = it; + top++; + + if (!(it = it->link[(cmp < 0)])) + return NULL; + + } + + // remember and return the found node. It might have been another one than intended + void *node = it->item; + + // Remove the node: + if (!(it->link[0] && it->link[1])) { // at least one child is NULL: + + // Which child is not null? + int dir = !(it->link[0]); + + /* Fix parent */ + if (top) { + up[top - 1]->link[upd[top - 1]] = it->link[dir]; + if (it->link[dir]) + it->link[dir]->up = up[top - 1]; + } else { + tree->root = it->link[dir]; + if (tree->root) + tree->root->up = NULL; + } + debugFree(it, tag); + + } else { // both childs NOT NULL: + + // Find the inorder successor + struct avl_node *heir = it->link[1]; + + // Save the path + upd[top] = 1; + up[top] = it; + top++; + + while (heir->link[0]) { + upd[top] = 0; + up[top] = heir; + top++; + heir = heir->link[0]; + } + + // Swap data + it->item = heir->item; + + // Unlink successor and fix parent + up[top - 1]->link[ (up[top - 1] == it) ] = heir->link[1]; + + if ( heir->link[1]) + heir->link[1]->up = up[top - 1]; + + debugFree(heir, tag); + } + + tree->items--; + + // Walk back up the search path + while (--top >= 0) { + int lh = avl_height(up[top]->link[upd[top]]); + int rh = avl_height(up[top]->link[!upd[top]]); + int max = avl_max(lh, rh); + + /* Update balance factors */ + up[top]->balance = max + 1; + + + // Terminate or re-balance as necessary: + if (lh - rh >= 0) // re-balance upper path... + continue; + + if (lh - rh == -1) // balance for upper path unchanged! + break; + + if (!(up[top]) || !(up[top]->link[!upd[top]])) { + dbgf(DBGL_SYS, DBGT_ERR, "up(top) %p link %p lh %d rh %d", + (void*)(up[top]), (void*)((up[top]) ? (up[top]->link[!upd[top]]) : NULL), lh, rh); + + paranoia(-500187, (!(up[top]))); + paranoia(-500188, (!(up[top]->link[!upd[top]]))); + } + + // if (lh - rh <= -2): rebalance here and upper path + + struct avl_node *a = up[top]->link[!upd[top]]->link[upd[top]]; + struct avl_node *b = up[top]->link[!upd[top]]->link[!upd[top]]; + + if (avl_height(a) <= avl_height(b)) + up[top] = avl_rotate_single(up[top], upd[top]); + else + up[top] = avl_rotate_double(up[top], upd[top]); + + // Fix parent: + if (top) { + up[top - 1]->link[upd[top - 1]] = up[top]; + up[top]->up = up[top - 1]; + } else { + tree->root = up[0]; + tree->root->up = NULL; + } + } + + return node; + +} + +#ifdef AVL_DEBUG + +struct avl_node *_avl_iter_down(int dir, struct avl_node *an, struct avl_iterator *it ) { + + for (; an; it->top++) { + /* Push direction and node onto stack */ + it->up[it->top] = an; + it->upd[it->top] = dir; + + an = an->link[dir]; + } + it->top--; + return it->up[it->top]; +} + +struct avl_node *avl_iter(struct avl_tree *tree, struct avl_iterator *it ) { + + struct avl_node *an = tree->root; + if ( !an ) + return NULL; + + if (!it->up[0]) { + // Get initial element and fill stack: + // go left... + it->top=0; + return _avl_iter_down( 0/*left*/, an, it ); + } + + if ((an = it->up[(it->top)]->link[1])) { + //go one right, then left... + it->upd[it->top] = 1; + it->top++; + return _avl_iter_down(0, an, it); + } + + while (it->top > 0 && it->upd[(it->top) - 1] == 1 /*was: down right*/) { + //so go one back up (up-left = opposite of down right) + it->top--; + } + + if (it->top > 0 && it->upd[(it->top) - 1] == 0 /*was: down left*/) { + //so go one back up (up-right = opposite of down left)): + it->top--; + return it->up[it->top]; + } + + it->up[0] = NULL; + return NULL; +} + +void avl_debug( struct avl_tree *tree ) { + + #define AVL_DBG_COL_CHARS 6 + + int i,j; + + int depth_min = 0; + + struct avl_node *an; + for (an = tree->root; an; an = an->link[0]) + depth_min++; + + int depth_max = depth_min+2; + int width = 1<<depth_max; + + char * dbg_mem = malloc( depth_max * width * AVL_DBG_COL_CHARS + 1); + memset( dbg_mem, ' ', depth_max * width * AVL_DBG_COL_CHARS + 1 ); + + for( i=1; i<=depth_max; i++ ) + dbg_mem[(i*width*AVL_DBG_COL_CHARS)-1] = '\n'; + + dbg_mem[ depth_max * width * AVL_DBG_COL_CHARS ] = 0; + + struct avl_iterator it; + memset ( &it, 0, sizeof( struct avl_iterator ) ); + + while( (an = avl_iter( tree, &it )) ) { + + j = (width - 1) * AVL_DBG_COL_CHARS / 2; + + for (i = 0; i < it.top; i++) { + + int k = ((width - 1) * AVL_DBG_COL_CHARS ); + int l = 4<<i; + int m = ( it.upd[i]?1:-1); + + j = j + (m * k / l); + } + +// dbg_mem[((i * width * AVL_DBG_COL_CHARS) + j)] = 'x'; + + snprintf(&dbg_mem[((i * width * AVL_DBG_COL_CHARS) + j)], AVL_DBG_COL_CHARS, + "%3d/%1d", ((int*) AVL_NODE_KEY(tree, an))[0], ((int*) AVL_NODE_KEY(tree, an))[1]); + + dbg_mem[((i * width * AVL_DBG_COL_CHARS) + j + (AVL_DBG_COL_CHARS-1))] = ' '; + + + printf("%3d/%1d ", ((int*) AVL_NODE_KEY(tree, an))[0], ((int*) AVL_NODE_KEY(tree, an))[1]); + } + + + dbg_mem[ depth_max * width * AVL_DBG_COL_CHARS ] = 0; + + printf("\n-----\n%s\n-----\n", dbg_mem ); + +} +#endif + +#ifdef AVL_TEST + +void avl_test( int max ) { + +#define AVL_TEST_MAX max + + struct test_type { + int32_t test_key; + int32_t v; + uint8_t k; + int32_t test_key2; + int32_t w; + }; + + struct avl_node *an = NULL; + AVL_TREE(test_tree, struct test_type, test_key ); + AVL_TREE(test_tree2, struct test_type, test_key2 ); + + int32_t i, j; + + struct test_type * t; + + + + // insert something: + + for (i = 0; i <= AVL_TEST_MAX; i++) { + t = debugMalloc(sizeof ( struct test_type), -300004); + t->test_key = i; + t->test_key2 = AVL_TEST_MAX-i; + t->v = t->w = 0; + avl_insert(&test_tree, t); + avl_insert(&test_tree2, t); + printf(" inserted %d/%d %d/%d\n", t->test_key, t->v, t->test_key2, t->w ); + } + +/* + // remove something: + + for (i = AVL_TEST_MAX/2; i >= 0; i--) { + + t = avl_remove(&test_tree, &i); + + if ( t != avl_remove(&test_tree2, &t->test_key2) ) + printf("ERROR...\n"); + + printf(" removed %d/%d %d/%d\n", t->test_key, t->v, t->test_key2, t->w ); + debugFree( t, -300038 ); + } +*/ + + + + + // debug test_tree: + + printf("\navl_iterate( test_tree ):\n"); + i=0; + an = NULL; + while( (an = avl_iterate( &test_tree, an ) ) ) { + t = ((struct test_type*)(an->item)); + if ( i > t->test_key) + printf("\nERROR %d > %d \n", i, t->test_key); + i = t->test_key; + printf( "%3d/%1d ", t->test_key, t->v ); + } + + printf("\navl_next():\n"); + i = 0; j = 0; + while( (an = avl_next( &test_tree, &j )) ) { + t = ((struct test_type*)(an->item)); + j = t->test_key; + if ( i > t->test_key) + printf("\nERROR %d > %d \n", i, t->test_key); + + i = t->test_key; + + printf( "N%4d/%1d ", t->test_key, t->v ); + + if ( i%10 == 0 ) + printf("\n"); + + } + +#ifdef AVL_DEBUG + printf("\navl_debug():\n"); + avl_debug( &test_tree ); +#endif + + printf("\n"); + + + + + // debug test_tree2: + + printf("\navl_iterate( test_tree2 ):\n"); + i=0; + an = NULL; + while( (an = avl_iterate( &test_tree2, an ) ) ) { + t = ((struct test_type*)(an->item)); + if ( i > t->test_key2) + printf("\nERROR %d > %d \n", i, t->test_key2); + i = t->test_key2; + printf( "%3d/%1d ", t->test_key2, t->w ); + } + + printf("\navl_next( test_tree2):\n"); + i = 0; j = 0; + while( (an = avl_next( &test_tree2, &j )) ) { + t = ((struct test_type*)(an->item)); + j = t->test_key2; + if ( i > t->test_key2) + printf("\nERROR %d > %d \n", i, t->test_key2); + + i = t->test_key2; + + printf( "N%4d/%1d ", t->test_key2, t->w ); + + if ( i%10 == 0 ) + printf("\n"); + + } + +#ifdef AVL_DEBUG + printf("\navl_debug():\n"); + avl_debug( &test_tree2 ); +#endif + + printf("\n"); + + + // remove whats left: + + while( test_tree.root ) { + t = (struct test_type*)test_tree.root->item; + avl_remove(&test_tree, &t->test_key, -300190); + avl_remove(&test_tree2, &t->test_key2, -300191); + printf(" removed %d/%d %d/%d\n", t->test_key, t->v, t->test_key2, t->w ); + debugFree( t, -300042 ); + } + + printf("\n"); + cleanup_all(CLEANUP_SUCCESS); +} + + +static int32_t tree_max = 5; +static int32_t opt_tree ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + if ( cmd == OPT_APPLY ) + avl_test(tree_max); + + return SUCCESS; +} + +static struct opt_type msg_options[]= +{ +// ord parent long_name shrt Attributes *ival min max default *func,*syntax,*help + + {ODI,0,0, 0, 5,0,0,0,0,0, 0, 0, 0, 0, 0, + 0, "\navl tree debuggin options:"} +, + {ODI,0,"tree", 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &tree_max, 0, 20, 1, opt_tree, + ARG_VALUE_FORM, "show tree with given number of elements"} + +}; + + + +#endif + +void init_avl( void ) +{ +#ifdef AVL_TEST + register_options_array( msg_options, sizeof( msg_options ) ); +#endif +}
Added: trunk/bmx/avl.h =================================================================== --- trunk/bmx/avl.h (rev 0) +++ trunk/bmx/avl.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* + * avl code inspired by: + * http://eternallyconfuzzled.com/tuts/datastructures/jsw_tut_avl.aspx + * where Julienne Walker said (web page from 28. 2. 2010 12:55): + * ...Once again, all of the code in this tutorial is in the public domain. + * You can do whatever you want with it, but I assume no responsibility + * for any damages from improper use. ;-) + */ + +#ifndef _AVL_H +#define _AVL_H + +#include <stdint.h> + + +#define AVL_MAX_HEIGHT 128 + + +struct avl_node { + void *item; + int balance; + struct avl_node * up; + struct avl_node * link[2]; +}; + +// obtain key pointer based on avl_node pointer +#define AVL_NODE_KEY( a_tree, a_node ) ( (void*) ( ((char*)(a_node)->item)+((a_tree)->key_offset) ) ) + +// obtain key pointer based on item pointer +#define AVL_ITEM_KEY( a_tree, a_item ) ( (void*) ( ((char*)(a_item))+((a_tree)->key_offset) ) ) + +struct avl_tree { + struct avl_node *root; + uint16_t key_size; + uint16_t key_offset; + uint32_t items; +}; + + +#define AVL_INIT_TREE(tree, element_type, key_field) do { \ + tree.root = NULL; \ + tree.key_size = sizeof( (((element_type *) 0)->key_field) ); \ + tree.key_offset = ((unsigned long) (&((element_type *) 0)->key_field)); \ + tree.items = 0; \ + } while (0) + +#define AVL_TREE(tree, element_type, key_field) struct avl_tree (tree) = { \ + NULL, \ + (sizeof( (((element_type *) 0)->key_field) )), \ + ((unsigned long)(&((element_type *)0)->key_field)), \ + 0 } + +#define avl_height(p) ((p) == NULL ? -1 : (p)->balance) +#define avl_max(a,b) ((a) > (b) ? (a) : (b)) + + +struct avl_node *avl_find( struct avl_tree *tree, void *key ); +void *avl_find_item( struct avl_tree *tree, void *key ); +struct avl_node *avl_next( struct avl_tree *tree, void *key ); +void *avl_next_item(struct avl_tree *tree, void *key); +struct avl_node *avl_iterate(struct avl_tree *tree, struct avl_node *it ); +void *avl_iterate_item(struct avl_tree *tree, struct avl_node **it ); + +void avl_insert(struct avl_tree *tree, void *node, int32_t tag); +void *avl_remove(struct avl_tree *tree, void *key, int32_t tag); + +void init_avl(void); + +#ifdef AVL_DEBUG +struct avl_iterator { + struct avl_node * up[AVL_MAX_HEIGHT]; + int upd[AVL_MAX_HEIGHT]; + int top; +}; + +struct avl_node *avl_iter(struct avl_tree *tree, struct avl_iterator *it ); +void avl_debug( struct avl_tree *tree ); +void avl_test(int m); +#endif + + +#endif
Added: trunk/bmx/bmx.c =================================================================== --- trunk/bmx/bmx.c (rev 0) +++ trunk/bmx/bmx.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,2279 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#define _GNU_SOURCE +#include <arpa/inet.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <unistd.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <linux/if.h> /* ifr_if, ifr_tun */ +#include <linux/rtnetlink.h> +#include <time.h> + +#include "bmx.h" +#include "msg.h" +#include "hna.h" +#include "plugin.h" +#include "schedule.h" +#include "route.h" + +#define DEF_HELLO_IVAL 500 +#define MIN_HELLO_IVAL 10 +#define MAX_HELLO_IVAL 10000 +#define ARG_HELLO_IVAL "hello_interval" + + + +int32_t my_pws = DEF_PWS; + +int32_t local_lws = DEF_LWS; + +int32_t local_rtq_lounge = DEF_RTQ_LOUNGE; + +int32_t my_path_lounge = DEF_PATH_LOUNGE; + +int32_t dad_to = DEF_DAD_TO; + +int32_t my_ttl = DEF_TTL; + +int32_t wl_clones = DEF_WL_CLONES; + +int32_t my_path_hystere = DEF_PATH_HYST; + +int32_t my_rcnt_hystere = DEF_RCNT_HYST; + +int32_t my_rcnt_pws = DEF_RCNT_PWS; + +int32_t my_rcnt_fk = DEF_RCNT_FK; + +int32_t my_late_penalty = DEF_LATE_PENAL; + +static int32_t drop_2hop_loop = DEF_DROP_2HLOOP; + + +static int32_t Asocial_device = DEF_ASOCIAL; + + +int32_t asym_weight = DEF_ASYM_WEIGHT; +int32_t sym_weight = DEF_SYM_WEIGHT; + +static int32_t asym_exp = DEF_ASYM_EXP; + +int32_t my_hop_penalty = DEF_HOP_PENALTY; + + +static int32_t ogi_pwrsave = MIN_OGM_INTERVAL; + + +static int32_t purge_to = DEF_PURGE_TO; + + +void* FAILURE_POINTER = &FAILURE_POINTER; + +static int8_t stop = 0; + +static struct timeval start_time_tv; +static struct timeval ret_tv, new_tv, diff_tv, acceptable_m_tv, acceptable_p_tv, max_tv = {0,(2000*MAX_SELECT_TIMEOUT_MS)}; + + +uint32_t My_pid = 0; + +uint32_t bmx_time = 0; +uint32_t bmx_time_sec = 0; + +uint8_t on_the_fly = NO; + +uint32_t s_curr_avg_cpu_load = 0; + +struct dev_node *primary_if = NULL; + + +struct metric_algo link_metric_algo[SQR_RANGE]; + + + +struct orig_node my_orig_node; + + +AVL_TREE(dev_ip4_tree, struct dev_node, ip4_addr); +AVL_TREE(dev_name_tree, struct dev_node, name); + +AVL_TREE(link_tree, struct link_node, llip4); +AVL_TREE(blacklisted_tree, struct black_node, dhash); + +AVL_TREE(link_dev_tree, struct link_dev_node, key); + +AVL_TREE(neigh_tree, struct neigh_node, nnkey); + +AVL_TREE(dhash_tree, struct dhash_node, dhash); +AVL_TREE(dhash_invalid_tree, struct dhash_node, dhash); +LIST_SIMPEL( dhash_invalid_plist, struct plist_node, list ); + +AVL_TREE(orig_tree, struct orig_node, id); +AVL_TREE(blocked_tree, struct orig_node, id); + + +/* +STATIC_FUNC +void purge_neigh_node( struct link_node *ln, struct dhash_node *dhn ); +*/ + +/*********************************************************** + Data Infrastructure + ************************************************************/ + + + +void blacklist_neighbor(struct packet_buff *pb) +{ + + dbgf(DBGL_SYS, DBGT_ERR, "%s via %s", pb->neigh_str, pb->iif->name); +} + + +IDM_T blacklisted_neighbor(struct packet_buff *pb, struct description_hash *dhash) +{ + + dbgf(DBGL_ALL, DBGT_INFO, "%s via %s", pb->neigh_str, pb->iif->name); + return NO; +} + + +IDM_T validate_metric_algo(struct metric_algo *ma, struct ctrl_node *cn) +{ + + if (ma->sqn_window <= ma->sqn_steps) { + + dbg_cn(cn, DBGL_SYS, DBGT_ERR, "SQN: window=%d MUST be greater than steps=%d", + ma->sqn_window, ma->sqn_steps); + + } else if (ma->sqn_window <= ma->sqn_lounge) { + + dbg_cn(cn, DBGL_SYS, DBGT_ERR, "SQN: window=%d MUST BE greater than lounge=%d", + ma->sqn_window, ma->sqn_lounge); + + } else if (ma->sqn_window % ma->sqn_steps) { + + dbg_cn(cn, DBGL_SYS, DBGT_ERR, "SQN: window=%d MUST BE multiple of steps=%d", + ma->sqn_window, ma->sqn_steps); + + } else if (ma->sqn_lounge % ma->sqn_steps) { + + dbg_cn(cn, DBGL_SYS, DBGT_ERR, "SQN: lounge=%d MUST BE multiple of steps=%d", + ma->sqn_lounge, ma->sqn_steps); + } else { + + return SUCCESS; + } + + return FAILURE; +} + + +uint32_t update_metric(struct metric_record *mr, struct metric_algo *ma, SQN_T sqn_in, SQN_T sqn_max, uint32_t probe) +{ + + dbgf(DBGL_ALL, DBGT_INFO, + "sqn_in %d sqn_max %d probe %u " + "metric_algo: mask 0x%X steps %d window %d lounge %d metric_record: clr %d, set %d val %u", + sqn_in, sqn_max, probe, + ma->sqn_mask, ma->sqn_steps, ma->sqn_window, ma->sqn_lounge, mr->clr, mr->set, mr->val); + + + ASSERTION(-500491, (ma->sqn_mask == (SQN_T) (0XFFFF << (bits_count(((SQN_T) ~(ma->sqn_mask))))))); + ASSERTION(-500492, (ma->sqn_steps == (SQN_T) (0X0001 << (bits_count(((SQN_T) ~(ma->sqn_mask))))))); + assertion(-500493, (ma->sqn_window > ma->sqn_steps)); + assertion(-500494, (ma->sqn_window > ma->sqn_lounge)); + assertion(-500495, (!(ma->sqn_window & ((SQN_T)~(ma->sqn_mask))))); + assertion(-500496, (!(ma->sqn_lounge & ((SQN_T)~(ma->sqn_mask))))); + assertion(-500497, (!(mr->clr & ((SQN_T) ~(ma->sqn_mask))))); + +// assertion(-500498, (probe >= ma->sqn_window * ma->sqn_steps)); // may cause zero metric (which is ok) +// assertion(-500499, (!(probe % (ma->sqn_window * ma->sqn_steps)))); // may cause slightly unfair metric + assertion( -500500, (probe <= ma->metric_max)); + + sqn_max &= ma->sqn_mask; + SQN_T sqn_min = sqn_max - ma->sqn_lounge; + SQN_T sqn_low_boundary = sqn_min + ma->sqn_steps - ma->sqn_window; + SQN_T i; + + + // first purge out-of-lounge positioned records: + + if (((SQN_T) (mr->clr - sqn_low_boundary)) < (ma->sqn_window - ma->sqn_steps)) { + // mr->clr within A: + SQN_T sqn_purge = sqn_min - mr->clr; + SQN_T metric_purge = sqn_purge / ma->sqn_steps; + mr->clr = sqn_min; + + assertion(-500500, (((SQN_T) (metric_purge - 1)) < ((ma->sqn_window / ma->sqn_steps) - 1))); + + for (i = 0; i < metric_purge; i++) + mr->val -= (mr->val / ma->regression); + + + } else if (((SQN_T) (mr->clr - sqn_min)) <= ma->sqn_lounge) { + + // mr->clr within B: FINE! + + } else { + + // mr->clr out of any range: + if ( mr->clr ) { + dbgf(DBGL_SYS, DBGT_ERR, + "sqn_in %d sqn_max %d probe %u " + "metric_algo: mask 0x%X steps %d window %d lounge %d metric_record: clr %d, set %d val %u", + sqn_in, sqn_max, probe, + ma->sqn_mask, ma->sqn_steps, ma->sqn_window, ma->sqn_lounge, mr->clr, mr->set, mr->val); + } + + mr->clr = sqn_min; + mr->val = 0; + } + + + if (!probe) + return mr->val; + + // then update the metric: + + if (((SQN_T) (sqn_in - sqn_min)) < (ma->sqn_lounge + ma->sqn_steps)) { + + // sqn_in within B: + +// if (((SQN_T) ((mr->set) - sqn_min)) >= (ma->sqn_lounge + ma->sqn_steps)) { + if (((SQN_T) ((mr->set & ma->sqn_mask) - sqn_min)) >= (ma->sqn_lounge + ma->sqn_steps)) { + + // mr->set out of B (fix maybe illegal mr->set): then set to mr->clr - sqn_steps + mr->set = sqn_min - ma->sqn_steps; + } + + + if ((((sqn_in & ma->sqn_mask) == (mr->set & ma->sqn_mask)))) { + + // sqn_in already set ! + + } else if (((SQN_T) ((sqn_in & ma->sqn_mask)-(mr->set & ma->sqn_mask))) <= ma->sqn_lounge + ma->sqn_steps) { + + // sqn_in > mr->set: + + SQN_T sqn_purge = ((sqn_in & ma->sqn_mask)-(mr->clr & ma->sqn_mask)); + SQN_T metric_purge = sqn_purge / ma->sqn_steps; + + for (i = 0; i < metric_purge; i++) + mr->val -= (mr->val / ma->regression); + + mr->val += (((((SQN_T) (sqn_in & ~(ma->sqn_mask))) + 1) * (probe / ma->regression)) / ma->sqn_steps); + + mr->set = mr->clr = sqn_in & ma->sqn_mask; + + uint64_t add = ((((SQN_T) (sqn_in & ~(ma->sqn_mask))) * probe) + (rand_num(probe - 1))) / ma->metric_max; + + assertion(-500501, (add <= ((SQN_T) (sqn_in & ~(ma->sqn_mask))))); + + mr->set += add; + + + } else { + + dbgf(DBGL_CHANGES, DBGT_WARN, + "sqn_in %d sqn_max %d probe %u " + "metric_algo: mask 0x%X steps %d window %d lounge %d metric_record: clr %d, set %d val %u", + sqn_in, sqn_max, probe, + ma->sqn_mask, ma->sqn_steps, ma->sqn_window, ma->sqn_lounge, mr->clr, mr->set, mr->val); + // impossible ?? assertion(-500502, (0)); + } + } + + assertion(-500503, (mr->val <= ma->metric_max)); + return mr->val; +} + + + + + +STATIC_FUNC +void assign_best_rtq_link(struct neigh_node *nn) +{ + struct list_node *lndev_pos; + struct link_dev_node *lndev_best = NULL; + struct link_node *ln; + struct avl_node *an = NULL; + + assertion( -500451, (nn)); + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s", nn->dhn->on->id.name); + + while ((ln = avl_iterate_item(&nn->link_tree, &an))) { + + list_for_each(lndev_pos, &ln->lndev_list) + { + struct link_dev_node *lndev = list_entry(lndev_pos, struct link_dev_node, list); + + if (!lndev_best || + lndev->mr[SQR_RTQ].val > lndev_best->mr[SQR_RTQ].val || + GREAT_U32(lndev->rtq_time_max, lndev_best->rtq_time_max) + ) + lndev_best = lndev; + + } + + } + assertion( -500406, (lndev_best)); + + nn->best_rtq = lndev_best; + +} + + + +//BMX3 (done) +STATIC_FUNC +void free_neigh_node(struct neigh_node *nn) +{ + paranoia(-500321, (nn->link_tree.items)); + + dbgf(DBGL_SYS, DBGT_INFO," "); + + avl_remove(&neigh_tree, &nn->nnkey, -300196); + iid_purge_repos(&nn->neighIID4x_repos); + debugFree(nn, -300129); +} + + +/* + * merging dhn->neigh into ln->neigh + * keeping ln->neigh and dhn + * purging ln->neigh->dhn and dhn->neigh + */ +//BMX3 (done) +STATIC_FUNC +struct neigh_node *merge_neigh_nodes(struct link_node *ln, struct dhash_node * dhn) +{ + struct neigh_node *neigh = ln->neigh; + + dbgf(DBGL_SYS, DBGT_ERR, "Neigh restarted ?!! purging %s %jX, keeping %s %jX", + neigh->dhn->on->desc0->id.name, neigh->dhn->on->desc0->id.rand.u64[0], + dhn->on->desc0->id.name, dhn->on->desc0->id.rand.u64[0]); + + if (dhn->neigh) { + struct neigh_node *del_neigh = dhn->neigh; + + dbgf(DBGL_SYS, DBGT_ERR, "Merging neigh_nodes, purging %d neighIID4x entries ", + del_neigh->neighIID4x_repos.tot_used); + + assertion(-500406, (del_neigh->link_tree.items)); + + struct avl_node *an; + while ((an = del_neigh->link_tree.root)) { + + struct link_node * mv_ln = an->item; + + ASSERTION(-500408, (!avl_find(&neigh->link_tree, &mv_ln->llip4))); + assertion(-500409, (mv_ln != ln)); + + avl_insert(&neigh->link_tree, mv_ln, -300140); + mv_ln->neigh = neigh; + avl_remove(&del_neigh->link_tree, &mv_ln->llip4, -300199); + } + + free_neigh_node(del_neigh); + } + + neigh->dhn->neigh = NULL; + invalidate_dhash_node(neigh->dhn); + neigh->dhn = dhn; + dhn->neigh = neigh; + + return neigh; +} + + +//BMX3 (done) +STATIC_FUNC +struct neigh_node * create_neigh_node(struct link_node *ln, struct dhash_node * dhn) +{ + assertion( -500400, ( ln && !ln->neigh && dhn && !dhn->neigh ) ); + + struct neigh_node *nn = debugMalloc(sizeof ( struct neigh_node), -300131); + + memset(nn, 0, sizeof ( struct neigh_node)); + + AVL_INIT_TREE(nn->link_tree, struct link_node, llip4); + + avl_insert(&nn->link_tree, ln, -300172); + + nn->dhn = dhn; + + nn->nnkey = nn; + avl_insert(&neigh_tree, nn, -300141); + + dhn->neigh = ln->neigh = nn; + + return nn; +} + + +//BMX3 (done) +IDM_T update_neigh_node(struct link_node *ln, struct dhash_node *dhn, IID_T neighIID4neigh) +{ + struct neigh_node *neigh = NULL; + + dbgf( DBGL_SYS, DBGT_INFO, "neigh %s neighIID4neigh %d dhn->orig %s", + ln->llip4_str, neighIID4neigh, dhn->on->desc0->id.name); + + assertion(-500389, (ln && neighIID4neigh > IID_RSVD_MAX)); + assertion(-500390, (dhn && dhn->on)); + + if (ln->neigh) { + + assertion(-500405, (ln->neigh->dhn && ln->neigh->dhn->on )); + assertion(-500391, (ln->neigh->dhn->neigh == ln->neigh)); + ASSERTION(-500392, (avl_find(&ln->neigh->link_tree, &ln->llip4))); + + if (ln->neigh == dhn->neigh) { + + assertion(-500393, (ln->neigh->dhn == dhn)); + +//always if new dhash: assertion(-500450, 0); //this never happen! or ? + + neigh = ln->neigh; + + + } else { + + neigh = merge_neigh_nodes( ln, dhn); + + } + + } else { + + if ( dhn->neigh ) { + + assertion(-500394, (dhn->neigh->dhn == dhn)); + assertion(-500395, (dhn->neigh->link_tree.items)); + ASSERTION(-500396, (!avl_find(&dhn->neigh->link_tree, &ln->llip4))); + + neigh = ln->neigh = dhn->neigh; + avl_insert(&neigh->link_tree, ln, -300173); + + } else { + + neigh = create_neigh_node( ln, dhn ); + + } + + } + + assign_best_rtq_link(neigh); + + return update_neighIID4x_repository(neigh, neighIID4neigh, neigh->dhn); +} + + +STATIC_FUNC +struct link_dev_node *get_link_dev_node(struct link_node *ln, struct dev_node *dev) +{ + struct list_node *lndev_pos; + struct link_dev_node *lndev; + + list_for_each( lndev_pos, &ln->lndev_list ) { + + lndev = list_entry( lndev_pos, struct link_dev_node, list ); + + if ( lndev->key.dev == dev ) + return lndev; + } + + lndev = debugMalloc( sizeof( struct link_dev_node ), -300023 ); + + memset( lndev, 0, sizeof( struct link_dev_node ) ); + + lndev->key.dev = dev; + lndev->key.llip4 = ln->llip4; + + dbgf( DBGL_CHANGES, DBGT_INFO, "creating new lndev %16s %10s %s", + ipStr(ln->llip4), dev->name, dev->ip4_str ); + + list_add_tail(&ln->lndev_list, &lndev->list); + + ASSERTION( -500489, !avl_find( &link_dev_tree, &lndev->key)); + + avl_insert( &link_dev_tree, lndev, -300220 ); + + lndev->link = ln; + + return lndev; +} + +STATIC_FUNC +struct link_node *get_link_node(uint32_t llip4) +{ + + dbgf_all( DBGT_INFO, "%s", ipStr(llip4) ); + + paranoia(-500210, !llip4); + + struct link_node *ln = avl_find_item(&link_tree, &llip4); + + if (!ln) { + + ln = debugMalloc(sizeof (struct link_node), -300024); + memset(ln, 0, sizeof (struct link_node)); + + LIST_INIT_HEAD(ln->lndev_list, struct link_dev_node, list); + + ln->llip4 = llip4; + + addr_to_str(llip4, ln->llip4_str); + + avl_insert(&link_tree, ln, -300147); + } + + return ln; +} + +void update_link_node(struct link_node *ln, struct dev_node *iif, SQN_T sqn, SQN_T sqn_max, uint8_t sqr, uint32_t probe) +{ +// uint8_t lounge_size = (sqr == SQR_RQ) ? RQ_LINK_LOUNGE : local_rtq_lounge; + + struct list_node *lndev_pos; + struct link_dev_node *lndev, *this_lndev = NULL; + + list_for_each(lndev_pos, &ln->lndev_list) + { + lndev = list_entry(lndev_pos, struct link_dev_node, list); + + if (lndev->key.dev == iif) { + + this_lndev = lndev; + + } else { + +// update_lounged_metric(0, lounge_size, sqn, sqn_max, &lndev->sqr[sqr], local_lws); + + update_metric(&lndev->mr[sqr], &link_metric_algo[sqr], sqn, sqn_max, 0); + + } + + } + + if (this_lndev) { + +// update_lounged_metric(probe, lounge_size, sqn, sqn_max, &this_lndev->sqr[sqr], local_lws); + + update_metric(&this_lndev->mr[sqr], &link_metric_algo[sqr], sqn, sqn_max, probe); + + if (probe && sqr == SQR_RTQ) { + + this_lndev->rtq_time_max = bmx_time; + + if ( + ln->neigh && + (!ln->neigh->best_rtq || + ln->neigh->best_rtq->mr[SQR_RTQ].val <= this_lndev->mr[SQR_RTQ].val)) { + + ln->neigh->best_rtq = this_lndev; + } + } + } + + if (probe && sqr == SQR_RQ) { + ln->rq_time_max = bmx_time; + ln->rq_sqn_max = sqn; + } + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s dev %s", + ipStr(ln->llip4), this_lndev ? this_lndev->key.dev->name : "???"); + +} + + + +STATIC_FUNC +void purge_link_node(uint32_t only_llip4, struct dev_node *only_dev, IDM_T only_expired) +{ + struct avl_node *an; + struct link_node *ln; + uint32_t itip4 = 0; + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s %s %s only_expired", + ipStr(only_llip4), only_dev ? only_dev->name : "---", only_expired ? " " : "not"); + + while ((an = (only_llip4 ? (avl_find(&link_tree, &only_llip4)) : (avl_next(&link_tree, &itip4)))) && (ln = an->item)) { + + struct list_node *pos, *tmp, *prev = (struct list_node *) & ln->lndev_list; + struct neigh_node *nn = ln->neigh; + IDM_T removed_lndev = NO; + + itip4 = ln->llip4; + + list_for_each_safe(pos, tmp, &ln->lndev_list) + { + struct link_dev_node *lndev = list_entry(pos, struct link_dev_node, list); + + if ((!only_dev || only_dev == lndev->key.dev) && + (!only_expired || (((uint32_t) (bmx_time - lndev->pkt_time_max)) > (uint32_t) purge_to))) { + + dbgf(DBGL_SYS, DBGT_INFO, "purging lndev %16s %10s %s", + ipStr(ln->llip4), lndev->key.dev->name, lndev->key.dev->ip4_str); + + list_del_next(&ln->lndev_list, prev); + avl_remove(&link_dev_tree, &lndev->key, -300221); + debugFree(pos, -300044); + removed_lndev = YES; + + } else { + prev = pos; + } + } + + assertion(-500323, (only_dev || only_expired || ln->lndev_list.items==0)); + + if (!ln->lndev_list.items) { + + dbgf(DBGL_CHANGES, DBGT_INFO, "purging: %s %s", ipStr(itip4), only_dev ? only_dev->name : "???"); + + if (nn) { + + avl_remove(&nn->link_tree, &ln->llip4, -300198); + + dbgf(DBGL_SYS, DBGT_INFO, "ln->neigh %s (%d more link_tree.items)", + ln->llip4_str, nn->link_tree.items); + + if (!nn->link_tree.items) { + + if (nn->dhn) { + dbgf(DBGL_SYS, DBGT_INFO, "setting ln->neigh->dhn (%s) = NULL", + nn->dhn->on->id.name); + nn->dhn->neigh = NULL; + } + + free_neigh_node(nn); + nn = NULL; + } + } + + avl_remove(&link_tree, &ln->llip4, -300193 ); + + debugFree( ln, -300045 ); + } + + if (nn && removed_lndev) + assign_best_rtq_link(nn); + + if ( only_llip4 ) + break; + } +} + +struct dhash_node* create_dhash_node(struct description_hash *dhash, struct orig_node *on) +{ + + struct dhash_node * dhn = debugMalloc(sizeof ( struct dhash_node), -300001); + memset(dhn, 0, sizeof ( struct dhash_node)); + memcpy(&dhn->dhash, dhash, BMX_HASH0_LEN); + avl_insert(&dhash_tree, dhn, -300142); + + dhn->myIID4orig = iid_new_myIID4x(dhn); + + on->updated_timestamp = bmx_time; + dhn->on = on; + on->dhn = dhn; + + dbgf(DBGL_CHANGES, DBGT_INFO, "dhash %8X.. myIID4orig %d", dhn->dhash.h.u32[0], dhn->myIID4orig); + + return dhn; +} + +STATIC_FUNC +void free_dhash_iid(struct dhash_node *dhn) +{ + struct avl_node *an; + struct neigh_node *nn; + + iid_free(&my_iid_repos, dhn->myIID4orig); + + //reset all neigh_node->oid_repos[x]=dhn->mid4o entries + for (an = NULL; (nn = avl_iterate_item(&neigh_tree, &an));) { + + iid_free_neighIID4x_by_myIID4x(&nn->neighIID4x_repos, dhn->myIID4orig); + + } + + debugFree(dhn, -300112); + +} + +STATIC_FUNC + void purge_dhash_to_list( IDM_T purge_all ) { + + struct dhash_node *dhn; + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s", purge_all ? "purge_all" : "only_expired"); + + while ((dhn = plist_get_first(&dhash_invalid_plist)) ) { + + if (purge_all || ((uint32_t) (bmx_time - dhn->referred_timestamp) > MIN_DHASH_TO)) { + + dbgf(DBGL_CHANGES, DBGT_INFO, "dhash %8X myIID4orig %d", dhn->dhash.h.u32[0], dhn->myIID4orig); + + plist_rem_head(&dhash_invalid_plist); + avl_remove(&dhash_invalid_tree, &dhn->dhash, -300194); + + free_dhash_iid(dhn); + + } else { + break; + } + } +} + +STATIC_FUNC +void unlink_dhash_node(struct dhash_node *dhn) +{ + dbgf(DBGL_CHANGES, DBGT_INFO, "dhash %8X myIID4orig %d", dhn->dhash.h.u32[0], dhn->myIID4orig); + + if (dhn->neigh) { + + struct avl_node *an; + struct link_node *ln; + + while ((an = dhn->neigh->link_tree.root) && (ln = an->item)) { + + dbgf(DBGL_SYS, DBGT_INFO, "dhn->neigh->link_tree item %s", ln->llip4_str); + + assertion(-500284, (ln->neigh == dhn->neigh)); + + ln->neigh = NULL; + + avl_remove(&dhn->neigh->link_tree, &ln->llip4, -300197); + } + + free_neigh_node(dhn->neigh); + + dhn->neigh = NULL; + } + + if (dhn->on) { + dhn->on->dhn = NULL; + free_orig_node( dhn->on ); + dhn->on = NULL; + } + + + + avl_remove(&dhash_tree, &dhn->dhash, -300195); + +} + + +void free_dhash_node( struct dhash_node *dhn ) +{ + dbgf(DBGL_SYS, DBGT_INFO, "dhash %8X myIID4orig %d", dhn->dhash.h.u32[0], dhn->myIID4orig); + + invalidate_dhash_node(dhn); + return; + + unlink_dhash_node(dhn); + + free_dhash_iid(dhn); + +} + +void invalidate_dhash_node( struct dhash_node *dhn ) +{ + + dbgf(DBGL_CHANGES, DBGT_INFO, + "dhash %8X myIID4orig %d, my_iid_repository: used %d, inactive %d min_free %d max_free %d ", + dhn->dhash.h.u32[0], dhn->myIID4orig, + my_iid_repos.tot_used, dhash_invalid_tree.items+1, my_iid_repos.min_free, my_iid_repos.max_free); + + unlink_dhash_node(dhn); // will clear dhn->on + + avl_insert(&dhash_invalid_tree, dhn, -300168); + plist_add_tail(&dhash_invalid_plist, dhn); + dhn->referred_timestamp = bmx_time; + + + //dhn->invalid=YES; +} + + + + + + +//BMX3 (done) +IDM_T update_neighIID4x_repository(struct neigh_node *neigh, IID_T neighIID4x, struct dhash_node *dhn) +{ + assertion(-500386, (neigh->dhn != my_orig_node.dhn)); + + if (dhn == my_orig_node.dhn) + neigh->neighIID4me = neighIID4x; + + if (dhn == neigh->dhn) + neigh->neighIID4neigh = neighIID4x; + + return iid_set_neighIID4x(&neigh->neighIID4x_repos, neighIID4x, dhn->myIID4orig); +} + + +STATIC_FUNC +void purge_router_tree( struct orig_node *on, IDM_T purge_all ) +{ + struct avl_node *an; + struct link_key key; + memset( &key, 0, sizeof(struct link_key)); + + while ((an = avl_next(&on->router_tree, &key))) { + + struct router_node *rn = an->item; + + memcpy(&key, &rn->key, sizeof (struct link_key)); + + if (purge_all || !avl_find(&link_dev_tree, &rn->key)) { + debugFree(rn, -300225); + avl_remove(&on->router_tree, &rn->key, -300226); + } + } +} + +IDM_T update_orig_metrics(struct packet_buff *pb, struct orig_node *on, IID_T orig_sqn) +{ + dbgf(DBGL_ALL, DBGT_INFO, "%s orig_sqn %d via neigh %s", on->id.name, orig_sqn,pb->neigh_str); + + struct router_node *rn_in = NULL; + uint32_t metric_in, metric_best = 0; + SQN_T mask = on->path_metric_algo.sqn_mask; +// SQN_T range = on->ogm_sqn_range; + +// return SUCCESS; + + if (on->blocked || (((SQN_T) (orig_sqn - on->ogm_sqn_min)) >= on->ogm_sqn_range)) + return FAILURE; + +/* + + if (((SQN_T) (((orig_sqn & mask) - (on->ogm_sqn_max_rcvd & mask)) - 1)) < on->ogm_sqn_range) { + + + for (;;) { + struct router_node *rn; + struct avl_node *an; +// SQN_T min_sqn_level = ((orig_sqn & mask) - on->ogm_sqn_steps); + struct router_node *rn_best = NULL; + uint32_t metric_temp; + metric_best = 0; + + for (an = NULL; (rn = avl_iterate_item(&on->router_tree, &an));) { + + if (memcmp(&rn->key, &pb->lndev->key, sizeof (struct link_key))) { + + metric_temp = update_metric(&rn->mr, &on->path_metric_algo, 0, orig_sqn, 0); + + if (metric_best < metric_temp) { + + metric_best = metric_temp; + rn_best = rn; + } + + } else { + rn_in = rn; + } + } + + if (rn_best && !avl_find(&link_dev_tree, &rn_best->key)) { + purge_router_tree(on, NO); + continue; + } else { + break; + } + } + + } else { + rn_in = avl_find_item(&on->router_tree, &pb->lndev->key); + } + + on->ogm_sqn_max_rcvd = MAX_SQ(on->ogm_sqn_max_rcvd, orig_sqn); + + */ + on->ogm_sqn_max_rcvd = MAX_SQ(on->ogm_sqn_max_rcvd, orig_sqn); + + if (((SQN_T) ((on->ogm_sqn_max_rcvd & mask) - (orig_sqn & mask))) > on->path_metric_algo.sqn_lounge ) { + + dbgf(DBGL_CHANGES, DBGT_WARN, "%s orig_sqn %d to old max_sqn %d via neigh %s", + on->id.name, orig_sqn, on->ogm_sqn_max_rcvd, pb->neigh_str); + + return FAILURE; + } + + for (;;) { + struct router_node *rn; + struct avl_node *an; + struct router_node *rn_best = NULL; + uint32_t metric_temp; + metric_best = 0; + + for (an = NULL; (rn = avl_iterate_item(&on->router_tree, &an));) { + + if (memcmp(&rn->key, &pb->lndev->key, sizeof (struct link_key))) { + + metric_temp = update_metric(&rn->mr, &on->path_metric_algo, 0, on->ogm_sqn_max_rcvd, 0); + + if (metric_best < metric_temp) { + + metric_best = metric_temp; + rn_best = rn; + } + + } else { + rn_in = rn; + } + } + + if (rn_best && !avl_find(&link_dev_tree, &rn_best->key)) { + purge_router_tree(on, NO); + continue; + } else { + break; + } + } + + + if (!rn_in) { + rn_in = debugMalloc(sizeof (struct router_node), -300222); + memset( rn_in, 0, sizeof(struct router_node)); + memcpy( &rn_in->key, &pb->lndev->key, sizeof(struct link_key)); +// rn_in->ogm_sqn_to_be_send = ((on->ogm_sqn_max_rcvd & mask) - on->ogm_sqn_steps); + avl_insert(&on->router_tree, rn_in, -300223); + } + + metric_in = update_metric(&rn_in->mr, &on->path_metric_algo, orig_sqn, on->ogm_sqn_max_rcvd, pb->lndev->mr[SQR_RTQ].val); + + + if (metric_in > metric_best && GREAT_SQN(rn_in->mr.set & mask, on->ogm_sqn_to_be_send & mask)) { + + on->router_path_metric = metric_in; + on->ogm_sqn_to_be_send = rn_in->mr.set; + ogm_aggreg_pending++; + + if (memcmp(&on->router_key, &rn_in->key, sizeof (struct link_key))) { + + struct link_dev_node *lndev_old = avl_find_item(&link_dev_tree, &on->router_key); + + dbgf((!on->router_key.llip4 || lndev_old) ? DBGL_CHANGES : DBGL_SYS, + (!on->router_key.llip4 || lndev_old) ? DBGT_INFO : DBGT_ERR, + "changing router to %s %s via %s %s metric %d (prev %s %s)", + on->id.name, on->primary_ip4_str, + ipStr(rn_in ? rn_in->key.llip4 : 0), rn_in ? rn_in->key.dev->name : "---", metric_in, + ipStr(on->router_key.llip4), lndev_old ? on->router_key.dev->name : "---"); + + assertion(-500504, (!on->router_key.llip4 || lndev_old)); + + if (on->router_key.llip4) { + configure_route(on->primary_ip4, 32, 0, on->router_key.llip4, 0, + on->router_key.dev->index, on->router_key.dev->name, + RT_TABLE_HOSTS, RTN_UNICAST, DEL, TRACK_OTHER_HOST); + } + + configure_route(on->primary_ip4, 32, 0, rn_in->key.llip4, my_orig_node.primary_ip4, + rn_in->key.dev->index, rn_in->key.dev->name, + RT_TABLE_HOSTS, RTN_UNICAST, ADD, TRACK_OTHER_HOST); + + memcpy(&on->router_key, &rn_in->key, sizeof (struct link_key)); + } + } + return SUCCESS; +} + + + +void free_orig_node(struct orig_node *on) +{ + dbgf(DBGL_CHANGES, DBGT_INFO, "%s", on->primary_ip4_str); + + if ( on == &my_orig_node) + return; + + if (on->router_key.llip4) { + configure_route(on->primary_ip4, 32, 0, on->router_key.llip4, 0, + on->router_key.dev->index, on->router_key.dev->name, + RT_TABLE_HOSTS, RTN_UNICAST, DEL, TRACK_OTHER_HOST); + } + + + process_description_tlvs(on, NULL, TLV_DEL_TEST_ADD, NULL); + + + if ( on->dhn ) { + on->dhn->on = NULL; + free_dhash_node(on->dhn); + } + + purge_router_tree(on, YES); + + avl_remove(&orig_tree, &on->id, -300200); + avl_remove(&blocked_tree, &on->id, -300201); + + debugFree( on->desc0, -300228 ); + debugFree( on, -300086 ); +} + + + + +void purge_orig(struct dev_node *only_dev, IDM_T only_expired) +{ + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s %s only expired", + only_dev ? only_dev->name : "---", only_expired ? " " : "NOT"); + + purge_link_node( 0, only_dev, only_expired ); + + int i; + for (i = IID_RSVD_MAX + 1; i < my_iid_repos.max_free; i++) { + + struct dhash_node *dhn; + + if ((dhn = my_iid_repos.arr.node[i]) && dhn->on) { + + if ( only_dev ) { + + //TODO: keep on and only remove route + if ( dhn->on->router_key.dev == only_dev) + free_orig_node(dhn->on); + + } else if (!only_expired || ((uint32_t) (bmx_time - dhn->referred_timestamp)) > (uint32_t) purge_to) { + + free_orig_node(dhn->on); + } + } + } +} + +struct dev_node * get_bif(char *dev) +{ + char dev_name[IFNAMSIZ]; + memset(dev_name, 0, IFNAMSIZ); + + strncpy(dev_name, dev, IFNAMSIZ - 1); + + return avl_find_item(&dev_name_tree, dev_name); + +} + + +void rx_packet( struct packet_buff *pb ) +{ + struct dev_node *dev; + struct dev_node *iif = pb->iif; + struct packet_header *hdr = (struct packet_header *) pb->packet_in; + hdr->pkt_length = ntohs(hdr->pkt_length); + hdr->pkt_dev_sqn = ntohs(hdr->pkt_dev_sqn); + + if (pb->pkt_buff_llip4 == iif->ip4_broad) { + dbgf(DBGL_SYS, DBGT_WARN, + "drop OGM: %s via %s ignoring all packets with broadcast source IP", + ipStr(pb->pkt_buff_llip4), pb->iif->name); + return; + } + + if ((dev = avl_find_item(&dev_ip4_tree, &pb->pkt_buff_llip4))) { + // mark own packets; + pb->oif = iif; + } else { + pb->oif = NULL; + } + + + addr_to_str( pb->pkt_buff_llip4, pb->neigh_str ); + + // immediately drop invalid packets... + // we acceppt longer packets than specified by pos->size to allow padding for equal packet sizes + if ( pb->total_length < (int) (sizeof (struct packet_header) + sizeof (struct frame_header)) || + hdr->pkt_length < (int) (sizeof (struct packet_header) + sizeof (struct frame_header)) || + hdr->bmx_version != COMPAT_VERSION || + hdr->pkt_length > pb->total_length || hdr->pkt_length > MAX_UDPD_SIZE) { + + goto process_packet_error_hdr; + } + + dbgf_all( DBGT_INFO, "version? %i, reserved? %X, size? %i, sqn %d rcvd udp_len %d via NB %s %s %s", + hdr->bmx_version, hdr->bmx_capabilities, hdr->pkt_length, hdr->pkt_dev_sqn, + pb->total_length, pb->neigh_str, iif->name, pb->unicast ? "UNICAST" : "BRC"); + + + if (!pb->oif) { + + iif->link_activity_timestamp = bmx_time; + + } else if (((SQN_T) (pb->oif->packet_sqn - hdr->pkt_dev_sqn)) > 2) { + + dbgf(DBGL_SYS, DBGT_WARN, + "DAD-Alert via dev %s Somebody is using my Link-Local Address %s pkt_sqn %d != %d!!!", + iif->name, pb->oif->ip4_str, hdr->pkt_dev_sqn, pb->oif->packet_sqn); + + goto process_packet_error_hdr; + + } else { + return; + } + + + struct link_node *ln = get_link_node(pb->pkt_buff_llip4); + + if (ln->pkt_time_max && ln->pkt_sqn_max && ((SQN_T) (hdr->pkt_dev_sqn - ln->pkt_sqn_max)) > SQN_DAD_RANGE) { + + dbgf(DBGL_SYS, DBGT_WARN, + "NB %s (via %s) reinitialized (or DAD?!) pkt_sqn %d != %d! Reinitializing link_node", + pb->neigh_str, iif->name, hdr->pkt_dev_sqn, ln->pkt_sqn_max); + + purge_link_node(ln->llip4, NULL, NO); + ASSERTION( -500213, !avl_find(&link_tree, &pb->pkt_buff_llip4 ) ); + ln = get_link_node(pb->pkt_buff_llip4); + } + + ln->pkt_sqn_max = hdr->pkt_dev_sqn; + ln->pkt_time_max = bmx_time; + pb->ln = ln; + pb->lndev = get_link_dev_node(ln, pb->iif); + pb->lndev->pkt_time_max = bmx_time; + + + dbgf(DBGL_ALL, DBGT_INFO, "rcvd packet from %s size %d via dev %s", + pb->neigh_str, hdr->pkt_length, iif->name); + + if (blacklisted_neighbor(pb, NULL)) + return; + + if (rx_frames(pb, hdr->pkt_data, hdr->pkt_length - sizeof (struct packet_header)) == SUCCESS) + return; + +process_packet_error_hdr: + dbgf(DBGL_SYS, DBGT_WARN, + "Drop (remaining) packet: rcvd problematic packet via NB %s %s" + "(version? %i, reserved? %X, pkt_size? %i), rcvd udp_len % d My version is % d, max_udpd_size %d", + pb->neigh_str, iif->name, + hdr->bmx_version, hdr->bmx_capabilities, hdr->pkt_length, pb->total_length, + COMPAT_VERSION, MAX_UDPD_SIZE); + + return; +} + + +/*********************************************************** + Runtime Infrastructure +************************************************************/ + +void upd_time( struct timeval *precise_tv ) { + + timeradd( &max_tv, &new_tv, &acceptable_p_tv ); + timercpy( &acceptable_m_tv, &new_tv ); + gettimeofday( &new_tv, NULL ); + + if ( timercmp( &new_tv, &acceptable_p_tv, > ) ) { + + timersub( &new_tv, &acceptable_p_tv, &diff_tv ); + timeradd( &start_time_tv, &diff_tv, &start_time_tv ); + + dbg( DBGL_SYS, DBGT_WARN, + "critical system time drift detected: ++ca %ld s, %ld us! Correcting reference!", + diff_tv.tv_sec, diff_tv.tv_usec ); + + if ( diff_tv.tv_sec > CRITICAL_PURGE_TIME_DRIFT ) + purge_orig(NULL, NO); + + } else if ( timercmp( &new_tv, &acceptable_m_tv, < ) ) { + + timersub( &acceptable_m_tv, &new_tv, &diff_tv ); + timersub( &start_time_tv, &diff_tv, &start_time_tv ); + + dbg( DBGL_SYS, DBGT_WARN, + "critical system time drift detected: --ca %ld s, %ld us! Correcting reference!", + diff_tv.tv_sec, diff_tv.tv_usec ); + + if ( diff_tv.tv_sec > CRITICAL_PURGE_TIME_DRIFT ) + purge_orig(NULL, NO); + + } + + timersub( &new_tv, &start_time_tv, &ret_tv ); + + if ( precise_tv ) { + precise_tv->tv_sec = ret_tv.tv_sec; + precise_tv->tv_usec = ret_tv.tv_usec; + } + + bmx_time = ( (ret_tv.tv_sec * 1000) + (ret_tv.tv_usec / 1000) ); + bmx_time_sec = ret_tv.tv_sec; + +} + + + +char *get_human_uptime( uint32_t reference ) { + // DD:HH:MM:SS + static char ut[32]="00:00:00:00"; + + sprintf( ut, "%2i:%i%i:%i%i:%i%i", + (((bmx_time_sec-reference)/86400)), + (((bmx_time_sec-reference)%86400)/36000)%10, + (((bmx_time_sec-reference)%86400)/3600)%10, + (((bmx_time_sec-reference)%3600)/600)%10, + (((bmx_time_sec-reference)%3600)/60)%10, + (((bmx_time_sec-reference)%60)/10)%10, + (((bmx_time_sec-reference)%60))%10 + ); + + return ut; +} + + +void wait_sec_msec( uint32_t sec, uint32_t msec ) { + + struct timeval time; + + //no debugging here because this is called from debug_output() -> dbg_fprintf() which may case a loop! + //dbgf_all( DBGT_INFO, "%d sec %d msec...", sec, msec ); + + time.tv_sec = sec + (msec/1000) ; + time.tv_usec = ( msec * 1000 ) % 1000000; + + select( 0, NULL, NULL, NULL, &time ); + + //update_bmx_time( NULL ); //this will cause critical system time drift message from the client + //dbgf_all( DBGT_INFO, "bat_wait(): done"); + + return; +} + + + +int32_t rand_num( uint32_t limit ) { + + return ( limit == 0 ? 0 : rand() % limit ); +} + + + +int8_t terminating() { + + return stop != 0; + +} + + +static void handler( int32_t sig ) { + + if ( !Client_mode ) { + dbgf( DBGL_SYS, DBGT_ERR, "called with signal %d", sig); + } + + printf("\n");// to have a newline after ^C + + stop = 1; + cb_plugin_hooks( NULL, PLUGIN_CB_TERM ); + +} + + +/* counting bits based on http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable */ + +static unsigned char BitsSetTable256[256]; + +STATIC_FUNC +void init_set_bits_table256(void) +{ + BitsSetTable256[0] = 0; + int i; + for (i = 0; i < 256; i++) + { + BitsSetTable256[i] = (i & 1) + BitsSetTable256[i / 2]; + } +} + +// count the number of true bits in v + +uint8_t bits_count(uint32_t v) +{ + uint8_t c=0; + uint32_t w = v; + + for (; v; v = v>>8 ) + c += BitsSetTable256[v & 0xff]; + + dbgf_all( DBGT_INFO, "%8X, counted %d bits", w, c); + + return c; +} + +uint8_t bit_get(uint8_t *array, uint16_t array_bit_size, uint16_t bit) +{ + bit = bit % array_bit_size; + + uint16_t byte_pos = bit / 8; + uint8_t bit_pos = bit % 8; + + return (array[byte_pos] & (0x01 << bit_pos)) ? 1 : 0; +} + +void bit_set(uint8_t *array, uint16_t array_bit_size, uint16_t bit, IDM_T value) +{ + bit = bit % array_bit_size; + + uint16_t byte_pos = bit / 8; + uint8_t bit_pos = bit % 8; + + if (value) + array[byte_pos] |= (0x01 << bit_pos); + else + array[byte_pos] &= ~(0x01 << bit_pos); + + assertion(-500415, (!value == !bit_get(array, array_bit_size, bit))); +} + +/* + * clears bit range between and including begin and end + */ +void bit_clear(uint8_t *array, uint16_t array_bit_size, uint16_t begin_bit, uint16_t end_bit) +{ + assertion(-500435, (array_bit_size % 8 == 0)); + + if (((uint16_t) (end_bit - begin_bit)) >= (array_bit_size - 1)) { + + memset(array, 0, array_bit_size / 8); + return; + } + + begin_bit = begin_bit % array_bit_size; + end_bit = end_bit % array_bit_size; + + uint16_t begin_byte = begin_bit/8; + uint16_t end_byte = end_bit/8; + uint16_t array_byte_size = array_bit_size/8; + + + if (begin_byte != end_byte && ((begin_byte + 1) % array_byte_size) != end_byte) + byte_clear(array, array_byte_size, begin_byte + 1, end_byte - 1); + + + uint8_t begin_mask = ~(0xFF << (begin_bit%8)); + uint8_t end_mask = (0xFF >> ((end_bit%8)+1)); + + if (begin_byte == end_byte) { + + array[begin_byte] &= (begin_mask | end_mask); + + } else { + + array[begin_byte] &= begin_mask; + array[end_byte] &= end_mask; + } +} + +/* + * clears byte range between and including begin and end + */ +void byte_clear(uint8_t *array, uint16_t array_size, uint16_t begin, uint16_t end) +{ + + assertion(-500436, (array_size % 2 == 0)); + + begin = begin % array_size; + end = end % array_size; + + memset(array + begin, 0, end >= begin ? end + 1 - begin : array_size - begin); + + if ( begin > end) + memset(array, 0, end + 1); + + +} + +uint8_t is_zero(char *data, int len) +{ + int i; + + for (i = 0; i < len && !data[i]; i++); + + if ( i < len ) + return NO; + + return YES; +} + + + +static int segfault = NO; +static int cleaning_up = NO; + +static void segmentation_fault(int32_t sig) +{ + + dbg(DBGL_SYS, DBGT_ERR, "SIGSEGV %d received, try cleaning up (%s-rv%d)...", + sig, SOURCE_VERSION, REVISION_VERSION); + + if (!segfault) { + + segfault = YES; + + signal(SIGSEGV, SIG_DFL); + + + if (!on_the_fly) + dbg(DBGL_SYS, DBGT_ERR, + "check up-to-dateness of bmx libs in default lib path %s or customized lib path defined by %s !", + BMX_DEF_LIB_PATH, BMX_ENV_LIB_PATH); + + + if (!cleaning_up) + cleanup_all(CLEANUP_RETURN); + + dbg(DBGL_SYS, DBGT_ERR, "raising SIGSEGV again ..."); + } + + errno=0; + if ( raise( SIGSEGV ) ) { + dbg( DBGL_SYS, DBGT_ERR, "raising SIGSEGV failed: %s...", strerror(errno) ); + } +} + + +void cleanup_all(int status) +{ + + if (status < 0) { + dbg(DBGL_SYS, DBGT_ERR, "Terminating with error code %d ! Please notify a developer", status); + segmentation_fault(1); + } + + if (!cleaning_up) { + + dbgf_all(DBGT_INFO, "cleaning up (status %d)...", status); + + cleaning_up = YES; + + // first, restore defaults... + + stop = 1; + + cleanup_schedule(); + + + if (my_orig_node.dhn) { + my_orig_node.dhn->on = NULL; + free_dhash_node(my_orig_node.dhn); + } + + avl_remove(&orig_tree, &(my_orig_node.id), -300203); + + purge_orig( NULL, NO ); + + cleanup_plugin(); + + cleanup_config(); + + cleanup_route(); + + cleanup_msg(); + + purge_dhash_to_list(YES); + + + while (dev_name_tree.items) { + + struct dev_node *dev = dev_name_tree.root->item; + + if ( dev->active ) + dev_deactivate( dev ); + + avl_remove(&dev_name_tree, &dev->name, -300204); + + debugFree(dev, -300046); + + } + + // last, close debugging system and check for forgotten resources... + cleanup_control(); + + checkLeak(); + + dbgf_all( DBGT_ERR, "...cleaning up done"); + + if (status == CLEANUP_SUCCESS) { + + exit(EXIT_SUCCESS); + + } else if (status == CLEANUP_FAILURE) { + + exit(EXIT_FAILURE); + + } else if (status == CLEANUP_RETURN) { + + return; + + } + + exit(EXIT_FAILURE); + dbg(DBGL_SYS, DBGT_ERR, "exit ignored!?"); + } + +} + + + + + + + + + + + +/*********************************************************** + Configuration data and handlers +************************************************************/ + + +STATIC_FUNC +int32_t opt_show_origs(uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn) +{ + + int rq, tq, rtq; + + if ( cmd == OPT_APPLY ) { + + if (!strcmp(opt->long_name, ARG_ORIGINATORS)) { + + dbg_printf(cn, "Originator: rand primary_ip via " + "metric mid4o descSQN ogmSQN < max lupd lref pogi pws\n"); + + struct avl_node *it = NULL; + + while ((it = avl_iterate(&orig_tree, it))) { + + struct orig_node *on = it->item; + + struct link_dev_node *lndev = avl_find_item(&link_dev_tree, &on->router_key); + + dbg_printf(cn, "%-22s %8X%8X %-15s %-15s %-10s" "%6i %5d %5d %5d %5d %5d %4d %5d %4d\n", + on->id.name, ntohl(on->id.rand.u32[0]), ntohl(on->id.rand.u32[1]), + on->blocked ? "BLOCKED" : on->primary_ip4_str, + ipStr(lndev ? lndev->key.llip4 : 0), + lndev ? lndev->key.dev->name : "---", + on->router_path_metric, + on->dhn->myIID4orig, on->desc0_sqn, + on->ogm_sqn_to_be_send, + (on->ogm_sqn_min + on->ogm_sqn_range), + (bmx_time - on->updated_timestamp) / 1000, + (bmx_time - on->dhn->referred_timestamp) / 1000, + ntohs(on->desc0->path_ogi), ntohs(on->desc0->path_window_size) + ); + + //process_description_tlvs( on, NULL, TLV_DEBUG, cn ); + } + + + } else if (!strcmp(opt->long_name, ARG_STATUS)) { + + dbg_printf(cn, "BMX %s-rv%d, %s, LWS %i, PWS %i, OGI %4ims, UT %s, CPU %d.%1d\n", + SOURCE_VERSION, REVISION_VERSION, my_orig_node.primary_ip4_str, local_lws, my_pws, my_ogm_interval, + get_human_uptime(0), s_curr_avg_cpu_load / 10, s_curr_avg_cpu_load % 10); + + + } else if ( !strcmp( opt->long_name, ARG_LINKS ) ) { + + dbg_printf(cn, "LinkLocalIP viaIF bestIF primaryIP RTQ RQ TQ oid4m lseq lvld\n"); + + struct avl_node *it = NULL; + + while ((it = avl_iterate(&link_tree, it))) { + + struct link_node *ln = it->item; + struct neigh_node *nn = ln->neigh; + struct orig_node *on = nn ? nn->dhn->on : NULL; + + struct list_node *lndev_pos; + + list_for_each( lndev_pos, &ln->lndev_list ) { + struct link_dev_node *lndev = list_entry( lndev_pos, struct link_dev_node, list ); + + rq = lndev->mr[SQR_RQ].val; + tq = TQ_RATE( lndev, PROBE_RANGE); // tq_rate( lndev, PROBE_RANGE ); + rtq = lndev->mr[SQR_RTQ].val; + + + dbg_printf( cn, "%-15s %-10s %-10s %-15s %4i %4i %4i %5d %5i %4i\n", + ipStr(ln->llip4), + lndev->key.dev->name, + ln->neigh && ln->neigh->best_rtq ? ln->neigh->best_rtq->key.dev->name : "---", + on ? on->primary_ip4_str : "???", + rtq , rq, tq, + nn ? nn->neighIID4me : 0, + ln->rq_sqn_max, + (bmx_time - lndev->rtq_time_max) / 1000 + ); + + } + + } + + } else { + return FAILURE; + } + + dbg_printf( cn, "\n" ); + } + + return SUCCESS; +} + + +STATIC_FUNC +int32_t opt_dev_show(uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn) +{ + + if ( cmd == OPT_APPLY ) { + + struct avl_node *it=NULL; + struct dev_node *dev; + while ((dev = avl_iterate_item(&dev_name_tree, &it))) { + + dbg_cn( cn, DBGL_ALL, DBGT_NONE, "%-10s %5d %8s %15s/%-2d brc %-15s SQN %5d %14s %8s %11s", + dev->name, + dev->index, + !dev->active ? "-" : + ( dev->linklayer == VAL_DEV_LL_LO ? "loopback": + ( dev->linklayer == VAL_DEV_LL_LAN ? "ethernet": + ( dev->linklayer == VAL_DEV_LL_WLAN ? "wireless": "???" ) ) ), + dev->ip4_str, + dev->ip4_prefix_length, + ipStr(dev->ip4_broad), + dev->ogm_sqn, + dev->announce ? "announced" : "not announced", + dev->active ? "active" : "inactive", + dev == primary_if ? "primary" : "non-primary" + ); + + } + } + return SUCCESS; +} +STATIC_FUNC +int32_t opt_dev(uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn) +{ + + struct list_node *list_pos; + struct dev_node *dev = NULL; + + struct dev_node test_bif; + + char *colon_ptr; + + dbgf_all( DBGT_INFO, "cmd: %s opt: %s instance %s", + opt_cmd2str[cmd], opt->long_name, patch ? patch->p_val : ""); + + if ( cmd == OPT_CHECK || cmd == OPT_APPLY ) { + + if ( strlen(patch->p_val) >= IFNAMSIZ ) { + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "dev name MUST be smaller than %d chars", IFNAMSIZ ); + return FAILURE; + } + + dev = get_bif( patch->p_val ); + + if ( patch->p_diff == DEL ) { + + if ( dev && primary_if == dev ) { + + dbg_cn( cn, DBGL_SYS, DBGT_ERR, + "primary interface %s %s can not be removed!", + dev->name, dev->ip4_str ); + + return FAILURE; + + } else if ( dev && cmd == OPT_APPLY ) { + + if ( dev->active ) + dev_deactivate( dev ); + + avl_remove( &dev_name_tree, &dev->name, -300205 ); + + debugFree(dev, -300048); + + return SUCCESS; + + + } else if ( !dev ) { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "Interface does not exist!" ); + return FAILURE; + } + } + + if ( !dev ) { + + if ( cmd == OPT_APPLY ) { + int i; + dev = debugMalloc( sizeof(struct dev_node), -300002 ); + memset(dev, 0, sizeof (struct dev_node)); + + if (!dev_name_tree.items) + primary_if = dev; + + snprintf(dev->name, wordlen(patch->p_val) + 1, "%s", patch->p_val); + + avl_insert( &dev_name_tree, dev, -300144 ); + + for (i = 0; i < FRAME_TYPE_ARRSZ; i++) { + LIST_INIT_HEAD(dev->tx_tasks_list[i], struct tx_task_node, list); + } + + AVL_INIT_TREE( dev->tx_timestamp_tree, struct tx_timestamp_node, key ); + + + + } else { + + dev = &test_bif; + memset(dev, 0, sizeof (struct dev_node)); + snprintf(dev->name, wordlen(patch->p_val) + 1, "%s", patch->p_val); + + } + +// bif->aggregation_out = bif->aggregation_out_buff; + + snprintf( dev->name_phy, wordlen(patch->p_val)+1, "%s", patch->p_val ); + + /* if given interface is an alias record physical interface name*/ + if ( ( colon_ptr = strchr( dev->name_phy, ':' ) ) != NULL ) + *colon_ptr = '\0'; + + dbgf_all( DBGT_INFO, "assign dev %s physical name %s", dev->name, dev->name_phy ); + + dev->ogm_sqn = rand_num(MAX_SQN); + + dev->packet_sqn = rand_num(MAX_SQN); + + // bif->aggregation_len = sizeof( struct bmx_pkt_hdr ); + + + // some configurable interface values - initialized to unspecified: + dev->send_clones_conf = -1; + dev->antenna_diversity_conf = -1; + dev->linklayer_conf = -1; + dev->announce_conf = -1; + + } + + if ( cmd == OPT_CHECK ) + return SUCCESS; + + list_for_each( list_pos, &patch->childs_instance_list ) { + + struct opt_child *c = list_entry( list_pos, struct opt_child, list ); + + int32_t val = c->c_val ? strtol( c->c_val , NULL , 10 ) : -1 ; + + if ( !strcmp( c->c_opt->long_name, ARG_DEV_CLONE ) ) { + + dev->send_clones_conf = val; + + } else if ( !strcmp( c->c_opt->long_name, ARG_DEV_ANTDVSTY ) ) { + + dev->antenna_diversity_conf = val; + + } else if ( !strcmp( c->c_opt->long_name, ARG_DEV_LL ) ) { + + dev->linklayer_conf = val; + dev->hard_conf_changed = YES; + + } else if ( !strcmp( c->c_opt->long_name, ARG_DEV_ANNOUNCE ) ) { + + dev->announce_conf = val; + } + + dev->soft_conf_changed = YES; + + } + + + } else if ( cmd == OPT_POST && opt && !opt->parent_name ) { + + dev_check(); //will always be called whenever a parameter is changed (due to OPT_POST) + + } + + return SUCCESS; +} + +STATIC_FUNC +int32_t opt_purge(uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn) +{ + + if ( cmd == OPT_APPLY) + purge_orig(NULL, NO); + + return SUCCESS; +} + + +/* +STATIC_FUNC +int32_t opt_if_soft(uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn) +{ + + if ( cmd == OPT_APPLY ) + if_conf_soft_changed = YES; + + return SUCCESS; +} +*/ + +#ifdef WITHUNUSED + +STATIC_FUNC +int32_t opt_if_hard(uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn) +{ + + if ( cmd == OPT_APPLY ) + if_conf_hard_changed = YES; + + return SUCCESS; +} +#endif + + +int32_t opt_update_description(uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn) +{ + + if ( cmd == OPT_APPLY ) + if_conf_soft_changed = YES; + + return SUCCESS; +} + +STATIC_FUNC +int32_t opt_path_metric(uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn) +{ + + + if (cmd == OPT_CHECK || cmd == OPT_APPLY || cmd == OPT_REGISTER) { + + struct metric_algo test_algo; + memset(&test_algo, 0, sizeof (struct metric_algo)); + + test_algo.sqn_mask = ((SQN_T)(0xFFFF << DEF_OGM0_PQ_BITS)); + test_algo.sqn_steps = (0x01 << DEF_OGM0_PQ_BITS); + test_algo.regression = my_pws / test_algo.sqn_steps / 2; + test_algo.sqn_lounge = my_path_lounge; + test_algo.sqn_window = my_pws; + test_algo.metric_max = PROBE_RANGE; + + if (validate_metric_algo(&test_algo, cn) == FAILURE) + return FAILURE; + + if (cmd == OPT_APPLY || cmd == OPT_REGISTER) + memcpy(&(my_orig_node.path_metric_algo), &test_algo, sizeof (struct metric_algo)); + + } + + if ( cmd == OPT_APPLY ) + if_conf_soft_changed = YES; + + return SUCCESS; +} + +STATIC_FUNC +int32_t opt_link_metric(uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn) +{ + if (cmd == OPT_CHECK || cmd == OPT_APPLY || cmd == OPT_REGISTER) { + + struct metric_algo test_algo[SQR_RANGE]; + memset(test_algo, 0, SQR_RANGE * sizeof (struct metric_algo)); + + test_algo[SQR_RTQ].sqn_mask = 0xFFFF; + test_algo[SQR_RTQ].sqn_steps = 1; + test_algo[SQR_RTQ].regression = local_lws / 2; + test_algo[SQR_RTQ].sqn_lounge = local_rtq_lounge; + test_algo[SQR_RTQ].sqn_window = local_lws; + test_algo[SQR_RTQ].metric_max = PROBE_RANGE; + + if (validate_metric_algo(&(test_algo[SQR_RTQ]), cn) == FAILURE) + return FAILURE; + + test_algo[SQR_RQ].sqn_mask = 0xFFFF; + test_algo[SQR_RQ].sqn_steps = 1; + test_algo[SQR_RQ].regression = local_lws / 2; + test_algo[SQR_RQ].sqn_lounge = RQ_LINK_LOUNGE; + test_algo[SQR_RQ].sqn_window = local_lws; + test_algo[SQR_RQ].metric_max = PROBE_RANGE; + + if (validate_metric_algo(&(test_algo[SQR_RQ]), cn) == FAILURE) + return FAILURE; + + if (cmd == OPT_APPLY || cmd == OPT_REGISTER) + memcpy(link_metric_algo, test_algo, SQR_RANGE * sizeof (struct metric_algo)); + + + } + + return SUCCESS; +} + + +static struct opt_type bmx_options[]= +{ +// ord parent long_name shrt Attributes *ival min max default *func,*syntax,*help + + {ODI,0,0, 0, 5,0,0,0,0,0, 0, 0, 0, 0, 0, + 0, "\nProtocol options:"}, + + {ODI,0,ARG_STATUS, 0, 5,A_PS0,A_USR,A_DYN,A_ARG,A_ANY, 0, 0, 0, 0, opt_show_origs, + 0, "show status\n"}, + + {ODI,0,ARG_ROUTES, 0, 5,A_PS0,A_USR,A_DYN,A_ARG,A_ANY, 0, 0, 0, 0, opt_show_origs, + 0, "show routes\n"}, + + {ODI,0,ARG_LINKS, 0, 5,A_PS0,A_USR,A_DYN,A_ARG,A_ANY, 0, 0, 0, 0, opt_show_origs, + 0, "show links\n"}, + + {ODI,0,ARG_ORIGINATORS, 0, 5,A_PS0,A_USR,A_DYN,A_ARG,A_ANY, 0, 0, 0, 0, opt_show_origs, + 0, "show originators\n"}, + + {ODI,0,ARG_DEV, 0, 5,A_PMN,A_ADM,A_DYI,A_CFA,A_ANY, 0, 0, 0, 0, opt_dev, + "<interface-name>", "add or change device or its configuration, options for specified device are:"}, + +#ifndef LESS_OPTIONS + {ODI,ARG_DEV,ARG_DEV_TTL, 't',5,A_CS1,A_ADM,A_DYI,A_CFA,A_ANY, 0, MIN_TTL, MAX_TTL, DEF_TTL, opt_dev, + ARG_VALUE_FORM, "set TTL of generated OGMs"}, + + {ODI,ARG_DEV,ARG_DEV_CLONE, 'c',5,A_CS1,A_ADM,A_DYI,A_CFA,A_ANY, 0, MIN_WL_CLONES, MAX_WL_CLONES, DEF_WL_CLONES, opt_dev, + ARG_VALUE_FORM, "broadcast OGMs per ogm-interval with given probability (e.g. 200% will broadcast the same OGM twice)"}, + + /* Antenna-diversity support for bmxd seems working but unfortunately there are few wireless drivers which support + * my understanding of the typical antenna-diversity implementation. This is what I hoped (maybe I am wrong): + * - The RX-antenna is detected on-the-fly on a per-packet basis by comparing + * the rcvd signal-strength via each antenna during reception of the phy-preamble. + * - The TX-antenna is determined per MAC-address based on the last detected best RX-antenna for this MAC. + * - Broadcast packets should be send round-robin like via each enabled TX-antenna (e.g. alternating via ant1 and ant2). */ + {ODI,ARG_DEV,ARG_DEV_ANTDVSTY, 0, 5,A_CS1,A_ADM,A_DYI,A_CFA,A_ANY, 0, 1, 2, 1, opt_dev, + ARG_VALUE_FORM, 0/*"set number of broadcast antennas (e.g. for antenna-diversity use /d=2 /c=400 aggreg_interval=100)"*/}, + + + {ODI,ARG_DEV,ARG_DEV_ANNOUNCE, 'a',5,A_CS1,A_ADM,A_DYI,A_CFA,A_ANY, 0, 0, 1, DEF_DEV_ANNOUNCE,opt_dev, + ARG_VALUE_FORM, "disable/enable announcement of interface IP"}, +#endif + + {ODI,ARG_DEV,ARG_DEV_LL, 'l',5,A_CS1,A_ADM,A_DYI,A_CFA,A_ANY, 0, VAL_DEV_LL_LAN, VAL_DEV_LL_WLAN,0, opt_dev, + ARG_VALUE_FORM, "manually set device type for linklayer specific optimization (1=lan, 2=wlan)"}, + + {ODI,0,ARG_INTERFACES, 0, 5,A_PS0,A_USR,A_DYI,A_ARG,A_ANY, 0, 0, 1, 0, opt_dev_show, + 0, "show configured interfaces"}, + + {ODI,0,ARG_LWS, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &local_lws, MIN_LWS, MAX_LWS, DEF_LWS, opt_link_metric, + ARG_VALUE_FORM, "set link window size (LWS) for link-quality calculation (link metric)"}, + +#ifndef LESS_OPTIONS + + {ODI,0,ARG_RTQ_LOUNGE, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &local_rtq_lounge,MIN_RTQ_LOUNGE,MAX_RTQ_LOUNGE,DEF_RTQ_LOUNGE, opt_link_metric, + ARG_VALUE_FORM, "set local LLS buffer size to artificially delay OGM processing for ordered link-quality calulation"}, + + {ODI,0,ARG_PWS, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &my_pws, MIN_PWS, MAX_PWS, DEF_PWS, opt_path_metric, + ARG_VALUE_FORM, "set path window size (PWS) for end2end path-quality calculation (path metric)"}, + + {ODI,0,ARG_PATH_LOUNGE, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &my_path_lounge,MIN_PATH_LOUNGE,MAX_PATH_LOUNGE,DEF_PATH_LOUNGE, opt_path_metric, + ARG_VALUE_FORM, "set default PLS buffer size to artificially delay my OGM processing for ordered path-quality calulation"}, + + {ODI,0,ARG_PATH_HYST, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &my_path_hystere,MIN_PATH_HYST, MAX_PATH_HYST, DEF_PATH_HYST, opt_path_metric, + ARG_VALUE_FORM, "use hysteresis to delay route switching to alternative next-hop neighbors with better path metric"}, + + + {ODI,0,ARG_RCNT_PWS, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &my_rcnt_pws, MIN_RCNT_PWS, MAX_RCNT_PWS, DEF_RCNT_PWS, opt_path_metric, + ARG_VALUE_FORM, ""}, + + {ODI,0,ARG_RCNT_HYST, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &my_rcnt_hystere,MIN_RCNT_HYST, MAX_RCNT_HYST, DEF_RCNT_HYST, opt_path_metric, + ARG_VALUE_FORM, "use hysteresis to delay fast-route switching to alternative next-hop neighbors with a recently extremely better path metric"}, + + {ODI,0,ARG_RCNT_FK, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &my_rcnt_fk, MIN_RCNT_FK, MAX_RCNT_FK, DEF_RCNT_FK, opt_path_metric, + ARG_VALUE_FORM, "configure threshold faktor for dead-path detection"}, + + + {ODI,0,ARG_DROP_2HLOOP, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &drop_2hop_loop,MIN_DROP_2HLOOP,MAX_DROP_2HLOOP,DEF_DROP_2HLOOP,0, + ARG_VALUE_FORM, "drop OGMs received via two-hop loops"}, + + + {ODI,0,ARG_ASYM_EXP, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &asym_exp, MIN_ASYM_EXP, MAX_ASYM_EXP, DEF_ASYM_EXP, 0, + ARG_VALUE_FORM, "ignore OGMs (rcvd via asymmetric links) with TQ^<val> to radically reflect asymmetric-links"}, + + {ODI,0,"asocial_device", 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &Asocial_device,MIN_ASOCIAL, MAX_ASOCIAL, DEF_ASOCIAL, 0, + ARG_VALUE_FORM, "disable/enable asocial mode for devices unwilling to forward other nodes' traffic"}, + + {ODI,0,ARG_WL_CLONES, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &wl_clones, MIN_WL_CLONES, MAX_WL_CLONES, DEF_WL_CLONES, opt_path_metric, + ARG_VALUE_FORM, "broadcast OGMs per ogm-interval for wireless devices with\n" + " given probability [%] (eg 200% will broadcast the same OGM twice)"}, + + {ODI,0,ARG_ASYM_WEIGHT, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &asym_weight, MIN_ASYM_WEIGHT,MAX_ASYM_WEIGHT,DEF_ASYM_WEIGHT,opt_path_metric, + ARG_VALUE_FORM, "ignore OGMs (rcvd via asymmetric links) with given probability [%] to better reflect asymmetric-links"}, + + // there SHOULD! be a minimal lateness_penalty >= 1 ! Otherwise a shorter path with equal path-cost than a longer path will never dominate + {ODI,0,ARG_LATE_PENAL, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &my_late_penalty,MIN_LATE_PENAL,MAX_LATE_PENAL, DEF_LATE_PENAL, opt_path_metric, + ARG_VALUE_FORM, "penalize non-first rcvd OGMs "}, + +#endif + {ODI,0,ARG_DAD_TO, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &dad_to, MIN_DAD_TO, MAX_DAD_TO, DEF_DAD_TO, 0, + ARG_VALUE_FORM, "duplicate address (DAD) detection timout in seconds"}, + + {ODI,0,ARG_TTL, 't',5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &my_ttl, MIN_TTL, MAX_TTL, DEF_TTL, opt_path_metric, + ARG_VALUE_FORM, "set time-to-live (TTL) for OGMs of primary interface"}, + + {ODI,0,ARG_PURGE_TO, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &purge_to, MIN_PURGE_TO, MAX_PURGE_TO, DEF_PURGE_TO, 0, + ARG_VALUE_FORM, "timeout in seconds for purging stale originators"} +, + + {ODI,0,"flush_all", 0, 5,A_PS0,A_ADM,A_DYN,A_ARG,A_ANY, 0, 0, 0, 0, opt_purge, + 0, "purge all neighbors and routes on the fly"}, + + {ODI,0,ARG_OGI_PWRSAVE, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &ogi_pwrsave, MIN_OGM_INTERVAL, MAX_OGM_INTERVAL, MIN_OGM_INTERVAL, 0, + ARG_VALUE_FORM, "enable power-saving feature by setting increased OGI when no other nodes are in range"} + + +}; + +IDM_T validate_name( char* name ) { + + int i,len; + if ( (len = strlen( name )) >= DESCRIPTION0_ID_NAME_LEN ) + return FAILURE; + + for (i = 0; i < len; i++) { + + char c = name[i]; + + if (c == '"' || c < ' ' || c > '~') + return FAILURE; + + } + + return SUCCESS; +} + +STATIC_FUNC +void init_bmx(void) +{ + + static uint8_t my_desc0[MAX_PKT_MSG_SIZE]; + + memset( &my_orig_node, 0, sizeof(struct orig_node)); + + AVL_INIT_TREE((my_orig_node.router_tree), struct router_node, key); + + my_orig_node.desc0 = (struct description *) my_desc0; + + if (gethostname(my_orig_node.id.name, DESCRIPTION0_ID_NAME_LEN)) + cleanup_all(-500240); + + my_orig_node.id.name[DESCRIPTION0_ID_NAME_LEN - 1] = 0; + + if (validate_name(my_orig_node.id.name) == FAILURE) { + dbg(DBGL_SYS, DBGT_ERR, "illegal hostname %s", my_orig_node.id.name); + cleanup_all(-500272); + } + + my_orig_node.id.rand.u16[0] = (uint16_t) rand_num(U16_MAX); + my_orig_node.id.rand.u16[1] = (uint16_t) rand_num(U16_MAX); + my_orig_node.id.rand.u16[2] = (uint16_t) rand_num(U16_MAX); + my_orig_node.id.rand.u16[3] = (uint16_t) rand_num(U16_MAX); + + my_orig_node.ogm_sqn_min = (((uint16_t) rand_num(MAX_SQN)) & (MAX_SQN << MAX_OGM0_PQ_BITS)); + + my_orig_node.desc0_sqn = rand_num(MAX_SQN); + + avl_insert( &orig_tree, &my_orig_node, -300175 ); + + register_options_array( bmx_options, sizeof( bmx_options ) ); + +} + + + + +STATIC_FUNC +void bmx(void) +{ + + struct list_node *list_pos; + struct dev_node *dev; + uint32_t regular_timeout, statistic_timeout; + + uint32_t s_last_cpu_time = 0, s_curr_cpu_time = 0; + + regular_timeout = statistic_timeout = bmx_time; + + on_the_fly = YES; + + while ( !terminating() ) { + + uint32_t wait = whats_next( ); + + if ( wait ) + wait4Event( MIN( wait, MAX_SELECT_TIMEOUT_MS ) ); + + // The regular tasks... + if ( LESS_U32( regular_timeout + 1000, bmx_time ) ) { + + + // check for changed kernel konfigurations... + check_kernel_config( NULL ); + + // check for changed interface konfigurations... + struct avl_node *an = NULL; + while ((dev = avl_iterate_item(&dev_name_tree, &an))) { + + if ( dev->active ) + check_kernel_config( dev ); + + purge_tx_timestamp_tree(dev, NO); + + } + + purge_orig(NULL, YES); + + purge_dhash_to_list(NO); + + close_ctrl_node( CTRL_CLEANUP, 0 ); + + list_for_each( list_pos, &dbgl_clients[DBGL_ALL] ) { + + struct ctrl_node *cn = (list_entry( list_pos, struct dbgl_node, list ))->cn; + + dbg_printf( cn, "------------------ DEBUG ------------------ \n" ); + + check_apply_parent_option( ADD, OPT_APPLY, 0, get_option( 0, 0, ARG_STATUS ), 0, cn ); + check_apply_parent_option( ADD, OPT_APPLY, 0, get_option( 0, 0, ARG_LINKS ), 0, cn ); + check_apply_parent_option( ADD, OPT_APPLY, 0, get_option( 0, 0, ARG_ORIGINATORS ), 0, cn ); + dbg_printf( cn, "--------------- END DEBUG ---------------\n" ); + + } + + /* preparing the next debug_timeout */ + regular_timeout = bmx_time; + } + + + if ( LESS_U32( statistic_timeout + 5000, bmx_time ) ) { + + struct orig_node *on; + struct description_id id; + + memset(&id, 0, sizeof (struct description_id)); + + while ((on = avl_next_item(&blocked_tree, &id))) { + + memcpy( &id, &on->id, sizeof(struct description_id)); + + dbgf(DBGL_CHANGES, DBGT_INFO, "trying to unblock %s...", on->desc0->id.name); + + IDM_T tlvs_res = process_description_tlvs(on, on->desc0, TLV_DEL_TEST_ADD, NULL); + + assertion(-500364, (tlvs_res == TLVS_BLOCKED || tlvs_res == TLVS_SUCCESS)); + + dbgf(DBGL_CHANGES, DBGT_INFO, "...unblocking %s %s !", + on->desc0->id.name, tlvs_res == TLVS_SUCCESS ? "success" : "failed"); + + } + + + // check for corrupted memory.. + checkIntegrity(); + + + /* generating cpu load statistics... */ + s_curr_cpu_time = (uint32_t)clock(); + + s_curr_avg_cpu_load = ( (s_curr_cpu_time - s_last_cpu_time) / (uint32_t)(bmx_time - statistic_timeout) ); + + s_last_cpu_time = s_curr_cpu_time; + + statistic_timeout = bmx_time; + } + } + +} + +int main( int argc, char *argv[] ) +{ + + + // make sure we are using compatible description0 sizes: + assertion(-500201, (MSG_DESCRIPTION0_ADV_SIZE == sizeof ( struct msg_description_adv))); + + gettimeofday( &start_time_tv, NULL ); + gettimeofday( &new_tv, NULL ); + + upd_time( NULL ); + + My_pid = getpid(); + + srand( My_pid ); + + init_set_bits_table256(); + + + signal( SIGINT, handler ); + signal( SIGTERM, handler ); + signal( SIGPIPE, SIG_IGN ); + signal( SIGSEGV, segmentation_fault ); + + init_control(); + + init_route(); + + init_bmx(); + + init_msg(); + + init_schedule(); + + init_avl(); + + init_plugin(); + + apply_init_args( argc, argv ); + + check_kernel_config( NULL ); + + bmx(); + + cleanup_all( CLEANUP_SUCCESS ); + + return -1; +} + + +
Added: trunk/bmx/bmx.h =================================================================== --- trunk/bmx/bmx.h (rev 0) +++ trunk/bmx/bmx.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,932 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#include <stdint.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <linux/if.h> + +#include "cyassl/sha.h" + +#include "avl.h" +#include "list.h" +#include "iid.h" +#include "control.h" +#include "allocate.h" + + +/* + * from other headers: + */ + +#define IDM_T int8_t // int which size does NOT matter + +// dont touch this for compatibility reasons: +#define IP4_T uint32_t + +#define DESCRIPTION0_ID_RANDOM_T uint64_t +#define DESCRIPTION0_ID_RANDOM_LEN sizeof( DESCRIPTION0_ID_RANDOM_T ) +#define DESCRIPTION0_ID_NAME_LEN 22 + +#define BMX_HASH0_LEN SHA_DIGEST_SIZE // sha.h: 20 bytes +#define BMX_PKEY0_LEN 1024 + +struct description_id { + char name[DESCRIPTION0_ID_NAME_LEN]; + union { + uint8_t u8[DESCRIPTION0_ID_RANDOM_LEN]; + uint16_t u16[DESCRIPTION0_ID_RANDOM_LEN / sizeof(uint16_t)]; + uint32_t u32[DESCRIPTION0_ID_RANDOM_LEN / sizeof(uint32_t)]; + uint64_t u64[DESCRIPTION0_ID_RANDOM_LEN / sizeof( uint64_t)]; + } rand; +} __attribute__((packed)); + +struct description_hash { + union { + uint8_t u8[BMX_HASH0_LEN]; + uint32_t u32[BMX_HASH0_LEN/sizeof(uint32_t)]; + } h; +}; + +// from msg.h +#define FRAME_TYPE_ARRSZ 14 + + +/* + * bmx.h: + */ + + +#define MIN_OGM0_SQN_RANGE 64 +#define MAX_OGM0_SQN_RANGE 8192// <=> 4096 / ogm_sqn_step_size=8 * ogm_interval=1000 = ~512sec +#define DEF_OGM0_SQN_RANGE 8192// <=> 1024 / ogm_sqn_step_size=8 * ogm_interval=1000 = ~128sec + +#define ARG_OGM0_SQN_RANGE "ogm0_validity_range" + +#define SQN_DAD_RANGE 64 // if rcvd sqn is >= last_rcvd_sqn + SQN_TIMEOUT_RANGE then DAD +#define SQN_T uint16_t + +#define MIN_SQN 0 +#define MAX_SQN ((SQN_T)-1) +#define DEF_SQN 0 /* causes seqno to be randomized */ + + +#define MIN_OGM0_PQ_BITS 0 +#define MAX_OGM0_PQ_BITS 5 +#define DEF_OGM0_PQ_BITS 5 +#define ARG_OGM0_PQ_BITS "path_quality_bits" + + + + + + +#define MIN_MASK 1 +#define MAX_MASK 32 +#define ARG_MASK "netmask" +#define ARG_NETW "network" + + +#define ARG_DEBUG "debug" +#define ARG_NO_FORK "no_fork" +#define ARG_QUIT "quit" + +#define ARG_CONNECT "connect" +#define ARG_RUN_DIR "runtime_dir" +#define DEF_RUN_DIR "/var/run/bmx" + + +extern uint32_t My_pid; +#define BMX_ENV_LIB_PATH "BMX_LIB_PATH" +#define BMX_DEF_LIB_PATH "/usr/lib" +// e.g. sudo BMX_LIB_PATH="$(pwd)/lib" ./bmxd -d3 eth0:bmx +#define BMX_ENV_DEBUG "BMX_DEBUG" + + + + +#define ARG_HELP "help" +#define ARG_VERBOSE_HELP "verbose_help" +#define ARG_EXP "exp_help" +#define ARG_VERBOSE_EXP "verbose_exp_help" + +#define ARG_VERSION "version" +#define ARG_TRAILER "trailer" + +#define ARG_TEST "test" +#define ARG_SHOW_CHANGED "options" + + +#define ARG_DEV "dev" +#define ARG_DEV_TTL "ttl" +#define ARG_DEV_CLONE "clone" +#define ARG_DEV_ANTDVSTY "ant_diversity" +#define ARG_DEV_LL "linklayer" +#define ARG_DEV_ANNOUNCE "announce" +#define DEF_DEV_ANNOUNCE YES + +#define VAL_DEV_LL_LO 0 +#define VAL_DEV_LL_LAN 1 +#define VAL_DEV_LL_WLAN 2 + + +#define ARG_ORIGINATORS "originators" +#define ARG_STATUS "status" +#define ARG_LINKS "links" +#define ARG_ROUTES "routes" +#define ARG_INTERFACES "interfaces" + +#define ARG_THROW "throw" + + +#define PROBE_RANGE 1024 + + + +#define MAX_PWS 1024 /* 250 TBD: should not be larger until ogm->ws and neigh_node.packet_count (and related variables) is only 8 bit */ +#define MIN_PWS 32 +#define DEF_PWS 256 /* NBRF: NeighBor Ranking sequence Frame) sliding packet range of received orginator messages in squence numbers (should be a multiple of our word size) */ +#define ARG_PWS "path_window_size" +extern int32_t my_pws; // my path window size used to quantify the end to end path quality between me and other nodes + + +#define DEF_LWS 64 +#define MAX_LWS 250 +#define MIN_LWS 1 +#define ARG_LWS "link_window_size" +extern int32_t local_lws; // my link window size used to quantify the link qualities to direct neighbors + + +// the default link_lounge_size of 2 is good to compensate for ogi ~ but <= aggreg_interval +#define MIN_RTQ_LOUNGE 0 +#define MAX_RTQ_LOUNGE 10 +#define DEF_RTQ_LOUNGE 2 +#define ARG_RTQ_LOUNGE "link_lounge_size" +extern int32_t local_rtq_lounge; + +#define RQ_LINK_LOUNGE 0 /* may also be rtq_link_lounge */ + + +#define MIN_PATH_LOUNGE 0 +#define MAX_PATH_LOUNGE (0x01 << MAX_OGM0_PQ_BITS) +#define DEF_PATH_LOUNGE (0x01 << DEF_OGM0_PQ_BITS) +#define ARG_PATH_LOUNGE "path_lounge_size" +extern int32_t my_path_lounge; + +#define MIN_PATH_HYST 0 +#define MAX_PATH_HYST (PROBE_RANGE)/2 +#define DEF_PATH_HYST 0 +#define ARG_PATH_HYST "path_hysteresis" +extern int32_t my_path_hystere; + +#define MIN_RCNT_HYST 0 +#define MAX_RCNT_HYST (PROBE_RANGE)/2 +#define DEF_RCNT_HYST 10 +#define ARG_RCNT_HYST "fast_path_hysteresis" +extern int32_t my_rcnt_hystere; + +#define DEF_RCNT_PWS 10 +#define MIN_RCNT_PWS 2 +#define MAX_RCNT_PWS 50 +#define ARG_RCNT_PWS "fast_path_window_size" +extern int32_t my_rcnt_pws; + + +#define DEF_RCNT_FK 4 +#define MIN_RCNT_FK 1 +#define MAX_RCNT_FK 11 +#define ARG_RCNT_FK "fast_path_faktor" +extern int32_t my_rcnt_fk; + +#define MIN_LATE_PENAL 0 +#define MAX_LATE_PENAL 100 +#define DEF_LATE_PENAL 1 +#define ARG_LATE_PENAL "lateness_penalty" +extern int32_t my_late_penalty; + +#define MIN_DROP_2HLOOP NO +#define MAX_DROP_2HLOOP YES +#define DEF_DROP_2HLOOP NO +#define ARG_DROP_2HLOOP "drop_two_hop_loops" + + +#define DEF_DAD_TO 50000 +#define MIN_DAD_TO 100 +#define MAX_DAD_TO 360000000 +#define ARG_DAD_TO "dad_timeout" +extern int32_t dad_to; + +#define MIN_ASOCIAL NO +#define MAX_ASOCIAL YES +#define DEF_ASOCIAL NO + +#define DEF_TTL 50 /* Time To Live of OGM broadcast messages */ +#define MAX_TTL 63 +#define MIN_TTL 1 +#define ARG_TTL "ttl" +extern int32_t my_ttl; + +#define DEF_WL_CLONES 200 +#define MIN_WL_CLONES 0 +#define MAX_WL_CLONES 400 +#define ARG_WL_CLONES "ogm_broadcasts" +extern int32_t wl_clones; + +#define DEF_LAN_CLONES 100 + + +#define DEF_ASYM_WEIGHT 100 +#define MIN_ASYM_WEIGHT 0 +#define MAX_ASYM_WEIGHT 100 +#define ARG_ASYM_WEIGHT "asymmetric_weight" +extern int32_t asym_weight; + + + +#define DEF_SYM_WEIGHT 80 +#define MIN_SYM_WEIGHT 0 +#define MAX_SYM_WEIGHT 100 +#define ARG_SYM_WEIGHT "symmetric_weight" +extern int32_t sym_weight; + +#define DEF_ASYM_EXP 1 +#define MIN_ASYM_EXP 0 +#define MAX_ASYM_EXP 3 +#define ARG_ASYM_EXP "asymmetric_exp" + +#define DEF_HOP_PENALTY 1 +#define MIN_HOP_PENALTY 0 +#define MAX_HOP_PENALTY 100 +#define ARG_HOP_PENALTY "hop_penalty" +extern int32_t my_hop_penalty; + +#define ARG_OGI_PWRSAVE "ogi_power_save" + + + +#define DEF_PURGE_TO 50000 +#define MIN_PURGE_TO 100 +#define MAX_PURGE_TO 864000000 /*10 days*/ +#define ARG_PURGE_TO "purge_timeout" +// extern int32_t purge_to; + +#define MIN_DHASH_TO 300000 +#define DHASH_TO_TOLERANCE_FK 10 + + + + + +#define OGM_AGGREG_SQN_CACHE_RANGE 64 +#define OGM_AGGREG_SQN_CACHE_WARN (OGM_AGGREG_SQN_CACHE_RANGE/2) +#define OGM_AGGREG_ARRAY_BYTE_SIZE (OGM_AGGREG_SQN_CACHE_RANGE/8) + + + + + + +#define SOURCE_VERSION "0.4-alpha" //put exactly one distinct word inside the string like "0.3-pre-alpha" or "0.3-rc1" or "0.3" + +#define COMPAT_VERSION 11 + +#define IP4_STR_LEN 16 + +#define MAX_DBG_STR_SIZE 1500 +#define OUT_SEQNO_OFFSET 1 + +enum NoYes { + NO, + YES +}; + +enum ADGSN { + ADD, + DEL, + GET, + SET, + NOP +}; + + +#define SUCCESS 0 +#define FAILURE -1 + +extern void* FAILURE_POINTER; + +#define ILLEGAL_STATE "Illegal program state. This should not happen!" + +#ifndef REVISION_VERSION +#define REVISION_VERSION 0 +#endif + +#define MAX_SELECT_TIMEOUT_MS 400 /* MUST be smaller than (1000/2) to fit into max tv_usec */ +#define CRITICAL_PURGE_TIME_DRIFT 5 + +#define RAND_INIT_DELAY 50 +#define COMMON_OBSERVATION_WINDOW (DEF_OGM_INTERVAL*DEF_PWS) + +//#define TYPE_OF_WORD unsigned long /* you should choose something big, if you don't want to waste cpu */ +//#define WORD_BIT_SIZE ( sizeof(TYPE_OF_WORD) * 8 ) + +#define MAX( a, b ) ( (a>b) ? (a) : (b) ) +#define MIN( a, b ) ( (a<b) ? (a) : (b) ) + +#define U32_MAX 4294967296 +#define I32_MAX 2147483647 +#define U16_MAX 65536 +#define I16_MAX 32767 +#define U8_MAX 256 +#define I8_MAX 127 + + + +#define LESS_SQN( a, b ) ( ((uint16_t)( (a) - (b) ) ) > I16_MAX ) +#define LSEQ_SQN( a, b ) ( ((uint16_t)( (b) - (a) ) ) <= I16_MAX ) +#define GREAT_SQN( a, b ) ( ((uint16_t)( (b) - (a) ) ) > I16_MAX ) +#define GRTEQ_SQN( a, b ) ( ((uint16_t)( (a) - (b) ) ) <= I16_MAX ) + +#define LESS_U32( a, b ) ( ((uint32_t)( (a) - (b) ) ) > I32_MAX ) +#define LSEQ_U32( a, b ) ( ((uint32_t)( (b) - (a) ) ) <= I32_MAX ) +#define GREAT_U32( a, b ) ( ((uint32_t)( (b) - (a) ) ) > I32_MAX ) +#define GRTEQ_U32( a, b ) ( ((uint32_t)( (a) - (b) ) ) <= I32_MAX ) + +#define MAX_SQ( a, b ) ( (GREAT_SQN( (a), (b) )) ? (a) : (b) ) + + + + +#define WARNING_PERIOD 20000 + +#define MAX_PATH_SIZE 300 +#define MAX_ARG_SIZE 200 + + +extern uint32_t bmx_time; +extern uint32_t bmx_time_sec; + +extern uint8_t on_the_fly; + + +extern uint32_t s_curr_avg_cpu_load; + + + + + +extern struct dev_node *primary_if; + + +extern struct orig_node my_orig_node; + + +//extern struct list_head if_list; + +extern struct avl_tree dev_ip4_tree; +extern struct avl_tree dev_name_tree; + +extern struct avl_tree link_tree; +extern struct avl_tree link_dev_tree; + +extern struct avl_tree neigh_tree; + +extern struct avl_tree dhash_tree; +extern struct avl_tree dhash_invalid_tree; + +extern struct avl_tree orig_tree; +extern struct avl_tree blocked_tree; +extern struct avl_tree blacklisted_tree; + + + + + +/** + * The most important data structures + */ + + +struct ogm_aggreg_node { + + struct list_node list; + + struct msg_ogm_adv *ogm_advs; + + uint16_t aggregated_ogms; + + SQN_T sqn; + uint8_t tx_attempts; + uint32_t tx_timestamp; +}; + + +struct packet_buff { + + //filled by wait4Event() +#define pkt_buff_llip4 addr.sin_addr.s_addr + + unsigned char packet_in[2001]; + struct sockaddr_in addr; + struct timeval tv_stamp; + struct dev_node *iif; + int total_length; + uint8_t unicast; + + //filled in by rx_packet() + char neigh_str[IP4_STR_LEN]; + struct dev_node *oif; + struct link_node *ln; + + //filled in by rx_frm_hey0_reps() or rx_frame() + struct link_dev_node *lndev; + +}; + + + + +struct task_node +{ + struct list_node list; + uint32_t expire; + void (* task) (void *fpara); // pointer to the function to be executed + void *data; //NULL or pointer to data to be given to function. Data will be freed after functio is called. +}; + + + +struct tx_task_node { + struct list_node list; + + IP4_T dst_ip4; + IID_T myIID4x; + IID_T neighIID4x; + SQN_T sqn; + uint16_t frame_data_length_target; // because some variable msgs sizes may vary between scheduling and tx + uint8_t frame_type; + uint8_t tx_iterations; + uint32_t tx_timestamp; + struct dev_node *dev; // the outgoing interface to be used for transmitting + +}; + +struct tx_timestamp_key { + IP4_T myIID4x_or_dest_ip4; + IID_T neighIID4x; + uint16_t type; +}; + +struct tx_timestamp_node { + struct tx_timestamp_key key; + uint32_t timestamp; +}; + +struct dev_node +{ + struct list_node list; + char name[IFNAMSIZ]; + char name_phy[IFNAMSIZ]; + + char ip4_str[IP4_STR_LEN]; + + int32_t index; + + uint8_t active; + + uint16_t ip4_prefix_length; + + uint32_t ip4_addr; + uint32_t ip4_tree_addr; + uint32_t ip4_netaddr; + uint32_t ip4_broad; + uint32_t ip4_netmask; + + int ip4_mtu; + + int32_t rp_filter_orig; + int32_t send_redirects_orig; + + + struct sockaddr_in ip4_unicast_addr; + struct sockaddr_in ip4_netwbrc_addr; + + int32_t unicast_sock; + int32_t netwbrc_sock; + int32_t fullbrc_sock; + + SQN_T packet_sqn; + SQN_T ogm_sqn; + uint32_t link_activity_timestamp; + uint32_t next_powersave_hardbeat; + + uint16_t misc_flag; + + struct list_head tx_tasks_list[FRAME_TYPE_ARRSZ]; // scheduled frames and messages + struct tx_task_node *my_tx_tasks[FRAME_TYPE_ARRSZ]; + uint16_t tx_frames_data_len_target; // target (should) length of all currently scheduled tx messages (not including packet- or frame-headers + + struct avl_tree tx_timestamp_tree; // timestamps of recently send messages, ordered by type and iid + + // having a pointer right before the following array ensures 32/64 bit alignment. +// unsigned char *aggregation_out; +// unsigned char aggregation_out_buff[MAX_UDPD_SIZE + 1]; + +// int16_t aggregation_len; + + + int8_t soft_conf_changed; + int8_t hard_conf_changed; + + int8_t linklayer_conf; + int8_t linklayer; + + int16_t send_clones_conf; + int16_t send_clones; + + int16_t antenna_diversity_conf; + int16_t antenna_diversity; + + int8_t announce_conf; + int8_t announce; + +}; + + + +struct metric_algo { + SQN_T sqn_mask; + SQN_T sqn_steps; // e.g. 2 + SQN_T sqn_window; // MUST be given as multiple of sqn_steps + SQN_T sqn_lounge; // MUST be given as multiple of sqn_steps e.g. 6 + uint32_t regression; // e.g. window_size/2 + uint32_t metric_max; // e.g. 256 +}; + +#define SQR_RTQ 0x00 +#define SQR_RQ 0x01 +#define SQR_RANGE 0x02 + +extern struct metric_algo link_metric_algo[SQR_RANGE]; + +#define TQ_RATE( lndev, range) ( ((lndev)->mr[SQR_RQ].val ) ? \ +(MIN( ( ((range) * ((lndev)->mr[SQR_RTQ].val) ) / ((lndev)->mr[SQR_RQ].val) ), (range) )): \ +(0) ) + + +struct metric_record { + SQN_T clr; // SQN upto which waightedAverageVal has been purged + SQN_T set; // SQN which has been applied (if equals wa_pos) then wa_unscaled MUST NOT be set again! + uint32_t val; +}; + + + + + +struct link_key { + IP4_T llip4; + struct dev_node *dev; +}; + +struct link_dev_node { + struct list_node list; + + struct link_key key; + + uint32_t pkt_time_max; + + uint32_t rtq_time_max; + + struct link_node *link; + +// struct sq_record sqr[SQR_RANGE]; + + struct metric_record mr[SQR_RANGE]; + +}; + +/* MUST be allocated and initiated all or nothing ! + * MUST be initiated with any unidirectional received OGM + * from a direct link NB + */ +/* Only OG interfaces which are direct link neighbors have a link_node + * Because neighboring interfaces may be seen via several of our own interfaces + * each link_node points to one or several link_node_dev structures + */ +struct link_node +{ + uint32_t llip4; + char llip4_str[IP4_STR_LEN]; + + uint32_t pkt_time_max; + SQN_T pkt_sqn_max; + + SQN_T rq_sqn_max; + uint32_t rq_time_max; + + struct neigh_node *neigh; + + struct list_head lndev_list; // list with one link_node_dev element per link + +}; + + + +/* Path statistics per neighbor via which OGMs of the parent orig_node have been received */ +/* Every OG has one ore several neigh_nodes. */ +struct router_node { + + struct link_key key; + +// SQN_T ogm_sqn_to_be_send; + + struct metric_record mr; + +// uint32_t metric; + + + +}; + +struct orig_node { + + // filled in by validate_new_link_desc0(): + + struct description_id id; + + struct dhash_node *dhn; + struct description *desc0; +// struct orig_node *orig_key; + + uint32_t updated_timestamp; // last time this on's desc was succesfully updated +// uint32_t referred_timestamp;// use dhn->referred_timestamp instead + + SQN_T desc0_sqn; + + SQN_T ogm_sqn_min; + SQN_T ogm_sqn_to_be_send; + SQN_T ogm_sqn_aggregated; + + SQN_T ogm_sqn_range; + +// SQN_T ogm_sqn_mask; +// SQN_T ogm_sqn_steps; + + SQN_T ogm_sqn_max_rcvd; + + uint8_t ogm_sqn_pq_bits; + + struct metric_algo path_metric_algo; + + uint8_t blocked; + + // not yet filled in: + + // filled in by process_desc0_tlvs()-> + + uint32_t primary_ip4; + char primary_ip4_str[IP4_STR_LEN]; + + + // filled in by ??? + + + struct link_key router_key; // the neighbor which is the currently best_next_hop + struct avl_tree router_tree; + uint32_t router_path_metric; + + +/* + // old unused: + + + + + uint32_t last_aware; // when last valid ogm via this node was received + uint32_t last_valid_time; // when last valid ogm from this node was received + + uint32_t first_valid_sec; // only used for debugging purposes + + SQN_T last_decided_sqn; + SQN_T last_accepted_sqn; // last squence number acceppted for metric + SQN_T last_valid_sqn; // last and best known squence number + SQN_T last_wavg_sqn; // last sequence number used for estimating ogi + + uint8_t last_path_ttl; + int8_t last_path_change_scenario; + + uint8_t pws; + uint8_t path_lounge; + uint8_t path_hystere; + uint8_t late_penalty; + + uint8_t rcnt_pws; + uint8_t rcnt_hystere; + uint8_t rcnt_fk; + + uint32_t ogi_wavg; + uint32_t rt_changes; +*/ + /*size of plugin data is defined during intialization and depends on registered plugin-data hooks */ + void *plugin_data[]; + +}; + + +struct neigh_node { + + struct neigh_node *nnkey; + + struct avl_tree link_tree; + + struct dhash_node *dhn; + + + // filled in by ???: + + IID_T neighIID4me; + IID_T neighIID4neigh; + + struct iid_repos neighIID4x_repos; + + // filled in by ???: + struct link_dev_node *best_rtq; + + // filled in by ???: + SQN_T ogm_aggregation_rcvd_max; + uint8_t ogm_aggregations_acked[OGM_AGGREG_ARRAY_BYTE_SIZE]; + uint8_t ogm_aggregations_rcvd[OGM_AGGREG_ARRAY_BYTE_SIZE]; + +}; + + +struct dhash_node { + + struct description_hash dhash; + + // filled in by rx_frm_hiX0_advs() -> get_orig_node(): + + uint32_t referred_timestamp; // last time this dhn was referred + + struct neigh_node *neigh; + + IID_T myIID4orig; + + + // filled in by rx_frm_hi40_reps(): + + struct orig_node *on; + + // filled in by expire_dhash_node() + // uint8_t invalid; equal to on == NULL +}; + + +struct black_node { + + struct description_hash dhash; +}; + + + + +/* list element to store all the disabled tunnel rule netmasks */ +struct throw_node +{ + struct list_node list; + uint32_t addr; + uint8_t netmask; +}; + + + + +# define timercpy(d, a) (d)->tv_sec = (a)->tv_sec; (d)->tv_usec = (a)->tv_usec; + + + +enum { + CLEANUP_SUCCESS, + CLEANUP_FAILURE, + CLEANUP_MY_SIGSEV, + CLEANUP_RETURN +}; + +/* + * PARANOIA ERROR CODES: + * Negative numbers are used as SIGSEV error codes ! + * Currently used numbers are: -500000 ... -500501 + */ + +#ifdef NO_PARANOIA +#define paranoia( ... ) +#define assertion( ... ) +#define ASSERTION( ... ) +#else +#define paranoia( code , problem ); do { if ( (problem) ) { cleanup_all( code ); } }while(0) +#define assertion( code , condition ); do { if ( !(condition) ) { cleanup_all( code ); } }while(0) +#ifdef EXTREME_PARANOIA +#define ASSERTION( code , condition ); do { if ( !(condition) ) { cleanup_all( code ); } }while(0) +#define CHECK_INTEGRITY( ... ) checkIntegrity() +#else +#define CHECK_INTEGRITY( ... ) +#define ASSERTION( ... ) +#endif +#endif + + +#ifndef PROFILING +#define STATIC_FUNC static +#else +#define STATIC_FUNC +#endif + +#ifdef STATIC_VARIABLES +#define STATIC_VAR static +#else +#define STATIC_VAR +#endif + +/*********************************************************** + Data Infrastructure + ************************************************************/ + +void blacklist_neighbor(struct packet_buff *pb); + +IDM_T blacklisted_neighbor(struct packet_buff *pb, struct description_hash *dhash); + +IDM_T validate_metric_algo(struct metric_algo *ma, struct ctrl_node *cn); + +uint32_t update_metric(struct metric_record *mr, struct metric_algo *ma, SQN_T sqn_in, SQN_T sqn_max, uint32_t probe); + +void update_link_node(struct link_node *ln, struct dev_node *iif, SQN_T sqn, SQN_T sqn_max, uint8_t sqr, uint32_t probe); + +void purge_orig(struct dev_node *only_dev, IDM_T only_expired); +void free_orig_node(struct orig_node *on); + +IDM_T update_orig_metrics(struct packet_buff *pb, struct orig_node *on, IID_T orig_sqn); + +struct dhash_node* create_dhash_node(struct description_hash *dhash, struct orig_node *on); +void free_dhash_node( struct dhash_node *dhn ); +void invalidate_dhash_node( struct dhash_node *dhn ); + + +IDM_T update_neigh_node(struct link_node *ln, struct dhash_node *dhn, IID_T neighIID4neigh); + +IDM_T update_neighIID4x_repository(struct neigh_node *neigh, IID_T neighIID4x, struct dhash_node *dhn); + +struct dev_node * get_bif(char *dev); + +void rx_packet( struct packet_buff *pb ); +/*********************************************************** + Runtime Infrastructure +************************************************************/ +void wait_sec_msec( uint32_t sec, uint32_t msec ); + +void cleanup_all( int status ); + +void upd_time( struct timeval *precise_tv ); + +char *get_human_uptime( uint32_t reference ); + +int32_t rand_num( uint32_t limit ); + +int8_t terminating(); + +uint8_t bits_count( uint32_t v ); + +uint8_t bit_get(uint8_t *array, uint16_t array_bit_size, uint16_t bit); + +void bit_set(uint8_t *array, uint16_t array_bit_size, uint16_t bit, IDM_T value); + +void bit_clear(uint8_t *array, uint16_t array_bit_size, uint16_t begin, uint16_t end); + +void byte_clear(uint8_t *array, uint16_t array_size, uint16_t begin, uint16_t range); + +uint8_t is_zero(char *data, int len); + + +/*********************************************************** + Configuration data and handlers +************************************************************/ + + +IDM_T validate_name( char* name ); + +int32_t opt_update_description ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn );
Added: trunk/bmx/control.c =================================================================== --- trunk/bmx/control.c (rev 0) +++ trunk/bmx/control.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,3324 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdarg.h> +#include <syslog.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/un.h> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <unistd.h> +#include <fcntl.h> +#include <paths.h> + +#include "bmx.h" +#include "plugin.h" +#include "schedule.h" + + + +#define MAX_DBG_STR_SIZE 1500 + +static char run_dir[MAX_PATH_SIZE] = DEF_RUN_DIR; + +static int32_t debug_level = -1; +static int32_t dbg_mute_to; + +#define MIN_LOOP_INTERVAL 100 +#define MAX_LOOP_INTERVAL 10000 +#define DEF_LOOP_INTERVAL 1000 +static int32_t loop_interval = DEF_LOOP_INTERVAL; + +static int32_t loop_mode; + +#define MIN_PEDANT_CHK NO +#define MAX_PEDANT_CHK YES +#define DEF_PEDANT_CHK NO +static int32_t pedantic_check = DEF_PEDANT_CHK; + + +int unix_sock = 0; + +LIST_SIMPEL( ctrl_list, struct ctrl_node, list ); + +struct list_head dbgl_clients[DBGL_MAX+1]; +static struct dbg_histogram dbgl_history[2][DBG_HIST_SIZE]; + +static uint8_t debug_system_active = NO; + + +static char *init_string = NULL; + +static int32_t Testing = NO; +int32_t Load_config; + +char *prog_name; + +struct opt_type Patch_opt; + +LIST_SIMPEL( opt_list, struct opt_data, list ); // global opt_list + + +int32_t Client_mode = NO; //this one must be initialized manually! + +static void remove_dbgl_node( struct ctrl_node *cn ) { + + int8_t i; + struct dbgl_node *dn; + struct list_node *list_pos, *list_tmp, *list_prev; + + for ( i = DBGL_MIN; i <= DBGL_MAX; i++ ) { + + list_prev = (struct list_node *)&dbgl_clients[i]; + + list_for_each_safe( list_pos, list_tmp, (struct list_node *)&dbgl_clients[i] ) { + + dn = list_entry(list_pos, struct dbgl_node, list); + + if ( dn->cn == cn ) { + list_del_next(&dbgl_clients[i], list_prev); + debugFree( list_pos, -300049 ); + } else { + list_prev = &dn->list; + } + } + } + + cn->dbgl = -1; +} + + +static void add_dbgl_node( struct ctrl_node *cn, int dbgl ) { + + if ( !cn || dbgl < DBGL_MIN || dbgl > DBGL_MAX ) + return; + + struct dbgl_node *dn = debugMalloc( sizeof( struct dbgl_node ), -300009 ); + memset( dn, 0, sizeof( struct dbgl_node ) ); + + dn->cn = cn; + cn->dbgl = dbgl; + list_add_tail(&dbgl_clients[dbgl], &dn->list); + + if ( dbgl == DBGL_SYS || dbgl == DBGL_CHANGES ) { + dbg( DBGL_CHANGES, DBGT_INFO, "resetting muted dbg history" ); + memset( dbgl_history, 0, sizeof( dbgl_history ) ); + } + +} + + +static int daemonize() { + + int fd; + + switch( fork() ) { + + case -1: + return -1; + + case 0: + break; + + default: + exit(EXIT_SUCCESS); + + } + + if ( setsid() == -1 ) + return -1; + + /* Ensure we are no session leader */ + if ( fork() ) + exit(EXIT_SUCCESS); + + + errno=0; + if ( chdir( "/" ) < 0 ) { + dbg( DBGL_SYS, DBGT_ERR, "could not chdir to /: %s", strerror(errno) ); + } + + if ( ( fd = open( _PATH_DEVNULL, O_RDWR, 0) ) != -1 ) { + + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + + if ( fd > 2 ) + close(fd); + + } + + return 0; + +} + +static int update_pid_file( void ) { + + char tmp_path[MAX_PATH_SIZE+20] = ""; + int tmp_fd = 0; + + My_pid = getpid(); + + sprintf( tmp_path, "%s/pid", run_dir ); + + if ( (tmp_fd = open( tmp_path, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH )) < 0 ) { //check permissions of generated file + + dbgf( DBGL_SYS, DBGT_ERR, "could not open %s - %s", tmp_path, strerror(errno) ); + return FAILURE; + } + + dprintf( tmp_fd, "%d\n", My_pid ); + + close ( tmp_fd ); + return SUCCESS; +} + +static void activate_debug_system( void ) { + + if ( !debug_system_active ) { + + /* daemonize */ + if ( debug_level == -1 ) { + + if ( daemonize() < 0 ) { + dbg( DBGL_SYS, DBGT_ERR, "can't fork to background: %s", strerror(errno) ); + cleanup_all( -500093 ); + } + + // fork will result in a new pid + if ( update_pid_file() == FAILURE ) + cleanup_all( -500132 ); + + + } else { + + struct ctrl_node *cn = create_ctrl_node( STDOUT_FILENO, NULL, NO/*admin rights not necessary*/ ); + + add_dbgl_node( cn, debug_level ); + + } + + //dbg( DBGL_CHANGES, DBGT_INFO, "resetting muted dbg history" ); + memset( dbgl_history, 0, sizeof( dbgl_history ) ); + + debug_system_active = YES; + + dbgf_all( DBGT_INFO, "activated level %d", debug_level); + + dbg( DBGL_CHANGES, DBGT_INFO, "BMX (BatMan-eXp) %s-rv%d (compatibility version %d): %s", + SOURCE_VERSION, REVISION_VERSION, COMPAT_VERSION, init_string); + + } +} + +struct ctrl_node *create_ctrl_node( int fd, void (*cn_fd_handler) (struct ctrl_node *), uint8_t authorized ) { + + struct ctrl_node *cn = debugMalloc( sizeof(struct ctrl_node), -300010 ); + memset( cn, 0, sizeof(struct ctrl_node) ); + + list_add_tail(&ctrl_list, &cn->list); + + cn->fd = fd; + cn->cn_fd_handler = cn_fd_handler; + cn->dbgl = -1; + cn->authorized = authorized; + + return cn; +} + + +void close_ctrl_node( uint8_t cmd, struct ctrl_node *ctrl_node ) { + + struct list_node* list_pos, *list_prev, *list_tmp; + int trash; + + list_prev = (struct list_node *)&ctrl_list; + + list_for_each_safe( list_pos, list_tmp, &ctrl_list ) { + + struct ctrl_node *cn = list_entry(list_pos, struct ctrl_node, list); + + if ( ( cmd == CTRL_CLOSE_ERROR || cmd == CTRL_CLOSE_SUCCESS || cmd == CTRL_CLOSE_DELAY ) && cn == ctrl_node ) { + + if ( cn->fd > 0 && cn->fd != STDOUT_FILENO ) { + + cn->closing_stamp = MAX(bmx_time,1); + remove_dbgl_node( cn ); + + //leaving this after remove_dbgl_node() prevents debugging via broken -d4 pipe + dbgf_all( DBGT_INFO, "closed ctrl node fd %d with cmd %d", cn->fd, cmd ); + + + if ( cmd == CTRL_CLOSE_SUCCESS ) + trash=write( cn->fd, CONNECTION_END_STR, strlen(CONNECTION_END_STR) ); + + if ( cmd != CTRL_CLOSE_DELAY ) { + close( cn->fd ); + cn->fd = 0; + change_selects(); + } + + } + + return; + + } else if ( ( cmd == CTRL_CLOSE_STRAIGHT && cn == ctrl_node ) || + ( cmd == CTRL_PURGE_ALL ) || + ( cmd == CTRL_CLEANUP && cn->closing_stamp &&/* cn->fd <= 0 && */ + GREAT_U32( bmx_time, cn->closing_stamp + CTRL_CLOSING_TIMEOUT ) ) ) + { + + if ( cn->fd > 0 && cn->fd != STDOUT_FILENO ) { + remove_dbgl_node( cn ); + //leaving this after remove_dbgl_node() prevents debugging via broken -d4 pipe + dbgf_all( DBGT_INFO, "closed ctrl node fd %d", cn->fd ); + + close( cn->fd ); + cn->fd = 0; + change_selects(); + } + + list_del_next(&ctrl_list, list_prev); + debugFree( cn, -300050 ); + + } else { + + list_prev = (struct list_node *)&cn->list; + + } + } +} + + +void accept_ctrl_node( void ) { + + struct sockaddr addr; + socklen_t addr_size = sizeof(struct sockaddr); + int32_t unix_opts; + + int fd = accept( unix_sock, (struct sockaddr *)&addr, &addr_size); + + if ( fd < 0 ) { + dbg( DBGL_SYS, DBGT_ERR, "can't accept unix client: %s", strerror(errno) ); + return; + } + + /* make unix socket non blocking */ + unix_opts = fcntl( fd, F_GETFL, 0 ); + fcntl( fd, F_SETFL, unix_opts | O_NONBLOCK ); + + create_ctrl_node( fd, NULL, YES ); + + change_selects(); + + dbgf_all( DBGT_INFO, "got unix control connection" ); + +} + + + +void handle_ctrl_node( struct ctrl_node *cn ) { + char buff[MAX_UNIX_MSG_SIZE+1]; + + if ( cn->cn_fd_handler ) { + (cn->cn_fd_handler) (cn); + return; + } + + errno=0; + int input = read( cn->fd, buff, MAX_UNIX_MSG_SIZE ); + + buff[input]='\0'; + + if ( input > 0 && input < MAX_UNIX_MSG_SIZE ) { + + dbgf_all( DBGT_INFO, "rcvd ctrl stream via fd %d, %d bytes, auth %d: %s", + cn->fd, input, cn->authorized, buff ); + + if ( (apply_stream_opts( buff, OPT_CHECK, NO/*no cfg by default*/, cn ) == FAILURE) || + (apply_stream_opts( buff, OPT_APPLY, NO/*no cfg by default*/, cn ) == FAILURE) ) + { + + dbg( DBGL_SYS, DBGT_ERR, "invalid ctrl stream via fd %d, %d bytes, auth %d: %s", + cn->fd, input, cn->authorized, buff ); + + close_ctrl_node( CTRL_CLOSE_ERROR, cn ); + return; + } + + respect_opt_order( OPT_APPLY, 0, 99, NULL, NO/*load_cofig*/, OPT_POST, 0/*probably closed*/ ); + + cb_plugin_hooks( NULL, PLUGIN_CB_CONF ); + + } else { + + close_ctrl_node( CTRL_CLOSE_STRAIGHT, cn ); + + //leaving this after close_ctrl_node() -> remove_dbgl_node() prevents debugging via broken -d4 pipe + dbgf_all( DBGT_INFO, "closed fd %d, rcvd %d bytes, auth %d: %s", + cn->fd, input, cn->authorized, buff ); + + } + + return; +} + + +#ifndef TESTDEBUG + +// returns DBG_HIST_NEW, DBG_HIST_MUTING, or DBG_HIST_MUTED +static uint8_t check_dbg_history ( int8_t dbgl, char *s, uint16_t check_len ) { + + static int r=0; + int i, unused_i, h; + + check_len = MIN(check_len, DBG_HIST_TEXT_SIZE); + + if ( !strlen(s) || !dbg_mute_to || !check_len ) + return DBG_HIST_NEW; + + if ( dbgl == DBGL_SYS ) + h=0; + + else if ( dbgl == DBGL_CHANGES ) + h=1; + + else + return DBG_HIST_NEW; + + + unused_i = -1; + i = r = (r+1) % DBG_HIST_SIZE; + + do { + + if ( dbgl_history[h][i].check_len == check_len && + dbgl_history[h][i].expire == dbg_mute_to && + !memcmp( s, dbgl_history[h][i].text, MIN(check_len, strlen(s)) ) ) { + + if ( LESS_U32( bmx_time, dbgl_history[h][i].print_stamp + dbg_mute_to ) && + GRTEQ_U32( bmx_time, dbgl_history[h][i].print_stamp ) ) + { + + dbgl_history[h][i].catched++; + + if ( dbgl_history[h][i].catched == 2 ) + return DBG_HIST_MUTING; + + else + return DBG_HIST_MUTED; + + } + + dbgl_history[h][i].print_stamp = bmx_time; + dbgl_history[h][i].catched = 1; + return DBG_HIST_NEW; + + } + + if ( unused_i == -1 && + ( dbgl_history[h][i].catched == 0 || + !( LESS_U32( bmx_time, dbgl_history[h][i].print_stamp + dbg_mute_to ) && + GRTEQ_U32( bmx_time, dbgl_history[h][i].print_stamp ) ) ) ) + { + + unused_i = i; + } + + + i = ( (i+1) % DBG_HIST_SIZE ); + + } while ( i != r ); + + if ( unused_i == -1 ) + unused_i = r; + + dbgl_history[h][unused_i].expire = dbg_mute_to; + dbgl_history[h][unused_i].check_len = check_len; + dbgl_history[h][unused_i].print_stamp = bmx_time; + dbgl_history[h][unused_i].catched = 1; + memcpy( dbgl_history[h][unused_i].text, s, MIN( check_len, strlen(s) ) ); + + return DBG_HIST_NEW; + +} + + + +#define MAX_DBG_WRITES 4 +void dbg_printf( struct ctrl_node *cn, char *last, ... ) { + + if ( !cn || cn->fd <= 0 ) + return; + + static char s[ MAX_DBG_STR_SIZE + 1 ]; + ssize_t w, out=0; + int i = 1; + + va_list ap; + va_start( ap, last ); + vsnprintf( s, MAX_DBG_STR_SIZE, last, ap ); + va_end( ap ); + + // CONNECTION_END_CHR is reserved for signaling connection end + paranoia( -500146, (strchr( s, CONNECTION_END_CHR ) ) ); + + errno=0; + + while ( (w=write( cn->fd, s+out, strlen(s+out) )) != (ssize_t)strlen(s+out) ) { + + if ( errno == EPIPE || i >= MAX_DBG_WRITES || cn->dbgl==DBGL_ALL ) { + + if ( cn->dbgl != DBGL_ALL ) { + syslog( LOG_ERR, "failed %d times writing %d instead of %d/%d bytes (%s)! Giving up: %s\n", + i, (int)w, (int)strlen(s+out), (int)strlen(s), strerror(errno), s+out ); + } + + break; + } + i++; + + wait_sec_msec( 0, 10 ); + + if( w > 0 ) + out+=w; + + errno=0; + } +} + + +static void debug_output ( uint32_t check_len, struct ctrl_node *cn, int8_t dbgl, int8_t dbgt, char const *f, char *s ) { + + static uint16_t dbgl_all_msg_num = 0; + static char *dbgt2str[] = {"", "INFO ", "WARN ", "ERROR "}; + + struct list_node *list_pos; + int16_t dbgl_out[DBGL_MAX+1]; + int i = 0, j; + + uint8_t mute_dbgl_sys = DBG_HIST_NEW; + uint8_t mute_dbgl_changes = DBG_HIST_NEW; + + + if ( cn && cn->fd != STDOUT_FILENO ) + dbg_printf( cn, "%s%s%s%s\n", dbgt2str[dbgt], f?f:"", f?"(): ":"", s ); + + + if ( !debug_system_active ) { + + if ( dbgl == DBGL_SYS || debug_level == DBGL_ALL || debug_level == dbgl ) + printf( "[%d %8u] %s%s%s%s\n", My_pid, bmx_time, dbgt2str[dbgt], f?f:"", f?"(): ":"", s ); + + if ( dbgl == DBGL_SYS ) + syslog( LOG_ERR, "%s%s%s%s\n", dbgt2str[dbgt], f?f:"", f?"(): ":"", s ); + + return; + } + + + if ( dbgl == DBGL_ALL ) { + + if ( !LIST_EMPTY( &dbgl_clients[DBGL_ALL ] ) ) dbgl_out[i++] = DBGL_ALL; + + } else if ( dbgl == DBGL_CHANGES ) { + + if ( !LIST_EMPTY( &dbgl_clients[DBGL_CHANGES ] ) ) dbgl_out[i++] = DBGL_CHANGES; + if ( !LIST_EMPTY( &dbgl_clients[DBGL_ALL ] ) ) dbgl_out[i++] = DBGL_ALL; + + } else if ( dbgl == DBGL_TEST ) { + + if ( !LIST_EMPTY( &dbgl_clients[DBGL_TEST ] ) ) dbgl_out[i++] = DBGL_TEST; + if ( !LIST_EMPTY( &dbgl_clients[DBGL_ALL ] ) ) dbgl_out[i++] = DBGL_ALL; + + } else if ( dbgl == DBGL_PROFILE ) { + + if ( !LIST_EMPTY( &dbgl_clients[DBGL_PROFILE ] ) ) dbgl_out[i++] = DBGL_PROFILE; + + } else if ( dbgl == DBGL_SYS ) { + + if ( !LIST_EMPTY( &dbgl_clients[DBGL_SYS ] ) ) dbgl_out[i++] = DBGL_SYS; + if ( !LIST_EMPTY( &dbgl_clients[DBGL_CHANGES ] ) ) dbgl_out[i++] = DBGL_CHANGES; + if ( !LIST_EMPTY( &dbgl_clients[DBGL_ALL ] ) ) dbgl_out[i++] = DBGL_ALL; + + if ( check_len ) + mute_dbgl_sys = check_dbg_history( DBGL_SYS, s, check_len ); + + if ( mute_dbgl_sys != DBG_HIST_MUTED ) + syslog( LOG_ERR, "%s%s%s%s\n", dbgt2str[dbgt], f?f:"", f?"(): ":"", s ); + + if ( mute_dbgl_sys == DBG_HIST_MUTING ) + syslog( LOG_ERR, "%smuting further messages (with equal first %d bytes) for at most %d seconds\n", + dbgt2str[DBGT_WARN], check_len, dbg_mute_to/1000 ); + + } + + for ( j = 0; j < i; j++ ) { + + int level = dbgl_out[j]; + + if ( level == DBGL_ALL ) + dbgl_all_msg_num++; + + if ( level == DBGL_CHANGES && check_len && + (mute_dbgl_changes = check_dbg_history( DBGL_CHANGES, s, check_len )) == DBG_HIST_MUTED ) + continue; + + if ( level == DBGL_SYS && mute_dbgl_sys == DBG_HIST_MUTED ) + continue; + + + list_for_each ( list_pos, (struct list_node *)&(dbgl_clients[level]) ) { + + struct dbgl_node *dn = list_entry(list_pos, struct dbgl_node, list); + + if ( !dn->cn || dn->cn->fd <= 0 ) + continue; + + if ( level == DBGL_CHANGES || + level == DBGL_TEST || + level == DBGL_PROFILE || + level == DBGL_SYS ) + dbg_printf( dn->cn, "[%d %8u] ", My_pid, bmx_time ); + + + if ( level == DBGL_ALL ) + dbg_printf( dn->cn, "[%d %8u %5u] ", My_pid, bmx_time, dbgl_all_msg_num ); + + + dbg_printf( dn->cn, "%s%s%s%s\n", dbgt2str[dbgt], f?f:"", f?"(): ":"", s ); + + if ( ( level == DBGL_SYS && mute_dbgl_sys == DBG_HIST_MUTING ) || + ( level == DBGL_CHANGES && mute_dbgl_changes == DBG_HIST_MUTING ) ) + dbg_printf( dn->cn, + "[%d %8u] %smuting further messages (with equal first %d bytes) for at most %d seconds\n", + My_pid, bmx_time, dbgt2str[DBGT_WARN], check_len, dbg_mute_to/1000 ); + + } + } +} + + + +// this static array of char is used by all following dbg functions. +static char dbg_string_out[ MAX_DBG_STR_SIZE + 1 ]; + +void dbg ( int8_t dbgl, int8_t dbgt, char *last, ... ) { + va_list ap; + va_start( ap, last ); + vsnprintf( dbg_string_out, MAX_DBG_STR_SIZE, last, ap ); + va_end( ap ); + debug_output ( 0, 0, dbgl, dbgt, 0, dbg_string_out ); +} + +void _dbgf ( int8_t dbgl, int8_t dbgt, char const *f, char *last, ... ) { + va_list ap; + va_start( ap, last ); + vsnprintf( dbg_string_out, MAX_DBG_STR_SIZE, last, ap ); + va_end( ap ); + debug_output ( 0, 0, dbgl, dbgt, f, dbg_string_out ); +} + +void dbg_cn ( struct ctrl_node *cn, int8_t dbgl, int8_t dbgt, char *last, ... ) { + va_list ap; + va_start( ap, last ); + vsnprintf( dbg_string_out, MAX_DBG_STR_SIZE, last, ap ); + va_end( ap ); + debug_output ( 0, cn, dbgl, dbgt, 0, dbg_string_out ); +} + +void _dbgf_cn ( struct ctrl_node *cn, int8_t dbgl, int8_t dbgt, char const *f, char *last, ... ) { + va_list ap; + va_start( ap, last ); + vsnprintf( dbg_string_out, MAX_DBG_STR_SIZE, last, ap ); + va_end( ap ); + debug_output ( 0, cn, dbgl, dbgt, f, dbg_string_out ); +} + +void dbg_mute ( uint32_t check_len, int8_t dbgl, int8_t dbgt, char *last, ... ) { + va_list ap; + va_start( ap, last ); + vsnprintf( dbg_string_out, MAX_DBG_STR_SIZE, last, ap ); + va_end( ap ); + debug_output ( check_len, 0, dbgl, dbgt, 0, dbg_string_out ); +} + +void _dbgf_all ( int8_t dbgt, char const *f, char *last, ... ) { + va_list ap; + va_start( ap, last ); + vsnprintf( dbg_string_out, MAX_DBG_STR_SIZE, last, ap ); + va_end( ap ); + debug_output ( 0, 0, DBGL_ALL, dbgt, f, dbg_string_out ); +} + +uint8_t __dbgf_all( void ) { + + if ( debug_level != DBGL_ALL && LIST_EMPTY( &dbgl_clients[DBGL_ALL] ) ) + return NO; + + return YES; +} +#endif + + + + + + + +int (*load_config_cb) ( uint8_t test, struct opt_type *opt, struct ctrl_node *cn ) = NULL; + +int (*save_config_cb) ( uint8_t del, struct opt_type *opt, char *parent, char *val, struct ctrl_node *cn ) = NULL; + +int (*derive_config) ( char *reference, char *derivation, struct ctrl_node *cn ) = NULL; + + +void get_init_string( int g_argc, char **g_argv ) { + + uint32_t size = 1, dbg_init_out = 0; + int i; + char *dbg_init_str; + + for (i=0; i < g_argc; i++) + size+=( 1 + strlen( g_argv[i]) ); + + dbg_init_str = debugMalloc( size, -300012 ); + + for (i=0; i < g_argc; i++) + dbg_init_out = dbg_init_out + sprintf( (dbg_init_str + dbg_init_out), "%s ", g_argv[i] ); + + init_string = dbg_init_str; +} + + +static void free_init_string ( void ) { + + if ( init_string ) + debugFree( init_string, -300052 ); + + init_string = NULL; +} + +#ifndef NODEPRECATED +static int32_t opt_deprecated ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + char c = opt->short_name; + + if ( cmd == OPT_ADJUST ) + dbg( DBGL_SYS, DBGT_WARN, "option --%s%s%c%sis DEPRECATED and ignored!", + opt->long_name, c?", -":"", c?c:' ', c?" ":"" ); + + return SUCCESS; +} +#endif + + + + + +int32_t get_tracked_network( struct opt_type *opt, struct opt_parent *patch, char *out, uint32_t *ip, int32_t *mask, struct ctrl_node *cn ) { + + struct opt_child *nc, *mc; + struct opt_parent *p = get_opt_parent_val( opt, patch->p_val ); + + if ( !p || !(nc = get_opt_child(get_option(opt,0,ARG_NETW), p) ) || !nc->c_val ) + return FAILURE; + + if ( !p || !(mc = get_opt_child(get_option(opt,0,ARG_MASK), p) ) || !mc->c_val ) + return FAILURE; + + sprintf( out, "%s/%s", nc->c_val, mc->c_val ); + + if ( str2netw( out, ip, '/', cn, mask, 32 ) == FAILURE ) + return FAILURE; + + return SUCCESS; +} + + +int32_t adj_patched_network( struct opt_type *opt, struct opt_parent *patch, char *out, uint32_t *ip, int32_t *mask, struct ctrl_node *cn ) { + + struct opt_child *nc, *mc; + struct opt_parent *p; + + if ( strpbrk( patch->p_val, "*'"#\/~?^°,;|<>()[]{}$%&=`´" ) ) { + + dbg_cn( cn, DBGL_SYS, DBGT_ERR, + "%s %s with /%s and /%s MUST NOT be named with special characters or a leading number", + opt->long_name, patch->p_val, ARG_MASK, ARG_NETW ); + return FAILURE; + } + + // obtain netmask: + + p = get_opt_parent_val( opt, patch->p_val ); + + if ( (mc = get_opt_child( get_option(opt,0,ARG_MASK), patch ) ) && mc->c_val ) { + + // obtain the just-given netmask: + + if ( str2netw( mc->c_val, ip, '/', cn, 0,0 ) == SUCCESS ) { + + // the 255.255.255.0 notation: + + if ( *ip == htonl( 0xFFFFFFFF<<( 32 - (*mask=bits_count( *ip )) ) ) ) { + sprintf( out, "%d", *mask ); + set_opt_child_val( mc, out ); + } else { + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "invalid %s %s /%s %s", + opt->long_name, patch->p_val, ARG_MASK, mc->c_val ); + return FAILURE; + } + + } else if ( (*mask=strtol(mc->c_val, NULL, 10)) ) { + + // the prefix_len /24 notation: + + if ( *mask < MIN_MASK || *mask > MAX_MASK ) { + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "invalid prefix-length %s %s /%s %s", + opt->long_name, patch->p_val, ARG_MASK, mc->c_val ); + return FAILURE; + } + + } else { + + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "missing prefix-length %s %s /%s %s", + opt->long_name, patch->p_val, ARG_MASK, mc->c_val ); + return FAILURE; + } + + } else if ( p && (mc = get_opt_child(get_option(opt,0,ARG_MASK), p) ) && mc->c_val ) { + + // obtain the already-configured netmask: + *mask = strtol( mc->c_val , NULL , 10 ); + + } else { + + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "missing %s %s /%s", + opt->long_name, patch->p_val, ARG_MASK ); + return FAILURE; + } + + + // obtain the network: + + if ((nc = get_opt_child(get_option(opt, 0, ARG_NETW), patch)) && nc->c_val) { + + // obtain the just-given network: + + sprintf( out, "%s/%d", nc->c_val, *mask ); + if ( str2netw( out, ip, '/', cn, mask, 32 ) == FAILURE ) { + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "invalid patch %s %s /%s %s", + opt->long_name, patch->p_val, ARG_NETW, mc->c_val ); + return FAILURE; + } + + set_opt_child_val( nc, ipStr( validate_net_mask( *ip, *mask, 0 ) ) ); + + } else if ( p && (nc=get_opt_child( get_option(opt,0,ARG_NETW), p )) && nc->c_val ) { + + // obtain the already-configured network: + + sprintf( out, "%s/%d", nc->c_val, *mask ); + if ( str2netw( out, ip, '/', cn, mask, 32 ) == FAILURE ) { + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "invalid trac %s %s /%s %s", + opt->long_name, patch->p_val, ARG_NETW, mc->c_val ); + return FAILURE; + } + + } else { + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "missing %s %s /%s", + opt->long_name, patch->p_val, ARG_NETW ); + + return FAILURE; + } + + sprintf( out, "%s/%d", nc->c_val, *mask ); + return SUCCESS; +} + +static char* nextword( char *s ) { + + uint32_t i = 0; + uint8_t found_gap = NO; + + if ( !s ) + return NULL; + + for( i=0; i<strlen(s); i++ ) { + + if ( s[i] == '\0' || s[i] == '\n' ) + return NULL; + + if ( !found_gap && ( s[i]==' ' || s[i]=='\t' )) + found_gap = YES; + + if ( found_gap && ( s[i]!=' ' && s[i]!='\t' ) ) + return &(s[i]); + + } + + return NULL; + +} + +static char *debugWordDup( char* word, int32_t tag ) { + + if ( !word ) + return NULL; + + char *ret = debugMalloc( wordlen(word)+1, tag ); + snprintf( ret, wordlen(word)+1, "%s", word ); + return ret; +} + + + + +static void strchange( char *s, char i, char o ) { + + char *p; + while ( s && (p=strchr( s, i )) ) + p[0] = o; + +} + + +static int32_t end_of_cmd_stream( struct opt_type *opt, char *s ) { + + char test[MAX_ARG_SIZE]=""; + snprintf( test, wordlen(s)+1, "%s", s ); + strchange( test, '-', '_' ); + + if ( opt->opt_t != A_PS0 ) + s = nextword(s); + else if ( wordlen(s)>1 && !strncasecmp( test, opt->long_name, wordlen(opt->long_name) ) ) + s = nextword(s); + else if ( wordlen(s)>1 ) + s = s+1; + else + s = nextword(s); + + if ( s && ( s[0] != EOS_DELIMITER || wordlen(s)>1 ) ) + return NO; + + return YES; + +} + + +static int8_t is_valid_opt_ival( struct opt_type *opt, char *s, struct ctrl_node *cn ) { + + if ( opt->imin == opt->imax ) + return SUCCESS; + + char* invalids = NULL; + + errno=0; + int ival = strtol(s, &invalids , 10); + + if ( wordlen(s) < 1 || + ival < opt->imin || ival > opt->imax || + invalids!=(s+wordlen(s)) || + errno == ERANGE || errno == EINVAL ) + { + + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "--%s value %d is invalid! Must be %d <= <value> <= %d !", + opt->long_name, ival, opt->imin, opt->imax ); + + return FAILURE; + } + + return SUCCESS; +} + + +/* + * call given function for each applied option + * thus: if several hna are active func() is called once for each + */ +int8_t func_for_each_opt( struct ctrl_node *cn, void *data, char* func_name, + int8_t (*func) ( struct ctrl_node *cn, void *data, struct opt_type *opt, struct opt_parent *p, struct opt_child *c ) ) +{ + + struct list_node *list_pos; + + list_for_each( list_pos, &opt_list ) { + + struct opt_type *opt = (struct opt_type *)list_entry( list_pos, struct opt_data, list ); + + if ( /* !opt->help || we are also interested in uncommented configurations*/ !opt->long_name ) + continue; + + struct list_node *p_pos; + + list_for_each( p_pos, &(opt->d.parents_instance_list) ) { + + struct opt_parent *p = (struct opt_parent*)list_entry( p_pos, struct opt_parent, list ); + + if ( (*func)( cn, data, opt, p, NULL ) == FAILURE ) { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, + "func()=%s with %s %s failed", + func_name, opt->long_name, p->p_val ); + + return FAILURE; + } + + struct list_node *c_pos; + + list_for_each( c_pos, &p->childs_instance_list ) { + + struct opt_child *c = (struct opt_child*)list_entry( c_pos, struct opt_child, list ); + + if ( (*func)( cn, data, opt, p, c ) == FAILURE ) { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, + "func()=%s with %s %s %s %s failed", + func_name, opt->long_name, p->p_val, c->c_opt->long_name, c->c_val ); + + return FAILURE; + } + } + } + } + + return SUCCESS; +} + + +static void show_opts_help( uint8_t all_opts, uint8_t verbose, struct ctrl_node *cn ) { + + if ( !cn ) + return; + + dbg_printf(cn, "\n"); + dbg_printf(cn, "Usage: %s [LONGOPT [%c]VAL] | [-SHORTOPT[SHORTOPT...] [%c]VAL] ...\n", + prog_name, ARG_RESET_CHAR , ARG_RESET_CHAR); + dbg_printf(cn, " e.g. %s -cid8\n", prog_name); + dbg_printf(cn, " e.g. %s -c a 192.200.200.1/24\n", prog_name); + dbg_printf(cn, " e.g. %s -c %s my-vpn /n=192.168.115.0 /m=24\n", prog_name, ARG_THROW); + dbg_printf(cn, " e.g. %s -c %s -my-vpn \n", prog_name, ARG_THROW); + dbg_printf(cn, "\n"); + //dbg_printf(cn, "\nValid options are:\n" ); + + struct list_node *list_pos; + + list_for_each( list_pos, &opt_list ) { + + struct list_node *pos; + struct opt_type *opt = (struct opt_type *)list_entry( list_pos, struct opt_data, list ); + char sn[5], st[3 * MAX_ARG_SIZE], defaults[100]; + + if ( !( all_opts || opt->short_name ) ) + continue; + +#ifndef NODEPRECATED + if ( opt->call_custom_option == opt_deprecated ) + continue; +#endif + + if ( opt->long_name && opt->help && !opt->parent_name ) { + + + if ( opt->short_name ) + snprintf( sn,5, ", -%c", opt->short_name ); + else + *sn = '\0'; + + sprintf( st, "--%s%s %s ", opt->long_name, sn, opt->syntax ? opt->syntax: "" ); + + if ( opt->opt_t != A_PS0 && opt->imin != opt->imax ) + sprintf( defaults, "def: %-6d range: [ %d %s %d ]", + opt->idef, opt->imin, opt->imin+1 == opt->imax ? ",": "...", opt->imax ); + else + defaults[0]='\0'; + + dbg_printf( cn, "\n%-40s %s\n", st, defaults ); + + if ( verbose ) + dbg_printf( cn, " %s\n", opt->help ); + + + + } else if ( !opt->long_name && opt->help ) { + + dbg_printf( cn, "\n%s \n",opt->help ); + + } + + + list_for_each( pos, &opt->d.childs_type_list ) { + + struct opt_type *c_opt = (struct opt_type *)list_entry( pos, struct opt_data, list ); + + if ( !c_opt->parent_name || !c_opt->help ) + continue; + +#ifndef NODEPRECATED + if ( c_opt->call_custom_option == opt_deprecated ) + continue; +#endif + + if ( c_opt->short_name ) + snprintf( sn,5, ", /%c", c_opt->short_name ); + else + *sn = '\0'; + + sprintf( st, " /%s%s %s ", c_opt->long_name, sn, c_opt->syntax ? c_opt->syntax: "" ); + + if ( c_opt->opt_t != A_PS0 && c_opt->imin != c_opt->imax ) + sprintf( defaults, "def: %-6d range: [ %d %s %d ]", + c_opt->idef, c_opt->imin, c_opt->imin+1 == c_opt->imax ? ",": "...", c_opt->imax ); + else + defaults[0]='\0'; + + dbg_printf( cn, "%-40s %s\n", st, defaults ); + + if ( verbose ) + dbg_printf( cn, " %s\n", c_opt->help ); + + } + } + + dbg_printf( cn, "\n"); + + if ( all_opts ) { + dbg_printf( cn, "Environment variables (e.g. sudo %s=/usr/src/bmx/lib %s -d3 eth0:bmx ):\n", + BMX_ENV_LIB_PATH, prog_name ); + + dbg_printf(cn, "\t%s\n", BMX_ENV_LIB_PATH ); + dbg_printf(cn, "\t%s\n", BMX_ENV_DEBUG ); + } + + dbg_printf( cn, "\n"); + +} + +void register_option( struct opt_type *opt ) { + + dbgf_all( DBGT_INFO, "%s", (opt&&opt->long_name) ? opt->long_name : "" ); + + struct opt_type *tmp_opt = NULL; + struct list_node *tmp_pos; + + // these are the valid combinations: + if ( !( + //ival is validated and if valid assigned by call_option() + ( (opt->ival) && (opt->call_custom_option) && (opt->long_name) ) || + //ival is validated and if valid assigned + ( (opt->ival) && !(opt->call_custom_option) && (opt->long_name) ) || + //call_option() is called + ( !(opt->ival) && (opt->call_custom_option) && (opt->long_name) ) || + // + ( !(opt->ival) && !(opt->call_custom_option) && !(opt->long_name) && opt->help ) + ) ) + goto failure; + + + // arg_t A_PS0 with no function can only be YES/NO: + paranoia( -500111, ( opt->opt_t == A_PS0 && opt->ival && ( opt->imin != NO || opt->imax != YES || opt->idef != NO ) ) ); + + // arg_t A_PS0 can not be stored + paranoia( -500112, ( opt->opt_t == A_PS0 && opt->cfg_t != A_ARG ) ); + + paranoia( -500113, ( opt->order < 0 || opt->order > 99 ) ); + + paranoia( -500114, ( (opt->parent_name && strchr( opt->parent_name, '-')) || (opt->long_name && strchr( opt->long_name, '-')) ) ); + + + memset( &(opt->d), 0, sizeof( struct opt_data ) ); + + if ( opt->ival ) + *opt->ival = opt->idef; + + if ( opt->parent_name ) { + + list_for_each( tmp_pos, &opt_list ) { + tmp_opt = (struct opt_type *)list_entry( tmp_pos, struct opt_data, list ); + + if ( tmp_opt->long_name == opt->parent_name ) + break; + else + tmp_opt = NULL; + } + + if ( opt->opt_t != A_CS1 || !tmp_opt || tmp_opt->opt_t != A_PMN ) + goto failure; + + opt->d.parent_opt = tmp_opt; + + list_add_tail( &tmp_opt->d.childs_type_list, &opt->d.list ); + + } else { + + LIST_INIT_HEAD( opt->d.childs_type_list, struct opt_data, list); + LIST_INIT_HEAD( opt->d.parents_instance_list, struct opt_parent, list); + + if ( opt->order ) { + + struct list_node *prev_pos = (struct list_node *)&opt_list; + + list_for_each( tmp_pos, &opt_list ) { + + tmp_opt = (struct opt_type *)list_entry( tmp_pos, struct opt_data, list ); + + if ( tmp_opt->order > opt->order ) { + list_add_after(&opt_list, prev_pos, &opt->d.list); + break; + } else { + prev_pos = &tmp_opt->d.list; + tmp_opt = NULL; + } + } + } + + if ( !tmp_opt) + list_add_tail(&opt_list, &opt->d.list); + + } + + if ( opt->call_custom_option && ((opt->call_custom_option)( OPT_REGISTER, 0, opt, 0,0 )) == FAILURE ) { + + dbgf( DBGL_SYS, DBGT_ERR, "%s failed!", opt->long_name ); + goto failure; + } + + + return; + +failure: + + dbgf( DBGL_SYS, DBGT_ERR, "invalid data, tmp_opt: %c %s - option %c %s", + (tmp_opt && tmp_opt->short_name)?tmp_opt->short_name:'?', + (tmp_opt && tmp_opt->long_name)?tmp_opt->long_name:"??", + (opt && opt->short_name)?opt->short_name:'?', (opt && opt->long_name) ?opt->long_name:"??" ); + + paranoia( -500091, YES ); +} + + +static void remove_option( struct opt_type *opt ) { + + struct list_node *tmp_pos, *list_pos, *prev_pos; + + del_opt_parent( opt, NULL ); + + prev_pos = (struct list_node *)&opt_list; + + list_for_each_safe( list_pos, tmp_pos, &opt_list ) { + + struct opt_type *tmp_opt = (struct opt_type *)list_entry( list_pos, struct opt_data, list ); + + if ( opt == tmp_opt ) { + + if ( !opt->parent_name && opt->call_custom_option && + ((opt->call_custom_option)( OPT_UNREGISTER, 0, opt, 0,0 )) == FAILURE ) + { + dbgf( DBGL_SYS, DBGT_ERR, "%s failed!", opt->long_name ); + } + + list_del_next(&opt_list, prev_pos); + return; + + } else { + + prev_pos = &tmp_opt->d.list; + + } + + } + + dbgf( DBGL_SYS, DBGT_ERR, "%s no matching opt found", opt->long_name ); +} + + +void register_options_array ( struct opt_type *fixed_options, int size ) { + + int i = 0; + int i_max = size / sizeof( struct opt_type ); + + paranoia( -500149, ((size % sizeof( struct opt_type )) != 0 ) ); + + while ( i<i_max && (fixed_options[i].long_name || fixed_options[i].help) ) + register_option( &(fixed_options[i++]) ); + +} + +/* +void remove_options_array ( struct opt_type *fixed_options ) { + + int i = 0; + + while ( fixed_options[i].long_name != NULL || fixed_options[i].help != NULL ) + remove_option( &(fixed_options[i++]) ); + +} +*/ + + +struct opt_type *get_option( struct opt_type *parent_opt, uint8_t short_opt, char *sin ) { + + struct list_node *list_pos; + int32_t len = 0; + struct list_head *list; + struct opt_type *opt = NULL; + char *equalp = NULL; + char s[MAX_ARG_SIZE]=""; + + if ( parent_opt && short_opt ) + goto get_option_failure; + + if ( !sin || wordlen(sin)+1 >= MAX_ARG_SIZE ) + goto get_option_failure; + + snprintf( s, wordlen(sin)+1, "%s", sin ); + strchange( s, '-', '_' ); + + if ( short_opt ) + len = 1; + else if ( (equalp = index( s, '=' )) && equalp < s+wordlen(s) ) + len = equalp - s; + else + len = wordlen(s); + + + if ( parent_opt == NULL ) + list = &opt_list; + else + list = &parent_opt->d.childs_type_list; + + dbgf_all( DBGT_INFO, "searching %s", s ); + + list_for_each( list_pos, list ) { + + opt = (struct opt_type *)list_entry( list_pos, struct opt_data, list ); + + if ( !opt->long_name ) + continue; + + else if ( !short_opt && len == (int)strlen( opt->long_name ) && !strncasecmp( s, opt->long_name, len ) ) + break; + + else if ( !short_opt && len == 1 && s[0] == opt->short_name && on_the_fly && opt->dyn_t != A_INI ) + break; + + else if ( !short_opt && len == 1 && s[0] == opt->short_name && !on_the_fly && opt->dyn_t != A_DYN ) + break; + + else if ( short_opt && s[0] == opt->short_name && on_the_fly && opt->dyn_t != A_INI ) + break; + + else if ( short_opt && s[0] == opt->short_name && !on_the_fly && opt->dyn_t != A_DYN ) + break; + + + opt = NULL; + } + + if ( opt && opt->long_name ) { + dbgf_all( DBGT_INFO, + "Success! short_opt %d, opt: %s %c, type %d, dyn %d, ival %d, imin %d, imax %d, idef %d", + short_opt, opt->long_name ? opt->long_name : "-", opt->short_name ? opt->short_name : '-', + opt->opt_t, opt->dyn_t, + opt->ival ? *opt->ival : 0, opt->imin, opt->imax, opt->idef ); + + return opt; + } + + +get_option_failure: + + dbgf_all( DBGT_WARN, "Failed! called with parent %s, opt %c %s, len %d", + parent_opt?"YES":"NO", (short_opt ? s[0]: '-'), (!short_opt ? s : "-"), len ); + + return NULL; + +} + + + +struct opt_child *get_opt_child ( struct opt_type *opt, struct opt_parent *p ) { + + struct list_node *pos; + + paranoia( -500026, ( opt->opt_t != A_CS1 ) ); + + paranoia( -500119, ( !p ) ); + + + list_for_each( pos, &(p->childs_instance_list) ) { + + struct opt_child *c = list_entry( pos, struct opt_child, list ); + + if ( c->c_opt == opt ) + return c; + + } + + return NULL; +} + +void set_opt_child_val( struct opt_child *c, char *val ) { + + if ( val && c->c_val && wordsEqual( c->c_val, val ) ) + return; + + if ( c->c_val ) + debugFree( c->c_val, -300053 ); + + c->c_val = NULL; + + if ( val) + c->c_val = debugWordDup(val, -300013); +} + +static void set_opt_child_ref( struct opt_child *c, char *ref ) { + + if ( ref && c->c_ref && wordsEqual( c->c_ref, ref ) ) + return; + + if ( c->c_ref ) + debugFree( c->c_ref, -300054 ); + + c->c_ref = NULL; + + if ( ref ) + c->c_ref = debugWordDup( ref, -300014 ); +} + + +static void del_opt_child_save( struct list_node *prev, struct list_node *pos, struct opt_parent *p ) { + + struct opt_child *c = list_entry( pos, struct opt_child, list ); + + list_del_next(&p->childs_instance_list, prev); + + set_opt_child_val( c, NULL ); + set_opt_child_ref( c, NULL ); + + debugFree( pos, -300055 ); + return; +} + +static void del_opt_child( struct opt_parent *p, struct opt_type *opt ) { + + struct list_node *pos, *tmp, *prev; + + prev = (struct list_node*)&p->childs_instance_list; + + list_for_each_safe( pos, tmp, &(p->childs_instance_list) ) { + + struct opt_child *c = list_entry( pos, struct opt_child, list ); + + if ( !opt || c->c_opt == opt ) + del_opt_child_save( prev, pos, p ); + else + prev = pos; + } +} + +static struct opt_child *add_opt_child( struct opt_type *opt, struct opt_parent *p ) { + + struct opt_child *c = debugMalloc( sizeof( struct opt_child ), -300017 ); + memset( c, 0, sizeof(struct opt_child) ); + + c->c_opt = opt; + c->parent_instance = p; + list_add_tail(&p->childs_instance_list, &c->list); + + return c; +} + + + + +void set_opt_parent_val( struct opt_parent *p, char *val ) { + + if ( val && p->p_val && wordsEqual( p->p_val, val ) ) + return; + + if ( p->p_val ) + debugFree( p->p_val, -300056 ); + + p->p_val = NULL; + + if ( val) + p->p_val = debugWordDup( val, -300015 ); +} + +void set_opt_parent_ref( struct opt_parent *p, char *ref ) { + + if ( ref && p->p_ref && wordsEqual( p->p_ref, ref ) ) + return; + + if ( p->p_ref ) + debugFree( p->p_ref, -300057 ); + + p->p_ref = NULL; + + if ( ref ) + p->p_ref = debugWordDup( ref, -300016 ); +} + + +struct opt_parent *add_opt_parent( struct opt_type *opt ) { + + struct opt_parent *p = debugMalloc( sizeof( struct opt_parent ), -300018 ); + memset( p, 0, sizeof(struct opt_parent) ); + + LIST_INIT_HEAD( p->childs_instance_list, struct opt_child, list ); + + list_add_tail(&opt->d.parents_instance_list, &p->list); + + return p; +} + +static void del_opt_parent_save( struct opt_type *opt, struct list_node *prev, struct list_node *pos ) { + + struct opt_parent *p = list_entry( pos, struct opt_parent, list ); + + list_del_next(&opt->d.parents_instance_list, prev); + + del_opt_child( p, NULL ); + + set_opt_parent_val( p, NULL ); + set_opt_parent_ref( p, NULL ); + + debugFree( p, -300058 ); +} + +void del_opt_parent( struct opt_type *opt, struct opt_parent *parent ) { + + struct list_node *pos, *tmp, *prev = (struct list_node*)&(opt->d.parents_instance_list); + + list_for_each_safe( pos, tmp, &(opt->d.parents_instance_list) ) { + + struct opt_parent *p = list_entry( pos, struct opt_parent, list ); + + if ( !parent || p == parent ) + del_opt_parent_save( opt, prev, pos ); + else + prev = pos; + } +} + + +struct opt_parent *get_opt_parent_val ( struct opt_type *opt, char *val ) { + + struct opt_parent *p = NULL; + struct list_node *pos; + + paranoia( -500118, ( opt->cfg_t == A_ARG ) ); + +// paranoia( -500117, ( (opt->opt_t == A_PS0 || opt->opt_t == A_PS1) && opt->d.found_parents > 1 ) ); + paranoia( -500117, ( (opt->opt_t == A_PS0 || opt->opt_t == A_PS1) && opt->d.parents_instance_list.items > 1 ) ); + + list_for_each( pos, &(opt->d.parents_instance_list) ) { + + p = list_entry( pos, struct opt_parent, list ); + + if ( !val || wordsEqual( p->p_val, val ) ) + return p; + + } + + return NULL; +} + +struct opt_parent *get_opt_parent_ref ( struct opt_type *opt, char *ref ) { + + struct opt_parent *p = NULL; + struct list_node *pos; + + paranoia( -500124, ( opt->cfg_t == A_ARG ) ); + +// paranoia( -500116, ( (opt->opt_t == A_PS0 || opt->opt_t == A_PS1) && opt->d.found_parents > 1 ) ); + paranoia( -500116, ( (opt->opt_t == A_PS0 || opt->opt_t == A_PS1) && opt->d.parents_instance_list.items > 1 ) ); + + list_for_each( pos, &(opt->d.parents_instance_list) ) { + + p = list_entry( pos, struct opt_parent, list ); + + if ( ref && wordsEqual( p->p_ref, ref ) ) + return p; + + } + + return NULL; +} + +static struct opt_parent *dup_opt_parent ( struct opt_type *opt, struct opt_parent *p ) { + + struct opt_parent *dup_p = add_opt_parent( opt ); + set_opt_parent_val ( dup_p, p->p_val ); + set_opt_parent_ref ( dup_p, p->p_ref ); + + dup_p->p_diff = p->p_diff; + + struct list_node *pos; + list_for_each( pos, &(p->childs_instance_list) ) { + + struct opt_child *c = list_entry( pos, struct opt_child, list ); + + struct opt_child *dup_c = add_opt_child( c->c_opt, dup_p ); + set_opt_child_val( dup_c, c->c_val ); + set_opt_child_ref( dup_c, c->c_ref ); + } + + return dup_p; +} + + + + +char *opt_cmd2str[] = { + "OPT_REGISTER", + "OPT_PATCH", + "OPT_ADJUST", + "OPT_CHECK", + "OPT_APPLY", + "OPT_SET_POST", + "OPT_POST", + "OPT_UNREGISTER" +}; + + +int32_t check_apply_parent_option( uint8_t del, uint8_t cmd, uint8_t _save, struct opt_type *opt, char *in, struct ctrl_node *cn ) { + + int32_t ret; + + paranoia( -500102, ( (cmd != OPT_CHECK && cmd != OPT_APPLY) || opt->parent_name ) ); + + struct opt_parent *p = add_opt_parent( &Patch_opt ); + + if ((ret = call_option(del, OPT_PATCH, _save, opt, p, in, cn)) == FAILURE || + call_option(del, OPT_ADJUST, _save, opt, p, in, cn) == FAILURE || + call_option(del, cmd, _save, opt, p, in, cn) == FAILURE) + ret = FAILURE; + + del_opt_parent( &Patch_opt, p ); + + dbgf_all( DBGT_INFO, "del:%d, %s, save:%d, %s %s returns: %d", + del, opt_cmd2str[cmd], _save, opt->long_name, in, ret ); + + return ret; +} + + + +static int32_t call_opt_patch( uint8_t ad, struct opt_type *opt, struct opt_parent *patch, char *strm, struct ctrl_node *cn ) { + + dbgf_all( DBGT_INFO, "ad:%d opt:%s val:%s strm:%s", + ad, opt->long_name, patch->p_val, strm ); + + if ( opt->opt_t == A_PS0 ) { + + patch->p_diff = ((ad==ADD) ? ADD : DEL); + + } else if ( opt->opt_t == A_PS1 || opt->opt_t == A_PMN || opt->opt_t == A_CS1 ) { + + char *ref = NULL; + char tmp[MAX_ARG_SIZE]; + + // assign one or more values + if ( ad == ADD || opt->opt_t == A_PMN ) { + + if ( !strm || !wordlen(strm) || strm[0] == EOS_DELIMITER ) + return FAILURE; + + if ( strm && wordlen(strm) > strlen( REFERENCE_KEY_WORD ) && + !strncmp( strm, REFERENCE_KEY_WORD, strlen( REFERENCE_KEY_WORD ) ) ) + { + ref = strm; + + if ( ad == ADD ) { + + if ( !derive_config || derive_config( ref, tmp, cn ) == FAILURE || !wordlen(strm) ) { + dbg_cn( cn, DBGL_SYS, DBGT_ERR, + "%s. Could not derive reference %s", + derive_config ? "invalid config" : "undefined callback", strm ); + return FAILURE; + } + + strm = tmp; + + } else if ( ad == DEL ) { + + struct opt_parent *p_track = get_opt_parent_ref( opt, strm ); + + if ( !p_track || !p_track->p_val ) { + dbg_cn( cn, DBGL_SYS, DBGT_ERR, + "Could not derive reference %s from tracked options", strm ); + return FAILURE; + } + + strm = p_track->p_val; + } + } + + if ( is_valid_opt_ival( opt, strm, cn ) == FAILURE ) + return FAILURE; + + } + + if ( opt->opt_t == A_PS1 || opt->opt_t == A_PMN ) { + + set_opt_parent_val( patch, strm ); + set_opt_parent_ref( patch, ref ); + + patch->p_diff = ((ad==ADD) ? ADD : DEL); + + } else if ( opt->opt_t == A_CS1 ) { + + struct opt_child *c = add_opt_child( opt, patch ); + + if ( ad == ADD ) + set_opt_child_val( c, strm ); + + set_opt_child_ref( c, ref ); + } + } + + return SUCCESS; +} + + +static int32_t cleanup_patch( struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + uint8_t del = patch->p_diff; + char *val = patch->p_val; + + dbgf_all( DBGT_INFO, "del %d opt %s val %s", del, opt->long_name, val ); + + if ( opt->cfg_t == A_ARG ) + return SUCCESS; + + if ( opt->opt_t == A_PS0 ) { + +// if ( (del && !opt->d.found_parents) || (!del && opt->d.found_parents) ) + if ((del && !opt->d.parents_instance_list.items) || (!del && opt->d.parents_instance_list.items)) + patch->p_diff = NOP; + + + } else if ( opt->opt_t == A_PS1 ) { + +// if ( (del && !opt->d.found_parents) || (!del && get_opt_parent_val( opt, val )) ) + if ((del && !opt->d.parents_instance_list.items) || (!del && get_opt_parent_val(opt, val))) + patch->p_diff = NOP; + + + } else if ( opt->opt_t == A_PMN ) { + + struct opt_parent *p_track = NULL; + struct list_node *c_pos, *c_tmp; + struct list_node *c_prev = (struct list_node*)&patch->childs_instance_list; + + list_for_each_safe( c_pos, c_tmp, &patch->childs_instance_list ) { + + struct opt_child *c = list_entry( c_pos, struct opt_child, list ); + struct opt_child *c_track = NULL; + uint8_t c_del = c->c_val ? ADD : DEL; + + p_track = NULL; + + dbgf_all( DBGT_INFO, "p_val:%s", patch->p_val ); + + if ( (p_track = get_opt_parent_val( opt, val )) ) + c_track = get_opt_child( c->c_opt, p_track ); + + if ( ( c_del && !c_track ) || + ( !c_del && c_track && wordsEqual( c_track->c_val, c->c_val ) ) ) + { + del_opt_child_save( c_prev, c_pos, patch ); + } else { + c_prev = c_pos; + } + } + + + p_track = get_opt_parent_val( opt, val ); + + if ( ( del && !p_track ) || ( !del && p_track ) ) + patch->p_diff = NOP; + + } else { + return FAILURE; + } + + return SUCCESS; +} + + + +static int32_t _opt_connect ( uint8_t cmd, struct opt_type *opt, struct ctrl_node *cn, char *curr_strm_pos ) { + + char tmp_path[MAX_PATH_SIZE+20] = ""; + char unix_buff[MAX_UNIX_MSG_SIZE+1] = ""; + int trash; + + dbgf_all( DBGT_INFO, "cmd %s, opt_name %s, stream %s", + opt_cmd2str[cmd], opt->long_name, curr_strm_pos ); + + if ( cmd == OPT_CHECK || cmd == OPT_APPLY ) { + + if ( !curr_strm_pos ) + cleanup_all( -500141 ); + + sprintf( tmp_path, "%s/sock", run_dir ); + + struct sockaddr_un unix_addr; + + memset( &unix_addr, 0, sizeof(struct sockaddr_un) ); + unix_addr.sun_family = AF_LOCAL; + strcpy( unix_addr.sun_path, tmp_path ); + + + if ( strlen( curr_strm_pos ) + 4 + strlen( ARG_TEST ) > sizeof( unix_buff ) ) { + + dbg( DBGL_SYS, DBGT_ERR, "message too long: %s", curr_strm_pos ); + cleanup_all( CLEANUP_FAILURE ); + } + + if ( cmd == OPT_CHECK ) + return SUCCESS; + + Client_mode = YES; + + do { + + dbgf_all( DBGT_INFO, "called with %s",curr_strm_pos); + + if ( strlen(curr_strm_pos) > strlen(ARG_CONNECT) && + !strncmp( curr_strm_pos, ARG_CONNECT, strlen(ARG_CONNECT) ) && + (curr_strm_pos+strlen(ARG_CONNECT))[0] == ' ' ) + { + sprintf( unix_buff, "%s %c", nextword(curr_strm_pos), EOS_DELIMITER ); + + } else if ( strlen(curr_strm_pos) > strlen(ARG_CONNECT) && + !strncmp( curr_strm_pos, ARG_CONNECT, strlen(ARG_CONNECT) ) && + (curr_strm_pos+strlen(ARG_CONNECT))[0] == '=' ) + { + sprintf( unix_buff, "%s %c", curr_strm_pos+strlen(ARG_CONNECT)+1 , EOS_DELIMITER ); + + } else if ( strlen(curr_strm_pos) > 1 && curr_strm_pos[0] == opt->short_name && curr_strm_pos[1] == ' ' ) { + sprintf( unix_buff, "%s %c", nextword(curr_strm_pos), EOS_DELIMITER ); + + } else if ( strlen(curr_strm_pos) > 1 && curr_strm_pos[0] == opt->short_name && curr_strm_pos[1] != ' ' ) { + sprintf( unix_buff, "-%s %c", curr_strm_pos+1, EOS_DELIMITER ); + + } else { + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "invalid connect stream %s",curr_strm_pos); + return FAILURE; + } + + unix_sock = socket( AF_LOCAL, SOCK_STREAM, 0 ); + + /* make unix_sock socket non blocking */ + int sock_opts = fcntl( unix_sock, F_GETFL, 0 ); + fcntl( unix_sock, F_SETFL, sock_opts | O_NONBLOCK ); + + + if ( connect ( unix_sock, (struct sockaddr *)&unix_addr, sizeof(struct sockaddr_un) ) < 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, + "can't connect to unix socket '%s': %s ! Is bmxd running on this host ?", + tmp_path, strerror(errno) ); + + cleanup_all( CLEANUP_FAILURE ); + + } + + if ( write( unix_sock, unix_buff, strlen( unix_buff ) ) < 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, "can't write to unix socket: %s", strerror(errno) ); + cleanup_all( CLEANUP_FAILURE ); + + } + + //printf("::::::::::::::::: from %s begin :::::::::::::::::::\n", tmp_path ); + + if ( loop_mode ) + trash=system( "clear" ); + + int32_t recv_buff_len = 0; + + while ( !terminating() ) { + + recv_buff_len = 0; + + fd_set unix_wait_set; + + FD_ZERO(&unix_wait_set); + FD_SET(unix_sock, &unix_wait_set); + + struct timeval to = {0, 100000}; + + select( unix_sock+1, &unix_wait_set, NULL, NULL, &to ); + + if ( !FD_ISSET( unix_sock, &unix_wait_set ) ) + continue; + + do { + errno = 0; + recv_buff_len = read( unix_sock, unix_buff, MAX_UNIX_MSG_SIZE ); + + if ( recv_buff_len > 0 ) { + + char *p; + unix_buff[recv_buff_len] = '\0'; + + if ( (p=strchr( unix_buff, CONNECTION_END_CHR )) ) { + *p='\0'; + printf( "%s", unix_buff ); + break; + + } + + printf( "%s", unix_buff ); + } + + } while ( recv_buff_len > 0 ); + + if ( recv_buff_len < 0 && ( errno == EWOULDBLOCK || errno == EAGAIN ) ) + continue; + + if ( recv_buff_len < 0 ) { + dbgf(DBGL_SYS, DBGT_INFO, "sock returned %d errno %d: %s", + recv_buff_len, errno, strerror(errno) ); + } + + if ( recv_buff_len <= 0 ) + cleanup_all( CLEANUP_FAILURE ); + + break; + } + + close( unix_sock ); + unix_sock = 0; + + if ( loop_mode && !terminating() ) + wait_sec_msec( loop_interval/1000, loop_interval%1000 ); + + + } while ( loop_mode && !terminating() ); + + cleanup_all( CLEANUP_SUCCESS ); + + + } + + return SUCCESS; +} + + +static int32_t opt_connect ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + char tmp_path[MAX_PATH_SIZE+20] = ""; + + if ( cmd == OPT_SET_POST && !on_the_fly ) { + + // create unix sock: + + struct sockaddr_un unix_addr; + + sprintf( tmp_path, "%s/sock", run_dir ); + + memset( &unix_addr, 0, sizeof(struct sockaddr_un) ); + unix_addr.sun_family = AF_LOCAL; + strcpy( unix_addr.sun_path, tmp_path ); + + // Testing for open and used unix socket + + unix_sock = socket( AF_LOCAL, SOCK_STREAM, 0 ); + + if ( connect ( unix_sock, (struct sockaddr *)&unix_addr, sizeof(struct sockaddr_un) ) < 0 ) { + + dbgf_all( DBGT_INFO, "found unbound %s, going to unlink and reuse!", tmp_path ); + + close( unix_sock ); + unlink( tmp_path ); + unix_sock = socket( AF_LOCAL, SOCK_STREAM, 0 ); + + } else { + + dbg( DBGL_SYS, DBGT_ERR, + "%s busy! Probably bmxd is already running! Use [--%s %s] -c ... to connect to a running bmxd", + tmp_path, ARG_RUN_DIR, run_dir ); + cleanup_all( CLEANUP_FAILURE ); + + } + + + if ( bind ( unix_sock, (struct sockaddr *)&unix_addr, sizeof (struct sockaddr_un) ) < 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, "can't bind unix socket '%s': %s", tmp_path, strerror(errno) ); + cleanup_all( CLEANUP_FAILURE ); + + } + + if ( listen( unix_sock, 10 ) < 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, "can't listen unix socket '%s': %s", tmp_path, strerror(errno) ); + cleanup_all( CLEANUP_FAILURE ); + + } + + if ( update_pid_file() == FAILURE ) + return FAILURE; + + } + + return SUCCESS; +} + +static int32_t call_opt_apply( uint8_t cmd, uint8_t save, struct opt_type *opt, struct opt_parent *_patch, char *in, struct ctrl_node *cn ) { + + paranoia( -500154, ( cmd != OPT_CHECK && cmd != OPT_APPLY ) ); + + //cleanup_patch will change the patch, so we'll work with a duplicate and destroy it afterwards + struct opt_parent *patch = dup_opt_parent( &Patch_opt, _patch ); + + dbgf_all( DBGT_INFO, "%s save=%d %s p_diff=%d p_val:%s p_ref:%s strm:%s", + opt_cmd2str[cmd], save, opt->long_name, patch->p_diff, patch->p_val, patch->p_ref, in ); + + if ( cleanup_patch( opt, patch, cn ) == FAILURE ) + goto call_opt_apply_error; + + if ( patch->p_diff == NOP && LIST_EMPTY( &(patch->childs_instance_list) ) ) { + del_opt_parent( &Patch_opt, patch ); + return SUCCESS; + } + + // keep this check after cleanup_patch and p_diff==NOP and list_empty check to let config_reload + // apply all unchanged options + + if ( (!on_the_fly && opt->dyn_t == A_DYN) || ( on_the_fly && opt->dyn_t == A_INI) ) { + + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "--%s%s%c can %s be applied at startup", + opt->long_name, opt->short_name ? ", -" : "", opt->short_name ? opt->short_name : ' ', + on_the_fly ? "ONLY" : "NOT" ); + + goto call_opt_apply_error; + } + + + if ( opt->call_custom_option == opt_connect ) { + + if ( _opt_connect( cmd, opt, cn, in ) == FAILURE ) + goto call_opt_apply_error; + + + } else if ( cmd == OPT_CHECK ) { + + if ( opt->call_custom_option && + (opt->call_custom_option)(OPT_CHECK, save, opt, patch, cn) == FAILURE) { + + goto call_opt_apply_error; + } + + + + } else if ( cmd == OPT_APPLY ) { + + if ( opt->auth_t == A_ADM ) { + + dbg(DBGL_CHANGES, DBGT_INFO, "--%-22s %-30s", opt->long_name, patch->p_val); + + dbgf_all( DBGT_INFO, "--%-22s %-30s (%s order %d)", + opt->long_name, patch->p_val, opt_cmd2str[ cmd ], opt->order ); + } + + if ( opt->ival && patch->p_diff == DEL ) + *(opt->ival) = opt->idef; + + else if ( opt->opt_t == A_PS0 && opt->ival && patch->p_diff == ADD ) + *(opt->ival) = opt->imax; + + else if ( opt->opt_t != A_PS0 && opt->ival && patch->p_diff == ADD ) + *(opt->ival) = strtol( patch->p_val, NULL , 10 ); + + if ( opt->call_custom_option && + (opt->call_custom_option)( OPT_APPLY, save, opt, patch, cn ) == FAILURE ) + { + + dbg_cn( cn, DBGL_SYS, DBGT_ERR, + "failed setting the already succesfully tested option %s to %s", + opt->long_name, patch->p_val ); + + // this may happen when: + // - overwriting a config-file option with a startup-option (pain in the ass!) + // - configuring the same PMN option twice in one command-line + goto call_opt_apply_error; + } + + + } + + del_opt_parent( &Patch_opt, patch ); + return SUCCESS; + +call_opt_apply_error: + + del_opt_parent( &Patch_opt, patch ); + return FAILURE; +} + + +/* this table lists what could happen and how its' handled in track_opt_parent(): + +patch tracked patch tracked config- +p_val t_val p_ref t_ref -> file track + value: value: ref: + +DEL/0 x x x DEL DEL DEL | if ( !p_val && t_val ) + +DEL/0 NULL x x NOP NOP NOP | else if ( !p_val && !t_val ) + +A A A A NOP NOP NOP | else if ( p_val && p_val == t_val && p_ref == t_ref ) +A A 0 0 NOP NOP NOP | + + | else [if ( p_val && (p_val != t_val || p_ref != t_ref )] + | +A A A B ref value ref | | if ( p_ref ) +A A A 0 ref value ref | | +A B/NULL A A (*) ref value ref | | +A B/NULL A B (-) ref value ref | | +A B/NULL A 0 (-) ref value ref | | + | +A A 0 B value value 0 | | else [if( !p_ref)] +A B/NULL 0 0 value NOP 0 | | +A B/NULL 0 A (*) value value 0 | | + +(*) in these cases, when configuring parent-options +we have to reset the old (currently active) tracked t_val option +before configuring the new patched p_val parent value +This has already been done during call_option( cmd==CHECK || cmd==APPLY ) + +(-) impossible to configue in one step for parent-options + +*/ +static int32_t track_opt_parent( uint8_t cmd, uint8_t save, struct opt_type *p_opt, struct opt_parent *p_patch, struct ctrl_node *cn ) { + + struct list_node *pos; + struct opt_parent *p_reftr = get_opt_parent_ref( p_opt, p_opt->opt_t == A_PMN ? p_patch->p_ref : NULL ); + struct opt_parent *p_track = get_opt_parent_val( p_opt, p_opt->opt_t == A_PMN ? p_patch->p_val : NULL ); + + paranoia( -500125, ( p_reftr && p_track && p_reftr != p_track ) ); + + p_track = p_track ? p_track : p_reftr; + + dbgf_all( DBGT_INFO, "%s %s save=%d patch_diff:%d patch_val:%s patch_ref:%s track_val:%s track_ref:%s", + opt_cmd2str[cmd], p_opt->long_name, save, p_patch->p_diff, + p_patch->p_val, p_patch->p_ref, p_track?p_track->p_val:"-", p_track?p_track->p_ref:"-"); + + if ( p_patch->p_diff == DEL && p_track ) { + + if ( cmd==OPT_APPLY ) { + + if ( save && save_config_cb ) + save_config_cb( DEL, p_opt, p_track->p_ref ? p_track->p_ref : p_track->p_val, NULL, cn ); + + del_opt_parent( p_opt, p_track ); + } + + } else { + + uint8_t changed = NO; + + if ( p_patch->p_diff == DEL && !p_track ) { + + if ( save ) { + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "--%s %s does not exist", p_opt->long_name, p_patch->p_val ); + return FAILURE; + } + + return SUCCESS; + + } else if ( ( p_patch->p_diff == ADD && p_patch->p_val && p_track && wordsEqual( p_patch->p_val, p_track->p_val ) ) && + ( ( p_patch->p_ref && p_track->p_ref && wordsEqual( p_patch->p_ref, p_track->p_ref) ) || + ( !p_patch->p_ref && !p_track->p_ref ) ) ) + { + + } else if ( p_patch->p_val /*&& (patch_c->c_ref || !patch_c->c_ref)*/ ) { + + if ( cmd == OPT_APPLY ) { + + if ( !p_track ) { + p_track = add_opt_parent( p_opt ); + set_opt_parent_val( p_track, p_patch->p_val ); + set_opt_parent_ref( p_track, p_patch->p_ref ); + } + + if ( save && save_config_cb ) + save_config_cb( ADD, p_opt, + p_track->p_ref ? p_track->p_ref : p_track->p_val, + p_patch->p_ref ? p_patch->p_ref : p_patch->p_val, cn ); + + set_opt_parent_val( p_track, p_patch->p_val ); + set_opt_parent_ref( p_track, p_patch->p_ref ); + } + changed=YES; + + } else { + paranoia( -500121, YES ); + } + + if ( cmd == OPT_APPLY && changed && p_opt->auth_t == A_ADM ) + dbg_cn( cn, DBGL_CHANGES, DBGT_INFO, "--%-22s %c%-30s", + p_opt->long_name, p_patch->p_diff==DEL?'-':' ', p_patch->p_val); + + if ( p_track ) { + + list_for_each( pos, &p_patch->childs_instance_list ) { + + uint8_t changed_child=NO; + char *save_val = p_track->p_ref ? p_track->p_ref : p_track->p_val; + struct opt_child *c_patch = list_entry( pos, struct opt_child, list ); + struct opt_child *c_track = get_opt_child( c_patch->c_opt, p_track ); + + + if ( !c_patch->c_val && c_track ) { + + if ( cmd == OPT_APPLY ) { + if ( save && save_config_cb && c_track->c_opt->cfg_t != A_ARG ) + save_config_cb( DEL, c_track->c_opt, save_val, c_track->c_ref ? c_track->c_ref : c_track->c_val, cn ); + + del_opt_child( p_track, c_track->c_opt ); + } + changed_child=changed=YES; + + } else if ( !c_patch->c_val && !c_track ) { + + if ( save ) { + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "--%s %s /%s does not exist", + p_opt->long_name, p_patch->p_val, c_patch->c_opt->long_name ); + return FAILURE; + } + + } else if ( ( c_patch->c_val && c_track && wordsEqual(c_patch->c_val, c_track->c_val) ) && + ( ( c_patch->c_ref && c_track->c_ref && wordsEqual( c_patch->c_ref, c_track->c_ref) ) || + ( !c_patch->c_ref && !c_track->c_ref ) ) ) + { + + dbgf_all( DBGT_INFO, "--%s %s /%s %s already configured", + p_opt->long_name, p_patch->p_val, c_patch->c_opt->long_name, c_patch->c_val ); + + } else if ( c_patch->c_val ) { + + if ( cmd == OPT_APPLY ) { + if ( save && save_config_cb && c_patch->c_opt->cfg_t != A_ARG ) + save_config_cb( ADD, c_patch->c_opt, save_val, c_patch->c_ref ? c_patch->c_ref : c_patch->c_val, cn ); + + if ( !c_track ) + c_track = add_opt_child( c_patch->c_opt, p_track ); + + set_opt_child_val( c_track, c_patch->c_val ); + set_opt_child_ref( c_track, c_patch->c_ref ); + } + + changed_child=changed=YES; + + } else { + + paranoia( -500122, YES ); + } + + if ( cmd == OPT_APPLY && changed_child && c_patch->c_opt->auth_t == A_ADM ) + dbg_cn( cn, DBGL_CHANGES, DBGT_INFO, "--%-22s %-30s /%-22s %c%-30s", + p_opt->long_name, p_patch->p_val, + c_patch->c_opt->long_name, c_patch->c_val?' ':'-', c_patch->c_val ); + + } + } + + // be pedantic only after startup (!on_the_fly) and not reload-config (!save) + if ( !changed && on_the_fly && save && pedantic_check ) { + + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "--%s %s already configured", + p_opt->long_name, p_patch->p_val ); + + // actually here we can be pedantic or not because cleanup_patch() + // have already checked for double applied options + return FAILURE; + } + } + + return SUCCESS; +} + +int32_t call_option( uint8_t ad, uint8_t cmd, uint8_t save, struct opt_type *opt, struct opt_parent *patch, char *in, struct ctrl_node *cn ) { + + dbgf_all( DBGT_INFO, "%s (cmd %s del %d save %d parent_name %s order %d) p_val: %s in: %s", + opt->long_name, opt_cmd2str[ cmd ], ad, save, opt->parent_name, opt->order, patch?patch->p_val:"-", in ); + + if ( !opt ) // might be NULL when referring to disabled plugin functionality + return SUCCESS; + + paranoia( -500104, ( !( ad == ADD || ad == DEL ) ) ); + + paranoia( -500103, ( ( cmd == OPT_PATCH || cmd == OPT_ADJUST || cmd == OPT_CHECK || cmd == OPT_APPLY ) && !patch ) ); + + paranoia( -500147, ( ( cmd == OPT_PATCH || cmd == OPT_ADJUST || cmd == OPT_CHECK || cmd == OPT_APPLY ) && !cn ) ); + + if ( ( cmd == OPT_PATCH || cmd == OPT_ADJUST || cmd == OPT_CHECK || cmd == OPT_APPLY ) && + !cn->authorized && opt->auth_t == A_ADM ) + { + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "insufficient permissions to use command %s", opt->long_name ); + return FAILURE; + } + + if ( ad == DEL && ( /*!on_the_fly this is what concurrent -r and -g configurations do || */ + /* opt->dyn_t == A_INI this is what conf-reload tries ||*/ opt->cfg_t == A_ARG ) ) { + dbg( DBGL_SYS, DBGT_ERR, "option %s can not be resetted during startup!", opt->long_name ); + return FAILURE; + } + + + if ( (opt->pos_t==A_END || opt->pos_t==A_ETE) && in && !end_of_cmd_stream( opt, in ) ) { + + if ( cn ) { + dbg_cn( cn, DBGL_CHANGES, DBGT_ERR, "--%s%s%c MUST be last option before line feed", + opt->long_name, opt->short_name?", -":"", opt->short_name?opt->short_name:' ' ); + } + + goto call_option_failure; + } + + + if ( cmd == OPT_PATCH ) { + + if ( (call_opt_patch( ad, opt, patch, in, cn )) == FAILURE ) + goto call_option_failure; + + if ( ( opt->pos_t == A_EAT || opt->pos_t == A_ETE ) && in ) + return strlen( in ); + else + return SUCCESS; + + + } else if ( cmd == OPT_ADJUST ) { + + if ( opt->call_custom_option && + ((opt->call_custom_option)( OPT_ADJUST, 0, opt, patch, cn )) == FAILURE ) + goto call_option_failure; + else + return SUCCESS; + + + } else if ( cmd == OPT_CHECK || cmd == OPT_APPLY ) { + + paranoia( -500105, ( opt->parent_name ) ); + + paranoia( -500128, ( opt->cfg_t != A_ARG && ( opt->opt_t == A_PMN || patch->p_diff != DEL ) && !patch->p_val ) ); + + if ( opt->cfg_t != A_ARG && opt->opt_t == A_PMN ) { + + struct opt_parent *p_reftr = get_opt_parent_ref( opt, patch->p_ref ); + struct opt_parent *p_track = get_opt_parent_val( opt, patch->p_val ); + + paranoia( -500129, ( p_reftr && p_track && p_reftr != p_track ) ); + + p_track = p_track ? p_track : p_reftr; + + if ( ( patch->p_diff == ADD && patch->p_val && p_track && + !wordsEqual( patch->p_val, p_track->p_val ) ) && + ( patch->p_ref || p_track->p_ref ) ) + check_apply_parent_option( DEL, cmd, save, opt, p_track->p_val, cn ); + + } + + if ( (call_opt_apply( cmd, save, opt, patch, in, cn )) == FAILURE ) + goto call_option_failure; + + if ( opt->cfg_t != A_ARG && track_opt_parent( cmd, save, opt, patch, cn ) == FAILURE ) + goto call_option_failure; + + return SUCCESS; + + + } else if ( cmd == OPT_SET_POST || cmd == OPT_POST ) { + + if ( opt->call_custom_option && ((opt->call_custom_option)( cmd, 0, opt, 0, cn )) == FAILURE ) + goto call_option_failure; + + return SUCCESS; + } + + +call_option_failure: + + dbg_cn( cn, DBGL_SYS, DBGT_ERR, + "--%s %s Failed ! " + "( diff:%d ad:%d val:%d min:%d max:%d def:%d %s %d %d %d )", + opt->long_name ? opt->long_name : "-", in ? in: "-", + patch ? patch->p_diff : -1, + ad, opt->ival ? *(opt->ival) : 0, opt->imin, opt->imax, opt->idef, + opt_cmd2str[cmd], opt->opt_t, on_the_fly, wordlen(in) ); + + /* This results in too much side effects. And MUST be handled by calling function like apply_stream_opts() + if ( !on_the_fly && !pedantic_cmd_check && ( cmd == OPT_PATCH || cmd == OPT_ADJUST || cmd == OPT_CHECK || cmd == OPT_APPLY ) ) { + + dbg( DBGL_SYS, DBGT_ERR, + "ignored SYNTAX ERROR in startup configuration due to disabled --%s! FIX YOUR CONFIG NOW !!", + ARG_PEDANTIC_CMDCHECK ); + + return SUCCESS; + } + */ + + return FAILURE; +} + + + +int respect_opt_order( uint8_t test, int8_t last, int8_t next, struct opt_type *on, uint8_t load, uint8_t cmd, struct ctrl_node *cn ) { + + struct list_node *list_pos; + struct opt_type *opt; + + dbgf_all( DBGT_INFO, "%s, cmd: %s, last %d, next %d, opt %s load %d", + opt_cmd2str[ test ], opt_cmd2str[ cmd ], last, next, on?on->long_name:"???", load ); + + paranoia( -500002, ( test != OPT_CHECK && test != OPT_APPLY ) ); + + paranoia( -500107, ( cmd == OPT_CHECK || cmd == OPT_APPLY ) ); + + + if ( next == 0 ) + return last; + + if ( last > next ) { + + // debug which option caused the problems ! + dbg_cn( cn, DBGL_SYS, DBGT_ERR, + "--%s%s%c (order=%d option) MUST appear earlier in command sequence!", + on?on->long_name:"???", on&&on->short_name?", ":"",on&&on->short_name?on->short_name:' ', next ); + + return FAILURE; + } + + if ( last == next ) + return next; + + + list_for_each( list_pos, &opt_list ) { + + opt = (struct opt_type *)list_entry( list_pos, struct opt_data, list ); + + if ( load && opt->order >= last+1 && opt->order <= next ) { + + if ( load_config_cb && load_config_cb( test, opt, cn ) == FAILURE ) { + + dbgf_all( DBGT_ERR, "load_config_cb() %s failed", + opt->long_name ); + + return FAILURE; + } + } + + if ( test == OPT_APPLY && opt->order >= last && opt->order <= next-1 ) { + + if ( call_option( ADD, cmd, 0/*save*/, opt, 0,0, cn ) == FAILURE ) { + + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "call_option() %s cmd %s failed", + opt->long_name, opt_cmd2str[cmd] ); + + return FAILURE; + } + } + } + + return next; +} + + +// if returns SUCCESS then fd might be closed ( called remove_ctrl_node( fd ) ) or not. +// if returns FAILURE then fd IS open and must be closed +int8_t apply_stream_opts( char *s, uint8_t cmd, uint8_t load_cfg, struct ctrl_node *cn ) { + + enum { + NEXT_OPT, // 0 + NEW_OPT, // 1 + SHORT_OPT, // 2 + LONG_OPT, // 3 + LONG_OPT_VAL, // 4 + LONG_OPT_WHAT, // 5 + LONG_OPT_ARG, // 6 + LONG_OPT_ARG_VAL, // 7 + }; + + char *state2str[] = {"NEXT_OPT","NEW_OPT","SHORT_OPT","LONG_OPT","LONG_OPT_VAL","LONG_OPT_WHAT","LONG_OPT_ARG","LONG_OPT_ARG_VAL"}; + + + int8_t state = NEW_OPT; + struct opt_type *opt = NULL; + struct opt_type *opt_arg = NULL; + char* equalp = NULL; + char* pmn_s = NULL; + int8_t order = 0; + int32_t pb; + char argument[MAX_ARG_SIZE]; + struct opt_parent *patch = NULL; + + if ( cmd != OPT_CHECK && cmd != OPT_APPLY ) + return FAILURE; + + uint8_t del; + + Load_config = load_cfg; + Testing = 0; + + + while ( s && strlen(s) >= 1 ) { + + dbgf_all( DBGT_INFO, "cmd: %-10s, state: %s opt: %s, wordlen: %d rest: %s", + opt_cmd2str[cmd], state2str[state], opt?opt->long_name:"null", wordlen(s), s ); + + if ( Testing ) { + Testing = 0; + close_ctrl_node( CTRL_CLOSE_SUCCESS, cn ); + return SUCCESS; + } + + + if ( state == NEXT_OPT ) { + // assumes s points to last successfully processed word or its following gap + s = nextword(s); + state = NEW_OPT; + + } else if ( state == NEW_OPT && wordlen(s) >= 2 && s[0]=='-' && s[1]!='-' ) { + + s++; + state=SHORT_OPT; + + } else if ( state == NEW_OPT && wordlen(s) >=3 && s[0]=='-' && s[1]=='-' ) { + + s+=2; + state=LONG_OPT; + + } else if ( state == NEW_OPT && wordlen(s) >=1 && s[0]!='-' && s[0]!='/' ) { + + state=LONG_OPT; + + } else if ( state == SHORT_OPT && wordlen(s) >=1 ) { + + if ( !(opt = get_option( NULL, YES, s )) ) + goto apply_args_error; + + if ( (order=respect_opt_order( cmd, order, opt->order, opt, Load_config, OPT_SET_POST, cn )) < 0 ) + goto apply_args_error; + + if ( opt->opt_t == A_PS0 ) { + + if ( (pb=check_apply_parent_option( ADD, cmd, 0/*save*/, opt, s, cn )) == FAILURE ) + goto apply_args_error; + + + if ( pb ) { + s+=pb; + state = NEXT_OPT; + } else if ( wordlen(s+1) >= 1 ) { + s++; + state = SHORT_OPT; + } else if ( wordlen(s+1) == 0 ) { + s++; + state = NEXT_OPT; + } else { + goto apply_args_error; + } + + } else if ( opt->opt_t == A_PS1 || opt->opt_t == A_PMN ) { + + s++; + + if ( wordlen(s) > 1 && s[0] == '=' ) + s++; + + if ( wordlen(s) == 0 && !(s = nextword(s)) ) + goto apply_args_error; + + state = LONG_OPT_VAL; + } + + + } else if ( state == LONG_OPT && wordlen(s) >= 1 ) { + + opt = get_option( NULL, NO, s ); + + if ( opt ) { + + if ( (order=respect_opt_order( cmd, order, opt->order, opt, Load_config, OPT_SET_POST, cn )) < 0 ) + goto apply_args_error; + + + if ( opt->opt_t == A_PS0 ) { + + if ( (pb=check_apply_parent_option( ADD, cmd, 0/*save*/, opt, s, cn )) == FAILURE ) + goto apply_args_error; + + if ( pb ) + s+=pb; + else + s+=wordlen(s); + + state = NEXT_OPT; + + } else if ( opt->opt_t == A_PS1 || opt->opt_t == A_PMN ) { + + equalp=index( s, '=' ); + + if ( equalp && equalp < s+wordlen(s) ) { + + s = equalp+1; + + } else { + + if ( (s=nextword(s)) == NULL ) + goto apply_args_error; + + } + + state = LONG_OPT_VAL; + + } else { + goto apply_args_error; + } + + } else { + + goto apply_args_error; + } + + + } else if ( state == LONG_OPT_VAL && wordlen(s) >=1 ) { + + if ( opt->opt_t == A_PS1 ) { + + s = s + (del = ((s[0]==ARG_RESET_CHAR) ? 1 : 0) ); + + if ( (pb=check_apply_parent_option( del, cmd, (on_the_fly?YES:NO)/*save*/, opt, s, cn )) == FAILURE ) + goto apply_args_error; + + s+=pb; + state = NEXT_OPT; + + } else if ( opt->opt_t == A_PMN ) { + + s = s + (del = ((s[0] == ARG_RESET_CHAR) ? 1 : 0)); + + patch = add_opt_parent( &Patch_opt ); + + if ( (pb=call_option( del, OPT_PATCH, 0/*save*/, opt, patch, s, cn )) == FAILURE ) + goto apply_args_error; + + pmn_s = s; + s+=pb; + + state = LONG_OPT_WHAT; + + } else { + goto apply_args_error; + } + + } else if ( state == LONG_OPT_WHAT ) { + + if ( opt->opt_t != A_PMN ) + goto apply_args_error; + + char* slashp = index( s, '/' ); + + if ( slashp && slashp == nextword(s) && patch->p_diff == DEL ) { + + wordCopy( argument, slashp+1 ); + + dbg_cn( cn, DBGL_SYS, DBGT_ERR, + "--%s %s can not be resetted and refined at the same time. Just omit /%s!", + opt->long_name, patch->p_val, argument ); + + goto apply_args_error; + + } else if ( slashp && slashp == nextword(s) ) { + + //nextword starts with slashp + + s = slashp+1; + state = LONG_OPT_ARG; + + } else { + + if ( (call_option( ADD, OPT_ADJUST, 0/*save*/, opt, patch, pmn_s, cn )) == FAILURE ) + goto apply_args_error; + + //indicate end of LONG_OPT_ARGs + if ( (call_option( ADD, cmd, (on_the_fly?YES:NO)/*save*/, opt, patch, pmn_s, cn )) == FAILURE ) + goto apply_args_error; + + del_opt_parent( &Patch_opt, patch ); + patch = NULL; + state = NEXT_OPT; + } + + + } else if ( state == LONG_OPT_ARG && wordlen(s) >=1 ) { + + opt_arg = get_option( opt, NO, s ); + + if ( !opt_arg || opt_arg->opt_t != A_CS1 || opt_arg->order != opt->order ) + goto apply_args_error; + + equalp=index( s, '=' ); + + if ( equalp && equalp < s+wordlen(s) ) { + + s = equalp+1; + + } else { + + if ( (s=nextword(s))==NULL ) + goto apply_args_error; + } + + state = LONG_OPT_ARG_VAL; + + + } else if ( state == LONG_OPT_ARG_VAL && wordlen(s) >=1 ) { + + s = s + (del = ((s[0] == ARG_RESET_CHAR) ? 1 : 0)); + + if ( (pb=call_option( del, OPT_PATCH, 0/*save*/, opt_arg, patch, s, cn )) == FAILURE ) + goto apply_args_error; + + s+=pb; + + state = LONG_OPT_WHAT; + + + } else { + goto apply_args_error; + } + + continue; + } + + + if ( state != LONG_OPT_ARG && state != NEW_OPT && state != NEXT_OPT ) + goto apply_args_error; + + + dbgf_all( DBGT_INFO, "all opts and args succesfully called with %s", opt_cmd2str[cmd] ); + + if ( (order=respect_opt_order( cmd, order, 99, NULL, Load_config, OPT_SET_POST, cn )) < 0 ) + goto apply_args_error; + + + return SUCCESS; + +apply_args_error: + + if ( patch ) + del_opt_parent( &Patch_opt, patch ); + + snprintf( argument, MIN( sizeof(argument), wordlen(s)+1 ), "%s", s ); + + //otherwise invalid sysntax identified only by apply_stream_opts is not printed; + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "invalid argument: %s", argument ); + + return FAILURE; + +} + + + +void apply_init_args( int argc, char *argv[] ) { + + prog_name = argv[0]; + + get_init_string( argc, argv ); + + char *stream_opts = nextword( init_string ); + + struct ctrl_node *cn = create_ctrl_node( STDOUT_FILENO, NULL, (getuid() | getgid())/*are we root*/ ? NO : YES ); + + if ( ( apply_stream_opts( stream_opts, OPT_CHECK, YES/*load cfg*/, cn ) == FAILURE ) || + ( apply_stream_opts( stream_opts, OPT_APPLY, YES/*load cfg*/, cn ) == FAILURE ) ) + cleanup_all( CLEANUP_FAILURE ); + + respect_opt_order( OPT_APPLY, 0, 99, NULL, NO/*load_cofig*/, OPT_POST, 0/*probably closed*/ ); + + close_ctrl_node( CTRL_CLOSE_STRAIGHT, cn ); + + cb_plugin_hooks( NULL, PLUGIN_CB_CONF ); + + free_init_string(); +} + + + + + + + + +char *ipStr( uint32_t addr ) { +#define IP2S_ARRAY_LEN 10 + static uint8_t c=0; + static char str[IP2S_ARRAY_LEN][IP4_STR_LEN]; + + c = (c+1) % IP2S_ARRAY_LEN; + + inet_ntop( AF_INET, &addr, str[c], IP4_STR_LEN ); + + return str[c]; +} + + + +int8_t str2netw( char* args, uint32_t *ip, char delimiter, struct ctrl_node *cn, int32_t *val, int32_t max ) { + + struct in_addr tmp_ip_holder; + char *slashptr = NULL; + + char switch_arg[30]; + + if ( wordlen( args ) < 1 || wordlen( args ) > 29 ) + return FAILURE; + + wordCopy( switch_arg, args ); + switch_arg[wordlen( args )] = '\0'; + + if ( val ) { + + if ( ( slashptr = strchr( switch_arg, delimiter ) ) != NULL ) { + char *end = NULL; + + *slashptr = '\0'; + + errno = 0; + *val = strtol( slashptr+1, &end, 10 ); + + if ( ( errno == ERANGE ) || *val > max || *val < 0 ) { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "invalid argument %s %s", + args, strerror( errno ) ); + + return FAILURE; + + } else if ( end==slashptr+1 || wordlen(end) ) { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "invalid argument trailer %s", end ); + return FAILURE; + } + + } else { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "invalid argument %s! Fix you parameters!", switch_arg ); + return FAILURE; + } + } + + errno = 0; + + if ( (inet_pton( AF_INET, switch_arg, &tmp_ip_holder )) < 1 || !tmp_ip_holder.s_addr ) { + + dbgf_all( DBGT_WARN, "invalid argument: %s: %s", args, strerror(errno) ); + return FAILURE; + } + + *ip = tmp_ip_holder.s_addr; + + return SUCCESS; +} + + +void addr_to_str( uint32_t addr, char *str ) { + + inet_ntop( AF_INET, &addr, str, IP4_STR_LEN ); + return; +} + + + +uint32_t validate_net_mask( uint32_t ip, uint32_t mask, struct ctrl_node *cn ) { + + uint32_t nip = ip & htonl( 0xFFFFFFFF<<( 32 - mask ) ); + + if ( cn && nip != ip ) + dbg_cn( cn, DBGL_CHANGES, DBGT_WARN, + "inconsistent network prefix %s/%d - probably you mean: %s/%d", + ipStr(ip), mask, ipStr(nip), mask); + + return nip; +} + + + +int32_t check_file( char *path, uint8_t write, uint8_t exec ) { + + struct stat fstat; + + errno = 0; + int stat_ret = stat( path, &fstat ); + + if ( stat_ret < 0 ) { + + dbgf( DBGL_CHANGES, DBGT_WARN, "%s does not exist! (%s)", + path, strerror(errno)); + + } else { + + if ( S_ISREG( fstat.st_mode ) && + (S_IRUSR & fstat.st_mode) && + ((S_IWUSR & fstat.st_mode) || !write) && + ((S_IXUSR & fstat.st_mode) || !exec) ) + return SUCCESS; + + dbgf( DBGL_SYS, DBGT_ERR, + "%s exists but has inapropriate permissions (%s)", + path, strerror(errno)); + + } + + return FAILURE; +} + +int32_t check_dir( char *path, uint8_t create, uint8_t write ) { + + struct stat fstat; + + errno = 0; + int stat_ret = stat( path, &fstat ); + + if ( stat_ret < 0 ) { + + if ( create && mkdir( path, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH ) >= 0 ) + return SUCCESS; + + dbgf( DBGL_SYS, DBGT_ERR, + "directory %s does not exist and can not be created (%s)", path, strerror(errno)); + + } else { + + if ( S_ISDIR( fstat.st_mode ) && + ( S_IRUSR & fstat.st_mode) && + ( S_IXUSR & fstat.st_mode) && + ((S_IWUSR & fstat.st_mode) || !write) ) + return SUCCESS; + + dbgf( DBGL_SYS, DBGT_ERR, + "directory %s exists but has inapropriate permissions (%s)", path, strerror(errno)); + + } + + return FAILURE; + +} + + + + +uint32_t wordlen ( char *s ) { + + uint32_t i = 0; + + if ( !s ) + return 0; + + for( i=0; i<strlen(s); i++ ) { + + if ( s[i] == '\0' || s[i] == '\n' || s[i]==' ' || s[i]=='\t' ) + return i; + } + + return i; +} + + +int8_t wordsEqual ( char *a, char *b ) { + + if ( wordlen( a ) == wordlen ( b ) && !strncmp( a, b, wordlen(a) ) ) + return YES; + + return NO; +} + + +void wordCopy( char *out, char *in ) { + + if ( out && in && wordlen(in) < MAX_ARG_SIZE ) { + + snprintf( out, wordlen(in)+1, "%s", in ); + + } else if ( out && !in ) { + + out[0]=0; + + } else { + + dbgf( DBGL_SYS, DBGT_ERR, "called with out: %s and in: %s", out, in ); + cleanup_all( -500017 ); + + } +} + + +static int8_t show_info ( struct ctrl_node *cn, void *data, struct opt_type *opt, struct opt_parent *p, struct opt_child *c ) { + + if ( c ) + dbg_printf( cn, " /%-18s %-20s %s%s\n", + c->c_opt->long_name, c->c_val, (c->c_ref ? "resolved from " : ""), (c->c_ref ? c->c_ref : "") ); + else + dbg_printf( cn, " %-22s %-20s %s%s\n", + opt->long_name, p->p_val, (p->p_ref ? "resolved from " : ""), (p->p_ref ? p->p_ref : "") ); + + + return SUCCESS; +} + + +static int32_t opt_show_info ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + if ( cmd == OPT_APPLY ) { + + /* + //TBD: include all routing tables + dbg_printf(cn, " source_version %s\n", SOURCE_VERSION); + dbg_printf(cn, " compat_version %i\n", COMPAT_VERSION); + dbg_printf(cn, "\n"); + */ + + func_for_each_opt( cn, NULL, "opt_show_info()", show_info ); + + if ( !on_the_fly ) + cleanup_all(CLEANUP_SUCCESS); + + + } + + return SUCCESS; +} + + + +static int32_t opt_no_fork ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + if ( cmd == OPT_APPLY ) { + + debug_level = strtol( patch->p_val, NULL , 10); + + activate_debug_system(); + + } else if ( cmd == OPT_POST && !on_the_fly ) { + + activate_debug_system(); + + } + + return SUCCESS; +} + + +static int32_t opt_debug ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + if ( cmd == OPT_APPLY ) { + + int ival = strtol( patch->p_val, NULL , 10); + + + if ( ival == DBGL_SYS || + ival == DBGL_CHANGES || + ival == DBGL_TEST || + ival == DBGL_ALL ) + { + + remove_dbgl_node( cn ); + add_dbgl_node( cn, ival ); + return SUCCESS; + + } else if ( ival == DBGL_ROUTES ) { + + check_apply_parent_option( ADD, OPT_APPLY, _save, get_option( 0, 0, ARG_ROUTES ), 0, cn ); + + } else if ( ival == DBGL_LINKS ) { + + check_apply_parent_option( ADD, OPT_APPLY, _save, get_option( 0, 0, ARG_LINKS ), 0, cn ); + + } else if ( ival == DBGL_DETAILS ) { + + check_apply_parent_option( ADD, OPT_APPLY, 0, get_option( 0, 0, ARG_STATUS ), 0, cn ); + check_apply_parent_option( ADD, OPT_APPLY, _save, get_option( 0, 0, ARG_LINKS ), 0, cn ); + check_apply_parent_option( ADD, OPT_APPLY, _save, get_option( 0, 0, ARG_ORIGINATORS ), 0, cn ); +/* + } else if ( ival == DBGL_SERVICES ) { + + check_apply_parent_option( ADD, OPT_APPLY, _save, get_option( 0, 0, ARG_SERVICES ), 0, cn ); + + } else if ( ival == DBGL_HNAS ) { + + check_apply_parent_option( ADD, OPT_APPLY, _save, get_option( 0, 0, ARG_HNAS ), 0, cn ); + + } else if ( ival == DBGL_GATEWAYS ) { + + check_apply_parent_option( ADD, OPT_APPLY, _save, get_option( 0, 0, ARG_GATEWAYS ), 0, cn ); +*/ + } else if ( ival == DBGL_PROFILE ) { + +#if defined MEMORY_USAGE + debugMemory( cn ); +#endif + } + close_ctrl_node( CTRL_CLOSE_SUCCESS, cn ); + } + + return SUCCESS; +} + + +static int32_t opt_help ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + if ( cmd != OPT_APPLY ) + return SUCCESS; + + if ( !cn ) + return FAILURE; + + + if ( !strcmp(opt->long_name, ARG_VERBOSE_HELP)) { + + show_opts_help( NO, YES, cn ); + + } else if ( !strcmp(opt->long_name, ARG_HELP) ) { + + show_opts_help( NO, NO, cn ); + + } else if ( !strcmp(opt->long_name, ARG_VERBOSE_EXP) ) { + + show_opts_help( YES, YES, cn ); + + } else if ( !strcmp(opt->long_name, ARG_EXP) ) { + + show_opts_help( YES, NO, cn ); + + } else if ( !strcmp(opt->long_name, ARG_VERSION) ) { + + dbg_printf( cn, "BMX (BatMan-eXp) %s-rv%d (compatibility version %i)\n", + SOURCE_VERSION, REVISION_VERSION, COMPAT_VERSION); + + } else { + + show_opts_help( NO, NO, cn ); + } + + if ( !on_the_fly ) + cleanup_all(CLEANUP_SUCCESS); + + return SUCCESS; +} + + +static int32_t opt_quit ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + if ( cmd == OPT_APPLY ) + close_ctrl_node( CTRL_CLOSE_SUCCESS, cn ); + + return SUCCESS; +} + + + +static int32_t opt_run_dir ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + char tmp_dir[MAX_PATH_SIZE] = ""; + + if ( cmd == OPT_CHECK || cmd == OPT_APPLY ) { + + if ( wordlen( patch->p_val )+1 >= MAX_PATH_SIZE || patch->p_val[0] != '/' ) + return FAILURE; + + snprintf( tmp_dir, wordlen(patch->p_val)+1, "%s", patch->p_val ); + + if ( check_dir( tmp_dir, YES/*create*/, YES/*writable*/ ) == FAILURE ) + return FAILURE; + + if ( cmd == OPT_APPLY ) { + strcpy( run_dir, tmp_dir ); + } + + } else if ( cmd == OPT_SET_POST && !on_the_fly ) { + + if ( check_dir( run_dir, YES/*create*/, YES/*writable*/ ) == FAILURE ) + return FAILURE; + + } + + return SUCCESS; +} + + + + + + + + + +static struct opt_type control_options[]= +{ +// ord parent long_name shrt Attributes *ival min max default *func,*syntax,*help + {ODI,0,0, 0, 0,0,0,0,0,0, 0, 0, 0, 0, 0, + 0, "\nGeneral configuration options:"}, + + {ODI,0,ARG_HELP, 'h',0,A_PS0,A_USR,A_DYI,A_ARG,A_END, 0, 0, 0, 0, opt_help, + 0, "summarize help"}, + + {ODI,0,ARG_VERBOSE_HELP, 'H',0,A_PS0,A_USR,A_DYI,A_ARG,A_END, 0, 0, 0, 0, opt_help, + 0, "show help"}, + + {ODI,0,ARG_EXP, 'x',0,A_PS0,A_USR,A_DYI,A_ARG,A_END, 0, 0, 0, 0, opt_help, + 0, "summarize advanced and experimental options"}, + + {ODI,0,ARG_VERBOSE_EXP, 'X',0,A_PS0,A_USR,A_DYI,A_ARG,A_END, 0, 0, 0, 0, opt_help, + 0, "show advanced and experimental options"}, + + {ODI,0,ARG_VERSION, 'v',0,A_PS0,A_USR,A_DYI,A_ARG,A_ANY, 0, 0, 0, 0, opt_help, + 0, "show version"}, +#ifndef NOTRAILER + {ODI,0,ARG_TRAILER, 'V',0,A_PS0,A_USR,A_INI,A_ARG,A_END, 0, 0, 0, 0, opt_help, + 0, "show trailer"}, +#endif + + {ODI,0,ARG_TEST, 0, 0,A_PS0,A_ADM,A_DYI,A_ARG,A_ANY, &Testing, 0, 1, 0, 0, + 0, "test remaining args and provide feedback about projected success (without applying them)"}, + + {ODI,0,ARG_NO_FORK, 'd',0,A_PS1,A_ADM,A_INI,A_ARG,A_ANY, 0, DBGL_MIN, DBGL_MAX, -1, opt_no_fork, + ARG_VALUE_FORM, "print debug information instead of forking to background\n" }, + {ODI,0,ARG_DEBUG, 'd',0,A_PS1,A_ADM,A_DYN,A_ARG,A_ETE, 0, DBGL_MIN, DBGL_MAX, -1, opt_debug, + ARG_VALUE_FORM, "show debug information:\n" + " 0 : system\n" + " 1 : originators\n" + " 2 : gateways\n" + " 3 : changes\n" + " 4 : verbose changes\n" + " 5 : profiling (depends on -DDEBUG_MALLOC -DMEMORY_USAGE -DPROFILE_DATA)\n" + " 7 : services\n" + " 8 : details\n" + " 9 : announced networks and interfaces\n" + " 10 : links\n" + " 11 : testing" }, + + {ODI,0,ARG_RUN_DIR, 0, 2,A_PS1,A_ADM,A_INI,A_CFA,A_ANY, 0, 0, 0, 0, opt_run_dir, + ARG_DIR_FORM, "set runtime DIR of pid, socket,... - default: " DEF_RUN_DIR " (must be defined before --" ARG_CONNECT ")."}, + + + + {ODI,0,"loop_mode", 'l',3,A_PS0,A_ADM,A_INI,A_ARG,A_ANY, &loop_mode, 0, 1, 0, 0, + 0, "put client daemon in loop mode to periodically refresh debug information"}, + +#ifndef LESS_OPTIONS + {ODI,0,"loop_interval", 0, 3,A_PS1,A_ADM,A_INI,A_ARG,A_ANY, &loop_interval, MIN_LOOP_INTERVAL,MAX_LOOP_INTERVAL,DEF_LOOP_INTERVAL,0, + ARG_VALUE_FORM, "periodicity in ms with which client daemon in loop-mode refreshes debug information"}, +#endif + +#ifndef NODEPRECATED + {ODI,0,"batch_mode", 'b',3,A_PS0,A_ADM,A_INI,A_ARG,A_ANY, 0, 0, 0, 0, opt_deprecated,0,0}, +#endif + + {ODI,0,ARG_CONNECT, 'c',3,A_PS0,A_ADM,A_INI,A_ARG,A_EAT, 0, 0, 0, 0, opt_connect, + 0, "set client mode. Connect and forward remaining args to main routing daemon"}, + + //order=5: so when used during startup it also shows the config-file options + {ODI,0,ARG_SHOW_CHANGED, 'i',5,A_PS0,A_ADM,A_DYI,A_ARG,A_ANY, 0, 0, 0, 0, opt_show_info, + 0, "inform about configured options" }, +#ifndef LESS_OPTIONS + {ODI,0,ARG_PEDANTIC_CMDCHECK, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &pedantic_check,MIN_PEDANT_CHK, MAX_PEDANT_CHK, DEF_PEDANT_CHK, 0, + ARG_VALUE_FORM, "disable/enable pedantic checking of command-line parameters and context -\n" + " ( e.g. fail setting a parameter without changing it)" }, +#endif + {ODI,0,"dbg_mute_timeout", 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &dbg_mute_to, 0, 10000000, 100000, 0, + ARG_VALUE_FORM, "set timeout in ms for muting frequent messages"}, + + + {ODI,0,ARG_QUIT,EOS_DELIMITER, 5,A_PS0,A_USR,A_DYN,A_ARG,A_END, 0, 0, 0, 0, opt_quit,0,0} +}; + + + +void init_control( void ) { + + int i; + + char *d = getenv(BMX_ENV_DEBUG); + if ( d && strtol(d, NULL , 10) >= DBGL_MIN && strtol(d, NULL , 10) <= DBGL_MAX ) + debug_level = strtol(d, NULL , 10); + + for ( i = DBGL_MIN; i <= DBGL_MAX; i++ ) + LIST_INIT_HEAD( dbgl_clients[i], struct dbgl_node, list ); + + openlog( "bmx", LOG_PID, LOG_DAEMON ); + + memset( &Patch_opt, 0, sizeof( struct opt_type ) ); + + LIST_INIT_HEAD( Patch_opt.d.childs_type_list, struct opt_data, list); + LIST_INIT_HEAD( Patch_opt.d.parents_instance_list, struct opt_parent, list); + + register_options_array( control_options, sizeof( control_options ) ); + +} + +void cleanup_config( void ) { + + del_opt_parent( &Patch_opt, NULL ); + + while( !LIST_EMPTY( &opt_list ) ) + remove_option( (struct opt_type*)list_entry( (&opt_list)->next, struct opt_data, list ) ); + + free_init_string(); + +} + + + +void cleanup_control( void ) { + + int8_t i; + + debug_system_active = NO; + closelog(); + + if ( unix_sock ) + close( unix_sock ); + + unix_sock = 0; + + close_ctrl_node( CTRL_PURGE_ALL, 0 ); + + for ( i = DBGL_MIN; i <= DBGL_MAX; i++ ) { + + while( !LIST_EMPTY( &dbgl_clients[i] ) ) + remove_dbgl_node( (list_entry( (&dbgl_clients[i])->next, struct dbgl_node, list ))->cn ); + + } + +} +
Added: trunk/bmx/control.h =================================================================== --- trunk/bmx/control.h (rev 0) +++ trunk/bmx/control.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#define MIN_UPTIME 0 +#define MAX_UPTIME 2147383 /*(((TP32/1000)/2)-100) /1000 to talk about seconds and not ms, /2 to not render scheduled events outdated, -100 to be save */ +#define DEF_UPTIME 0 + +#define DBGT_NONE 0 +#define DBGT_INFO 1 +#define DBGT_WARN 2 +#define DBGT_ERR 3 + + +//extern int debug_level; +#define DBGL_MIN 0 +#define DBGL_SYS 0 +#define DBGL_ROUTES 1 +#define DBGL_GATEWAYS 2 +#define DBGL_CHANGES 3 +#define DBGL_ALL 4 +#define DBGL_PROFILE 5 +#define DBGL_UNUSED 6 +#define DBGL_SERVICES 7 +#define DBGL_DETAILS 8 +#define DBGL_HNAS 9 +#define DBGL_LINKS 10 +#define DBGL_TEST 11 +#define DBGL_MAX 11 +#define DBGL_INVALID 12 + + +extern int unix_sock; + +extern struct list_head ctrl_list; + +#define ARG_PEDANTIC_CMDCHECK "pedantic_cmd_check" + + +extern int32_t Client_mode; + + +#define CONNECTION_END_STR "$" +#define CONNECTION_END_CHR '$' + +enum { + CTRL_CLOSE_ERROR, + CTRL_CLOSE_SUCCESS, + CTRL_CLOSE_STRAIGHT, + CTRL_CLOSE_DELAY, + CTRL_CLEANUP, + CTRL_PURGE_ALL +}; + +#define CTRL_CLOSING_TIMEOUT 5000 + + +struct ctrl_node +{ + struct list_node list; + int fd; + void (*cn_fd_handler) (struct ctrl_node *); + uint32_t closing_stamp; + uint8_t authorized; + int8_t dbgl; +}; + +extern struct list_head dbgl_clients[DBGL_MAX+1]; + +struct dbgl_node +{ + struct list_node list; + struct ctrl_node *cn; +}; + + + +// muting does not help if a changing value like time or seqno occurs durig the first DBG_HIST_TEXT_SIZE bytes +#define DBG_HIST_TEXT_SIZE 80 +#define DBG_HIST_SIZE 20 +//#define DBG_HIST_EXPIRE 100000 + +struct dbg_histogram { + uint32_t print_stamp; + int32_t expire; + uint16_t check_len; + uint16_t catched; + char text[ DBG_HIST_TEXT_SIZE ]; +}; + + +#ifndef TESTDEBUG + +#define DBG_HIST_NEW 0x00 +#define DBG_HIST_MUTING 0x01 +#define DBG_HIST_MUTED 0x02 + +#ifdef NODEBUGALL +#define dbgf_all(...) {;} +#else +#define dbgf_all( dbgt, ... ); do { if ( __dbgf_all() ) { _dbgf_all( dbgt, __FUNCTION__, __VA_ARGS__ ); } } while (0) +#endif + +#ifdef EXTDEBUG +#define dbgf_ext( dbgt, ... ); do { if ( __dbgf_all() ) { _dbgf_all( dbgt, __FUNCTION__, __VA_ARGS__ ); } } while (0) +#else +#define dbgf_ext(...) {;} +#endif + +#define dbgf( dbgl, dbgt, ...); _dbgf( dbgl, dbgt, __FUNCTION__, __VA_ARGS__ ); +#define dbgf_cn( cn, dbgl, dbgt, ...); _dbgf_cn( cn, dbgl, dbgt, __FUNCTION__, __VA_ARGS__ ); + +void dbg ( int8_t dbgl, int8_t dbgt, char *last, ... ); +void _dbgf ( int8_t dbgl, int8_t dbgt, char const *f, char *last, ... ); +void dbg_cn ( struct ctrl_node *cn, int8_t dbgl, int8_t dbgt, char *last, ... ); +void _dbgf_cn ( struct ctrl_node *cn, int8_t dbgl, int8_t dbgt, char const *f, char *last, ... ); +void dbg_mute ( uint32_t check_len, int8_t dbgl, int8_t dbgt, char *last, ... ); +void _dbgf_all ( int8_t dbgt, char const *f, char *last, ... ); +uint8_t __dbgf_all( void ); + +void dbg_printf( struct ctrl_node *cn, char *last, ... ); + +#else + +#define dbgf( dbgl, dbgt, ...); printf( __VA_ARGS__ ) +#define dbgf_cn( cn, dbgl, dbgt, ...); printf( __VA_ARGS__ ) +#define dbg( dbgl, dbgt, ... ) printf( __VA_ARGS__ ) +#define dbg_cn( cn, dbgl, dbgt, ... ) printf( __VA_ARGS__ ) +#define dbg_mute( check_len, dbgl, dbgt, ... ) printf( __VA_ARGS__ ) +#define dbgf_all( dbgt, ... ) printf( __VA_ARGS__ ) +#define dbgf_ext( dbgt, ... ) printf( __VA_ARGS__ ) +#define dbg_printf( cn, ... ) printf( __VA_ARGS__ ) + + +#endif + +void accept_ctrl_node( void ); +void handle_ctrl_node( struct ctrl_node *cn ); +void close_ctrl_node( uint8_t cmd, struct ctrl_node *cn ); +struct ctrl_node *create_ctrl_node( int fd, void (*cn_fd_handler) (struct ctrl_node *), uint8_t authorized ); + + + + +#define REFERENCE_KEY_WORD "ref:" + +#define EOS_DELIMITER '#' + +#define MAX_UNIX_MSG_SIZE 2000 + +extern struct list_head opt_list; + + +/* opt_t types (Parent/Child, Single/Multiple, 0/1/N-arguments) */ +#define A_PS0 0x01 +#define A_PS1 0x02 +#define A_PMN 0x14 +#define A_CS1 0x22 + + +/* auth_t types */ +#define A_ADM 0x10 +#define A_USR 0x00 + +/* dyn_t types: */ +// can only be used on-the-fly +#define A_DYN 0x20 +// can never be used on-the-fly +#define A_INI 0x40 +// can be used during init and on-the-fly +#define A_DYI 0x60 + +/* cfg_t types: */ +// can only be set as command-line argument. NOT shows as relevant parameter +#define A_ARG 0x02 +// can be set in config file and as command-line argument. Shows as relevant parameter +#define A_CFA 0x03 + +/* pos_t types: */ +// must be given as first argument +#define A_BEG 0x01 NOT IMPLEMENTED +// may appera anywhere in command stream +#define A_ANY 0x02 +// must appear as last argument +#define A_END 0x03 +// may appera anywhere in command stream but consumes remaining arguments +#define A_EAT 0x04 +// must appear as last argunent and cosumes remaining arguments +#define A_ETE 0x05 + + +extern char *opt_cmd2str[]; + + +struct opt_child { + + struct list_node list; + + struct opt_type *c_opt; // key, pointing to related opt_type + struct opt_parent *parent_instance; + + char *c_val; + + uint8_t p_diff; //ADD, DEL, NOP + + char *c_ref; +}; + + +struct opt_parent { + + struct list_node list; + + struct list_head childs_instance_list; + + char *p_val; //key + + uint8_t p_diff; //ADD, DEL, NOP + + char *p_ref; + +}; + +#define ODI {{0},0,{0,0,0,0},{0,0,0,0}} + +struct opt_data { + + struct list_node list; + + struct opt_type *parent_opt; //REMOVE THIS and use casting instead ! + + struct list_head childs_type_list; //if this opt is a section type, then further sub-opts types can be listed here + + struct list_head parents_instance_list; // +// uint16_t found_parents; + +}; + +struct opt_type { + + struct opt_data d; //MUST be first structure in opt_type to allow casting between struct opt_data and struct opt_type + +// int8_t old_order; // enforces an order during the init process, (0==anytime????), 1..99: in this order. Might become removed + + char *parent_name; + char *long_name; + + char short_name; + int8_t order; // enforces an order during the init process, (0==anytime????), 1..99: in this order. Might become removed + + uint8_t opt_t; + + uint8_t auth_t; + uint8_t dyn_t; + uint8_t cfg_t; + uint8_t pos_t; + + // if != NULL call_option() will be initialize / reset(ARG_RESET) to idef + int32_t *ival; + // if imin != imax call_option() will test for validity + int32_t imin; + int32_t imax; + int32_t idef; + + int32_t (*call_custom_option)( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ); + +#define ARG_VALUE_FORM "<VAL>" +#define ARG_FILE_FORM "<FILE>" +#define ARG_DIR_FORM "<DIRECTORY>" +#define ARG_PORT_FORM "<PORT>" +#define ARG_ADDR_FORM "<ADDRESS>" +#define ARG_PREFIX_FORM "<NETADDR>/<PREFIX-LENGTH>" +#define ARG_NETW_FORM "<NETADDR>" +#define ARG_MASK_FORM "<NETMASK>" + + char *syntax; + char *help; + +}; + + +#define ARG_RESET "-" /* s-string preamble for call_option() to reset opt to default */ +#define ARG_RESET_CHAR '-' + + +enum opt_cmd { + // option handlers are registered, configured, and unregistered using primitives. + // for registration and unregistration the primitives OPT_REGISTER and OPT_UNREGISTER are used + // for configuration the primitives OPT_PATCH, OPT_ADJUST, OPT_CHECK, OPT_APPLY, OPT_SET_POST, OPT_POST are used + + + // called once for each option after registration + // Returns FAILURE or SUCCESS + OPT_REGISTER, + + + OPT_PATCH, + // opt values are configured by creating, extending, adjusting and finally testing and applying a patch + // option handler usually dont care about the creation of the patch. + // they get a complete patch which includes + // the option type-value pair and optional child type-valued pairs. + + + OPT_ADJUST, + // patched type-values pairs can be adjusted to a unified format before being checked or applied + // this has the following advantages: + // tracked and applied values are equal (different value notations can be adjusted to a unified format) + // -> track knows about already configured values (even when given with different notation) + // -> can prevent/warn reconfiguration of already configured values + // -> can reject resetting of non-configured values + + + OPT_CHECK, + // to test a given patch (type-value pair) ! + // Returns FAILURE or n>=0 of processed bytes-1 + + + OPT_APPLY, + // to apply a previously created and adjusted patch + // Returns FAILURE or n>=0 of processed bytes-1 + + + OPT_SET_POST, + // called whenever any option is changed and + // called ordered for each option and before next higher-order option + // Returns FAILURE or SUCCESS + + + OPT_POST, + // called whenever any option is changed and + // called ordered for each option after all options were (re-)set + // Returns FAILURE or SUCCESS + + + OPT_UNREGISTER + // called once before an option is unregistered + // Returns FAILURE or SUCCESS + +}; + +// evaluates global variable: "on_the_fly" +// fd may be set (>0) or not (=0) +// cmd==OPT_SET_POST / OPT_POST: +// s MBZ, return value is SUCCESS or FAILURE +int32_t call_option( uint8_t del, uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *parent, char *in, struct ctrl_node *cn ); + +int32_t check_apply_parent_option( uint8_t del, uint8_t cmd, uint8_t _save, struct opt_type *opt, char *in, struct ctrl_node *cn ); + +struct opt_type *get_option( struct opt_type *parent_opt, uint8_t short_opt, char *s ); + + + +void set_opt_child_val ( struct opt_child *c, char *val ); +struct opt_child * get_opt_child ( struct opt_type *opt, struct opt_parent *p ); + + +void del_opt_parent( struct opt_type *opt, struct opt_parent *parent ); +struct opt_parent * add_opt_parent( struct opt_type *opt ); + +void set_opt_parent_val ( struct opt_parent *p, char *val ); +struct opt_parent * get_opt_parent_val ( struct opt_type *opt, char *val ); +void set_opt_parent_ref ( struct opt_parent *p, char *ref ); +struct opt_parent * get_opt_parent_ref ( struct opt_type *opt, char *ref ); + + + +int8_t func_for_each_opt( struct ctrl_node *cn, void *data, char* func_name, + int8_t (*func) ( struct ctrl_node *cn, void *data, struct opt_type *opt, struct opt_parent *p, struct opt_child *c ) ); + +int respect_opt_order( uint8_t test, int8_t last, int8_t next, struct opt_type *on, uint8_t load_config, uint8_t cmd, struct ctrl_node *cn ); + +int8_t apply_stream_opts( char *s, uint8_t dryrun, uint8_t load_cfg, struct ctrl_node *cn ); + + +extern int (*load_config_cb) ( uint8_t test, struct opt_type *opt, struct ctrl_node *cn ); + +extern int (*save_config_cb) ( uint8_t del, struct opt_type *opt, char *parent, char *val, struct ctrl_node *cn ); + +extern int (*derive_config) ( char *reference, char *derivation, struct ctrl_node *cn ); + + +void register_option( struct opt_type *opt ); +//void remove_option( struct opt_type *opt ); +void register_options_array ( struct opt_type *fixed_options, int size ); + +extern int32_t Load_config; + + +int32_t get_tracked_network( struct opt_type *opt, struct opt_parent *patch, char *out, uint32_t *ip, int32_t *mask, struct ctrl_node *cn ); +int32_t adj_patched_network( struct opt_type *opt, struct opt_parent *patch, char *out, uint32_t *ip, int32_t *mask, struct ctrl_node *cn ); + + +void apply_init_args ( int argc, char *argv[] ); + +extern struct opt_type Patch_opt; + + +void init_control( void ); +void cleanup_control( void ); + +void cleanup_config( void ); + + +//char *debugWordDup( char* word, int32_t tag ); +//static void strchange( char *s, char i, char o ); +//char* nextword( char *s ); + + +//usefult tools: +char *ipStr( uint32_t addr ); +int8_t str2netw( char* args, uint32_t *ip, char delimiter, struct ctrl_node *cn, int32_t *val, int32_t max ); +void addr_to_str( uint32_t addr, char *str ); + +int8_t wordsEqual ( char *a, char *b ); +void wordCopy( char *out, char *in ); +uint32_t wordlen ( char *s ); +int32_t check_file( char *path, uint8_t write, uint8_t exec ); +int32_t check_dir( char *path, uint8_t create, uint8_t write ); +uint32_t validate_net_mask( uint32_t ip, uint32_t mask, struct ctrl_node *cn );
Added: trunk/bmx/cyassl/config.h =================================================================== --- trunk/bmx/cyassl/config.h (rev 0) +++ trunk/bmx/cyassl/config.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,73 @@ +/* ctaocrypt/include/config.h. Generated from config.h.in by configure. */ +/* ctaocrypt/include/config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define if you have POSIX threads libraries and header files. */ +#define HAVE_PTHREAD 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Name of package */ +#define PACKAGE "cyassl" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* The size of `long', as computed by sizeof. */ +#define SIZEOF_LONG 8 + +/* The size of `long long', as computed by sizeof. */ +#define SIZEOF_LONG_LONG 8 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "1.4.0" + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +/* #undef WORDS_BIGENDIAN */
Added: trunk/bmx/cyassl/misc.c =================================================================== --- trunk/bmx/cyassl/misc.c (rev 0) +++ trunk/bmx/cyassl/misc.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,128 @@ +/* misc.c + * + * Copyright (C) 2006-2009 Sawtooth Consulting Ltd. + * + * This file is part of CyaSSL. + * + * CyaSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * CyaSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + + +#include "misc.h" + +/* inlining these functions is a huge speed increase and a small size decrease, + because the functions are smaller than function call setup/cleanup, e.g., + md5 benchmark is twice as fast with inline. If you don't want it, then + define NO_INLINE and compile this file into cyassl, otherwise it's used as + a source header + */ + +#ifdef NO_INLINE + #define STATIC +#else + #define STATIC static +#endif + + +#ifdef INTEL_INTRINSICS + + #include <stdlib.h> /* get intrinsic definitions */ + + #pragma intrinsic(_lrotl, _lrotr) + + STATIC INLINE word32 rotlFixed(word32 x, word32 y) + { + return y ? _lrotl(x, y) : x; + } + + STATIC INLINE word32 rotrFixed(word32 x, word32 y) + { + return y ? _lrotr(x, y) : x; + } + +#else /* generic */ + + STATIC INLINE word32 rotlFixed(word32 x, word32 y) + { + return (x << y) | (x >> (sizeof(y) * 8 - y)); + } + + + STATIC INLINE word32 rotrFixed(word32 x, word32 y) + { + return (x >> y) | (x << (sizeof(y) * 8 - y)); + } + +#endif + + +STATIC INLINE word32 ByteReverseWord32(word32 value) +{ +#ifdef PPC_INTRINSICS + /* PPC: load reverse indexed instruction */ + return (word32)__lwbrx(&value,0); +#elif defined(FAST_ROTATE) + /* 5 instructions with rotate instruction, 9 without */ + return (rotrFixed(value, 8U) & 0xff00ff00) | + (rotlFixed(value, 8U) & 0x00ff00ff); +#else + /* 6 instructions with rotate instruction, 8 without */ + value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); + return rotlFixed(value, 16U); +#endif +} + + +STATIC INLINE void ByteReverseWords(word32* out, const word32* in, + word32 byteCount) +{ + word32 count = byteCount/sizeof(word32), i; + + for (i = 0; i < count; i++) + out[i] = ByteReverseWord32(in[i]); + +} + + +STATIC INLINE void ByteReverseBytes(byte* out, const byte* in, word32 byteCount) +{ + word32* op = (word32*)out; + const word32* ip = (const word32*)in; + + ByteReverseWords(op, ip, byteCount); +} + + +STATIC INLINE void XorWords(word* r, const word* a, word32 n) +{ + word32 i; + + for (i = 0; i < n; i++) r[i] ^= a[i]; +} + + +STATIC INLINE void xorbuf(byte* buf, const byte* mask, word32 count) +{ + if (((size_t)buf | (size_t)mask | count) % WORD_SIZE == 0) + XorWords( (word*)buf, (const word*)mask, count / WORD_SIZE); + else { + word32 i; + for (i = 0; i < count; i++) buf[i] ^= mask[i]; + } +} + + +#undef STATIC +
Added: trunk/bmx/cyassl/misc.h =================================================================== --- trunk/bmx/cyassl/misc.h (rev 0) +++ trunk/bmx/cyassl/misc.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,56 @@ +/* misc.h + * + * Copyright (C) 2006-2009 Sawtooth Consulting Ltd. + * + * This file is part of CyaSSL. + * + * CyaSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * CyaSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + + +#ifndef CTAO_CRYPT_MISC_H +#define CTAO_CRYPT_MISC_H + + +#include "types.h" +#include <string.h> + + + +#ifdef __cplusplus + extern "C" { +#endif + + +#ifdef NO_INLINE +word32 rotlFixed(word32, word32); +word32 rotrFixed(word32, word32); + +word32 ByteReverseWord32(word32); +void ByteReverseWords(word32*, const word32*, word32); +void ByteReverseBytes(byte*, const byte*, word32); + +void XorWords(word*, const word*, word32); +void xorbuf(byte*, const byte*, word32); +#endif /* NO_INLINE */ + + +#ifdef __cplusplus + } /* extern "C" */ +#endif + + +#endif /* CTAO_CRYPT_MISC_H */ +
Added: trunk/bmx/cyassl/sha.c =================================================================== --- trunk/bmx/cyassl/sha.c (rev 0) +++ trunk/bmx/cyassl/sha.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,208 @@ +/* sha.c + * + * Copyright (C) 2006-2009 Sawtooth Consulting Ltd. + * + * This file is part of CyaSSL. + * + * CyaSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * CyaSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + + +#include "sha.h" +#ifdef NO_INLINE + #include "misc.h" +#else + #include "misc.c" +#endif +#include <string.h> +#include <assert.h> + + +#ifndef min + + static INLINE word32 min(word32 a, word32 b) + { + return a > b ? b : a; + } + +#endif /* min */ + + +void InitSha(Sha* sha) +{ + sha->digest[0] = 0x67452301L; + sha->digest[1] = 0xEFCDAB89L; + sha->digest[2] = 0x98BADCFEL; + sha->digest[3] = 0x10325476L; + sha->digest[4] = 0xC3D2E1F0L; + + sha->buffLen = 0; + sha->loLen = 0; + sha->hiLen = 0; +} + + +#define f1(x,y,z) (z^(x &(y^z))) +#define f2(x,y,z) (x^y^z) +#define f3(x,y,z) ((x&y)|(z&(x|y))) +#define f4(x,y,z) (x^y^z) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+= f1(w,x,y) + W[i] + 0x5A827999+ \ + rotlFixed(v,5); w = rotlFixed(w,30); +#define R1(v,w,x,y,z,i) z+= f1(w,x,y) + W[i] + 0x5A827999+ \ + rotlFixed(v,5); w = rotlFixed(w,30); +#define R2(v,w,x,y,z,i) z+= f2(w,x,y) + W[i] + 0x6ED9EBA1+ \ + rotlFixed(v,5); w = rotlFixed(w,30); +#define R3(v,w,x,y,z,i) z+= f3(w,x,y) + W[i] + 0x8F1BBCDC+ \ + rotlFixed(v,5); w = rotlFixed(w,30); +#define R4(v,w,x,y,z,i) z+= f4(w,x,y) + W[i] + 0xCA62C1D6+ \ + rotlFixed(v,5); w = rotlFixed(w,30); + + +static void Transform(Sha* sha) +{ + word32 W[80], i; + + /* Copy context->state[] to working vars */ + word32 a = sha->digest[0]; + word32 b = sha->digest[1]; + word32 c = sha->digest[2]; + word32 d = sha->digest[3]; + word32 e = sha->digest[4]; + + for (i = 0; i < 16; i++) + W[i] = sha->buffer[i]; + + for (i = 16; i < 80; i++) + W[i] = rotlFixed(W[i-3]^W[i-8]^W[i-14]^W[i-16],1); + + /* 4 rounds of 20 operations each. */ + for (i = 0; i < 20; ) { + R0(a,b,c,d,e,i); i++; + R0(e,a,b,c,d,i); i++; + R0(d,e,a,b,c,i); i++; + R0(c,d,e,a,b,i); i++; + R0(b,c,d,e,a,i); i++; + } + + for (i = 20; i < 40; ) { + R2(a,b,c,d,e,i); i++; + R2(e,a,b,c,d,i); i++; + R2(d,e,a,b,c,i); i++; + R2(c,d,e,a,b,i); i++; + R2(b,c,d,e,a,i); i++; + } + + for (i = 40; i < 60; ) { + R3(a,b,c,d,e,i); i++; + R3(e,a,b,c,d,i); i++; + R3(d,e,a,b,c,i); i++; + R3(c,d,e,a,b,i); i++; + R3(b,c,d,e,a,i); i++; + } + + for (i = 60; i < 80; ) { + R4(a,b,c,d,e,i); i++; + R4(e,a,b,c,d,i); i++; + R4(d,e,a,b,c,i); i++; + R4(c,d,e,a,b,i); i++; + R4(b,c,d,e,a,i); i++; + } + + /* Add the working vars back into digest state[] */ + sha->digest[0] += a; + sha->digest[1] += b; + sha->digest[2] += c; + sha->digest[3] += d; + sha->digest[4] += e; +} + + +static INLINE void AddLength(Sha* sha, word32 len) +{ + word32 tmp = sha->loLen; + if ( (sha->loLen += len) < tmp) + sha->hiLen++; /* carry low to high */ +} + + +void ShaUpdate(Sha* sha, const byte* data, word32 len) +{ + /* do block size increments */ + byte* local = (byte*)sha->buffer; + + while (len) { + word32 add = min(len, SHA_BLOCK_SIZE - sha->buffLen); + memcpy(&local[sha->buffLen], data, add); + + sha->buffLen += add; + data += add; + len -= add; + + if (sha->buffLen == SHA_BLOCK_SIZE) { + #ifdef LITTLE_ENDIAN_ORDER + ByteReverseBytes(local, local, SHA_BLOCK_SIZE); + #endif + Transform(sha); + AddLength(sha, SHA_BLOCK_SIZE); + sha->buffLen = 0; + } + } +} + + +void ShaFinal(Sha* sha, byte* hash) +{ + byte* local = (byte*)sha->buffer; + + AddLength(sha, sha->buffLen); /* before adding pads */ + + local[sha->buffLen++] = 0x80; /* add 1 */ + + /* pad with zeros */ + if (sha->buffLen > SHA_PAD_SIZE) { + memset(&local[sha->buffLen], 0, SHA_BLOCK_SIZE - sha->buffLen); + sha->buffLen += SHA_BLOCK_SIZE - sha->buffLen; + + #ifdef LITTLE_ENDIAN_ORDER + ByteReverseBytes(local, local, SHA_BLOCK_SIZE); + #endif + Transform(sha); + sha->buffLen = 0; + } + memset(&local[sha->buffLen], 0, SHA_PAD_SIZE - sha->buffLen); + + /* put lengths in bits */ + sha->loLen = sha->loLen << 3; + sha->hiLen = (sha->loLen >> (8*sizeof(sha->loLen) - 3)) + + (sha->hiLen << 3); + + /* store lengths */ + #ifdef LITTLE_ENDIAN_ORDER + ByteReverseBytes(local, local, SHA_BLOCK_SIZE); + #endif + memcpy(&local[SHA_PAD_SIZE], &sha->hiLen, sizeof(word32)); + memcpy(&local[SHA_PAD_SIZE + sizeof(word32)], &sha->loLen, sizeof(word32)); + + Transform(sha); + #ifdef LITTLE_ENDIAN_ORDER + ByteReverseWords(sha->digest, sha->digest, SHA_DIGEST_SIZE); + #endif + memcpy(hash, sha->digest, SHA_DIGEST_SIZE); + + InitSha(sha); /* reset state */ +} +
Added: trunk/bmx/cyassl/sha.h =================================================================== --- trunk/bmx/cyassl/sha.h (rev 0) +++ trunk/bmx/cyassl/sha.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,62 @@ +/* sha.h + * + * Copyright (C) 2006-2009 Sawtooth Consulting Ltd. + * + * This file is part of CyaSSL. + * + * CyaSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * CyaSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + + +#ifndef CTAO_CRYPT_SHA_H +#define CTAO_CRYPT_SHA_H + +#include "types.h" + +#ifdef __cplusplus + extern "C" { +#endif + + + +/* in bytes */ +enum { + SHA_BLOCK_SIZE = 64, + SHA_DIGEST_SIZE = 20, + SHA_PAD_SIZE = 56 +}; + + +/* Sha digest */ +typedef struct Sha { + word32 buffLen; /* in bytes */ + word32 loLen; /* length in bytes */ + word32 hiLen; /* length in bytes */ + word32 digest[SHA_DIGEST_SIZE / sizeof(word32)]; + word32 buffer[SHA_BLOCK_SIZE / sizeof(word32)]; +} Sha; + + +void InitSha(Sha*); +void ShaUpdate(Sha*, const byte*, word32); +void ShaFinal(Sha*, byte*); + + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* CTAO_CRYPT_SHA_H */ +
Added: trunk/bmx/cyassl/types.h =================================================================== --- trunk/bmx/cyassl/types.h (rev 0) +++ trunk/bmx/cyassl/types.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,153 @@ +/* types.h + * + * Copyright (C) 2006-2009 Sawtooth Consulting Ltd. + * + * This file is part of CyaSSL. + * + * CyaSSL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * CyaSSL is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + + +#ifndef CTAO_CRYPT_TYPES_H +#define CTAO_CRYPT_TYPES_H + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#ifdef XMALLOC_USER + #include <stdlib.h> /* for size_t */ +#endif + +#ifdef __cplusplus + extern "C" { +#endif + + +#if defined(WORDS_BIGENDIAN) || (defined(__MWERKS__) && !defined(__INTEL__)) + #define BIG_ENDIAN_ORDER +#endif + +#ifndef BIG_ENDIAN_ORDER + #define LITTLE_ENDIAN_ORDER +#endif + +#ifdef IPHONE + #define SIZEOF_LONG_LONG 8 +#endif + +#ifdef THREADX + #define SIZEOF_LONG_LONG 8 +#endif + + +typedef unsigned char byte; +typedef unsigned short word16; +typedef unsigned int word32; + +#if defined(_MSC_VER) || defined(__BCPLUSPLUS__) + #define WORD64_AVAILABLE + #define WORD64_IS_DISTINCT_TYPE + typedef unsigned __int64 word64; +#elif SIZEOF_LONG == 8 + #define WORD64_AVAILABLE + typedef unsigned long word64; +#elif SIZEOF_LONG_LONG == 8 + #define WORD64_AVAILABLE + #define WORD64_IS_DISTINCT_TYPE + typedef unsigned long long word64; +#else + #define MP_16BIT /* for mp_int, mp_word needs to be twice as big as + mp_digit, no 64 bit type so make mp_digit 16 bit */ +#endif + + +/* These platforms have 64-bit CPU registers. */ +#if (defined(__alpha__) || defined(__ia64__) || defined(_ARCH_PPC64) || \ + defined(__mips64) || defined(__x86_64__)) + typedef word32 hword; + typedef word64 word; +#else + typedef word16 hword; + typedef word32 word; + #ifdef WORD64_AVAILABLE + typedef word64 dword; + #endif +#endif + + +enum { + WORD_SIZE = sizeof(word), + BIT_SIZE = 8, + WORD_BITS = WORD_SIZE * BIT_SIZE +}; + + +/* use inlining if compiler allows */ +#ifndef NO_INLINE + #ifdef _MSC_VER + #define INLINE __inline + #elif __GNUC__ + #define INLINE inline + #elif THREADX + #define INLINE _Inline + #else + #define INLINE + #endif +#else + #define INLINE +#endif + + +/* set up rotate style */ +#if defined(_MSC_VER) || defined(__BCPLUSPLUS__) + #define INTEL_INTRINSICS + #define FAST_ROTATE +#elif defined(__MWERKS__) && TARGET_CPU_PPC + #define PPC_INTRINSICS + #define FAST_ROTATE +#elif defined(__GNUC__) && defined(__i386__) + /* GCC does peephole optimizations which should result in using rotate + instructions */ + #define FAST_ROTATE +#endif + + +/* idea to add global alloc override by Moisés Guimarães */ +/* default to libc stuff */ +/* XCALLOC not used by CyaSSL or either math lib */ +/* XREALLOC is used once in mormal math lib, not in fast math lib */ +/* XFREE on some embeded systems doesn't like free(0) so test */ +#ifndef XMALLOC_USER + #define XMALLOC(s, h) malloc(s) + #define XFREE(p, h) if (p) free(p) + #define XREALLOC(p, n, h) realloc(p, n) + #define XCALLOC(n, s, h) calloc(n, s) +#else + /* prototypes for our heap functions */ + extern void *XMALLOC(size_t n, void* heap); + extern void *XREALLOC(void *p, size_t n, void* heap); + extern void *XCALLOC(size_t n, size_t s, void* heap); + extern void XFREE(void *p, void* heap); +#endif + + +#ifdef __cplusplus + } /* extern "C" */ +#endif + + +#endif /* CTAO_CRYPT_TYPES_H */ +
Added: trunk/bmx/hna.c =================================================================== --- trunk/bmx/hna.c (rev 0) +++ trunk/bmx/hna.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <asm/types.h> +#include <sys/socket.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> + + + +#include "bmx.h" +#include "msg.h" +#include "plugin.h" +#include "hna.h" +#include "route.h" + +AVL_TREE(global_uhna_tree, struct uhna4_node, key ); +AVL_TREE(local_uhna_tree, struct uhna4_node, key ); + +/* +struct uhna4_node *get_global_uhna_node(struct uhna4_key* key) +{ + return avl_find_item(&global_uhna_tree, key); +} +*/ + + +void set_uhna4_key(struct uhna4_key *key, uint8_t prefix_len, IP4_T glip4, uint32_t metric) +{ + memset( key, 0, sizeof(struct uhna4_key)); + key->prefix_len = prefix_len; + key->glip4 = glip4; + key->metric_be = htonl(metric); + +} + + + + + +int create_description_tlv_ip4(uint8_t *data, uint16_t max_size) +{ + struct avl_node *it = NULL; + struct dev_node *dev = primary_if; + int item = 0; + struct description0_msg_ip4 *glip4 = (struct description0_msg_ip4*) data; + + do { + if (item == 0 || (dev != primary_if && dev->announce)) { + + if ((item+1) * sizeof (struct description0_msg_ip4) > max_size) { + + dbgf(DBGL_SYS, DBGT_ERR, "unable to announce %s %s due to limiting --%s=%d", + dev->name, dev->ip4_str, ARG_UDPD_SIZE, max_size); + break; + } + + glip4[item++].ip4 = dev->ip4_addr; + } + + } while ((dev = avl_iterate_item(&dev_ip4_tree, &it))); + + return item * sizeof (struct description0_msg_ip4); +} + +int create_description_tlv_hna4(uint8_t *data, uint16_t max_size) +{ + struct avl_node *it = NULL; + struct dev_node *dev; + struct uhna4_node *un; + int item = 0; + struct description0_msg_hna4 *uhna4 = (struct description0_msg_hna4*) data; + + while ((un = avl_iterate_item(&local_uhna_tree, &it))) { + + if (!un->key.metric_be && un->key.prefix_len==32 && + (dev = avl_find_item(&dev_ip4_tree, &(un->key.glip4))) && dev->announce) + continue; + + if (item * sizeof (struct description0_msg_hna4) > max_size) { + + dbgf( DBGL_SYS, DBGT_ERR, "unable to announce %s/%d metric %d due to limiting --%s=%d", + ipStr(un->key.glip4), un->key.prefix_len, ntohl(un->key.metric_be), + ARG_UDPD_SIZE, max_size ); + continue; + } + + uhna4[item].ip4 = un->key.glip4; + uhna4[item].metric = un->key.metric_be; + uhna4[item].prefix_len = un->key.prefix_len; + item++; + } + + return item * sizeof (struct description0_msg_hna4); +} + + + +int process_description_tlv_hna4(struct orig_node *on, struct frame_header *tlv, IDM_T op, struct ctrl_node *cn ) +{ + struct description0_msg_ip4 *glip4 = NULL; + struct description0_msg_hna4 *uhna4 = NULL; + + assertion(-500357, (tlv->type == BMX_DSC_TLV_GLIP4 || tlv->type == BMX_DSC_TLV_UHNA4)); + + uint16_t msgs_size = ntohs(tlv->length) - sizeof (struct frame_header); + uint16_t msg_size, m, msgs; + + if (tlv->type == BMX_DSC_TLV_GLIP4) { + glip4 = (struct description0_msg_ip4 *) tlv->data; + msg_size = sizeof (struct description0_msg_ip4); + } else { + uhna4 = (struct description0_msg_hna4 *) tlv->data; + msg_size = sizeof (struct description0_msg_hna4); + } + + msgs = msgs_size / msg_size; + + for (m = 0; m < msgs; m++) { + struct uhna4_key key; + + if (glip4) { + set_uhna4_key(&key, 32, glip4[m].ip4, 0); + } else { + set_uhna4_key(&key, uhna4[m].prefix_len, uhna4[m].ip4, ntohl(uhna4[m].metric)); + } + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s %s %s/%d metric %d", + tlv_op_str[op], glip4 ? "glip4:" : "uhna4:", + ipStr(key.glip4), key.prefix_len, ntohl(key.metric_be)); + + if (op == TLV_DEL_TEST_ADD) { + + struct uhna4_node *un = avl_remove(&global_uhna_tree, &key, -300215); + assertion(-500358, (un && un->on == on)); + debugFree( un, -300161 ); + if (m == 0 && glip4) { + on->primary_ip4 = 0; + addr_to_str(0, on->primary_ip4_str); + } + + } else if (op == TLV_TEST) { + + if (avl_find(&global_uhna_tree, &key)) + return TLVS_BLOCKED; + + } else if (op == TLV_ADD) { + + struct uhna4_node *un = debugMalloc( sizeof(struct uhna4_node),-300162 ); + memset(un, 0, sizeof (struct uhna4_node)); + memcpy(&un->key, &key, sizeof ( struct uhna4_key)); + ASSERTION( -500359, (!avl_find(&global_uhna_tree, &key))); + un->on = on; + avl_insert(&global_uhna_tree, un, -300163); + + if (m == 0 && glip4) { + on->primary_ip4 = key.glip4; + addr_to_str(on->primary_ip4, on->primary_ip4_str); + } + + } else if ( op == TLV_DEBUG ) { + + dbg_printf(cn, " %s %s/%d metric %d\n", glip4 ? "glip4:" : "uhna4:", + ipStr(key.glip4), key.prefix_len, ntohl(key.metric_be) ); + + } else { + assertion( -500369, (NO)); + } + } + + return TLVS_SUCCESS; +} + + + + +void configure_hna ( IDM_T del, struct uhna4_key* key, struct orig_node *on ) { + + struct uhna4_node *un = avl_find_item( &global_uhna_tree, key ); + + paranoia( -500236, ((del && !un) || (!del && un)) ); + + // update uhna_tree: + if ( del ) { + + paranoia(-500234, (on != un->on)); + avl_remove(&global_uhna_tree, &un->key, -300212); + ASSERTION( -500233, (!avl_find( &global_uhna_tree, key)) ); // there should be only one element with this key + + if ( !on) + avl_remove(&local_uhna_tree, &un->key, -300213); + + } else { + + un = debugMalloc( sizeof (struct uhna4_node), -300090 ); + un->key = *key; + un->on = on; + avl_insert(&global_uhna_tree, un, -300149); + + if (!on) + avl_insert(&local_uhna_tree, un, -300150); + } + + if ( on ) { + + // update network routes: + if ( del) { + configure_route(key->glip4, key->prefix_len, ntohl(key->metric_be), + 0, my_orig_node.primary_ip4, + 0, 0, + RT_TABLE_NETWORKS, RTN_UNICAST, DEL, TRACK_OTHER_HNA); + } else { + ASSERTION(-500239, (avl_find( &link_dev_tree, &on->router_key))); + + configure_route(key->glip4, key->prefix_len, ntohl(key->metric_be), + on->router_key.llip4, my_orig_node.primary_ip4, + on->router_key.dev->index, on->router_key.dev->name, + RT_TABLE_NETWORKS, RTN_UNICAST, ADD, TRACK_OTHER_HNA); + } + + } else { + // update my description: + update_my_description_adv(); + + // update throw routes: + configure_route(key->glip4, key->prefix_len, 0, 0, 0, 0, "unknown", RT_TABLE_HOSTS, RTN_THROW, del, TRACK_MY_HNA); + configure_route(key->glip4, key->prefix_len, 0, 0, 0, 0, "unknown", RT_TABLE_NETWORKS, RTN_THROW, del, TRACK_MY_HNA); + configure_route(key->glip4, key->prefix_len, 0, 0, 0, 0, "unknown", RT_TABLE_TUNNEL, RTN_THROW, del, TRACK_MY_HNA); + } + + + if ( del) + debugFree(un, -300089); + +} + +STATIC_FUNC +int32_t opt_hna ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + uint32_t ip; + int32_t mask; + uint32_t metric = 0; + struct uhna4_key key; + + char new[30]; + + if ( cmd == OPT_ADJUST || cmd == OPT_CHECK || cmd == OPT_APPLY ) { + + dbgf( DBGL_CHANGES, DBGT_INFO, "diff %d cmd %s save %d opt %s patch %s", + patch->p_diff, opt_cmd2str[cmd], _save, opt->long_name, patch->p_val); + + + if ( patch->p_val[0] >= '0' && patch->p_val[0] <= '9' ) { + + // the unnamed UHNA + dbgf(DBGL_CHANGES, DBGT_INFO, "unnamed UHNA diff %d cmd %s save %d opt %s patch %s", + patch->p_diff, opt_cmd2str[cmd], _save, opt->long_name, patch->p_val); + + if ( str2netw( patch->p_val, &ip, '/', cn, &mask, 32 ) == FAILURE ) + return FAILURE; + + sprintf( new, "%s/%d", ipStr( validate_net_mask( ip, mask, 0 ) ), mask ); + set_opt_parent_val( patch, new ); + + if ( cmd == OPT_ADJUST ) + return SUCCESS; + + } else { + + // the named UHNA + + if ( adj_patched_network( opt, patch, new, &ip, &mask, cn ) == FAILURE ) + return FAILURE; + + if ( cmd == OPT_ADJUST ) + return SUCCESS; + + if ( patch->p_diff == NOP ) { + + // change network and netmask parameters of an already configured and named HNA + + char old[30]; + + // 1. check if announcing the new HNA would not block, + if ( check_apply_parent_option( ADD, OPT_CHECK, NO, opt, new, cn ) == FAILURE ) + return FAILURE; + + if ( get_tracked_network( opt, patch, old, &ip, &mask, cn ) == FAILURE ) + return FAILURE; + + // 3. remove the old HNA and hope to not mess it up... + set_uhna4_key(&key, mask, ip, metric); + + if ( cmd == OPT_APPLY ) + configure_hna(DEL, &key, NULL); + + + } + + // then continue with the new HNA + if ( str2netw( new , &ip, '/', cn, &mask, 32 ) == FAILURE ) + return FAILURE; + } + + set_uhna4_key(&key, mask, ip, metric); + + struct uhna4_node *un; + + if (patch->p_diff != DEL && (un = (avl_find_item(&global_uhna_tree, &key)))) { + + dbg_cn( cn, DBGL_CHANGES, DBGT_ERR, "UHNA %s/%d metric %d already blocked by %s !", + ipStr(ip), mask, metric, (un->on ? un->on->primary_ip4_str : "myself")); + + return FAILURE; + } + + if ( cmd == OPT_APPLY ) + configure_hna((patch->p_diff == DEL ? DEL : ADD), &key, NULL); + + + + } else if ( cmd == OPT_UNREGISTER ) { + + struct avl_node *an; + + while ((an = global_uhna_tree.root)) + configure_hna(DEL, (struct uhna4_key*) AVL_NODE_KEY( &global_uhna_tree, an), NULL); + + } + + return SUCCESS; + +} + +STATIC_FUNC +int32_t opt_show_hnas ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + + if ( cmd == OPT_APPLY ) { + + dbg_printf( cn, "unicast HNA metric Originator \n"); + + struct avl_node *an = NULL; + struct uhna4_node *un; + uint16_t hna_count = 0; + + while ((un = (struct uhna4_node*) ((an = avl_iterate(&global_uhna_tree, an)) ? an->item : NULL))) { + + paranoia(-500361, (un->on && !un->on->desc0)); + + dbg_printf(cn, "%15s/%-2d %10d %-15s %s \n", + ipStr(un->key.glip4), un->key.prefix_len, ntohl(un->key.metric_be), + un->on ? un->on->primary_ip4_str : "localhost", + un->on ? un->on->desc0->id.name : " "); + + process_description_tlvs(un->on, NULL, TLV_DEBUG, cn); + hna_count++; + } + + dbg_printf( cn, "\n" ); + } + return SUCCESS; +} + + + +STATIC_FUNC +struct opt_type hna_options[]= { +// ord parent long_name shrt Attributes *ival min max default *function + + {ODI,0,0, 0, 5,0,0,0,0,0, 0, 0, 0, 0, 0, + 0, "\nHost and Network Announcement (HNA) options:"}, + + {ODI,0,ARG_UHNA, 'u',5,A_PMN,A_ADM,A_DYI,A_CFA,A_ANY, 0, 0, 0, 0, opt_hna, + ARG_PREFIX_FORM,"perform host-network announcement (HNA) for defined ip range"}, + + {ODI,ARG_UHNA,ARG_NETW, 'n',5,A_CS1,A_ADM,A_DYI,A_CFA,A_ANY, 0, 0, 0, 0, opt_hna, + ARG_NETW_FORM, "specify network of announcement"}, + + {ODI,ARG_UHNA,ARG_MASK, 'm',5,A_CS1,A_ADM,A_DYI,A_CFA,A_ANY, 0, 0, 0, 0, opt_hna, + ARG_MASK_FORM, "specify network prefix of announcement"}, + + + {ODI,0,ARG_HNAS, 0, 5,A_PS0,A_USR,A_DYN,A_ARG,A_ANY, 0, 0, 0, 0, opt_show_hnas, + 0, "show HNAs of other nodes\n"} + +}; + + + + +STATIC_FUNC +void hna_cleanup( void ) { + +} + + +STATIC_FUNC +int32_t hna_init( void ) { + + register_options_array( hna_options, sizeof( hna_options ) ); + + return SUCCESS; +} + + + +struct plugin_v2 *hna_get_plugin_v2( void ) { + + static struct plugin_v2 hna_plugin; + memset( &hna_plugin, 0, sizeof ( struct plugin_v2 ) ); + + hna_plugin.plugin_version = PLUGIN_VERSION_02; + hna_plugin.plugin_name = "bmx_hna_plugin"; + hna_plugin.plugin_size = sizeof ( struct plugin_v2 ); + hna_plugin.plugin_bmx_revision = REVISION_VERSION; + hna_plugin.plugin_bmx_version = SOURCE_VERSION; + hna_plugin.cb_init = hna_init; + hna_plugin.cb_cleanup = hna_cleanup; + + return &hna_plugin; +} + +
Added: trunk/bmx/hna.h =================================================================== --- trunk/bmx/hna.h (rev 0) +++ trunk/bmx/hna.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#define ARG_UHNA "unicast_hna" +#define ARG_HNAS "uhnas" + +struct uhna4_key { + uint8_t reserved; + uint8_t prefix_len; + IP4_T glip4; + uint32_t metric_be; +}; + +struct uhna4_node { + struct uhna4_key key; + struct orig_node *on; +}; + +extern struct avl_tree global_uhna_tree; +extern struct avl_tree local_uhna_tree; + +struct plugin_v2 *hna_get_plugin_v2( void ); + +void set_uhna4_key(struct uhna4_key *key, uint8_t prefix_len, IP4_T glip4, uint32_t metric); + +int create_description_tlv_ip4(uint8_t *data, uint16_t max_size); +int create_description_tlv_hna4(uint8_t *data, uint16_t max_size); + +int process_description_tlv_hna4(struct orig_node *on, struct frame_header *tlv, IDM_T op, struct ctrl_node *cn ); + + +//struct uhna4_node *get_global_uhna_node( struct uhna4_key* key ); +
Added: trunk/bmx/iid.c =================================================================== --- trunk/bmx/iid.c (rev 0) +++ trunk/bmx/iid.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include <string.h> +#include <stdio.h> + +#include "bmx.h" +#include "iid.h" + +struct iid_repos my_iid_repos = { 0,0,0,0,{NULL} }; + +int8_t iid_extend_repos(struct iid_repos *rep) +{ + dbgf( DBGL_CHANGES, DBGT_INFO, "sizeof iid: %lu, tot_used %d arr_size %d ", + (rep == &my_iid_repos) ? sizeof (IID_NODE_T*) : sizeof (IID_T), rep->tot_used, rep->arr_size); + + paranoia(-500217, (rep == &my_iid_repos && rep->tot_used != rep->arr_size)); + + if (rep->arr_size + IID_REPOS_SIZE_BLOCK >= IID_REPOS_SIZE_WARN) { + + dbgf( DBGL_SYS, DBGT_WARN, "%d", rep->arr_size); + + if (rep->arr_size + IID_REPOS_SIZE_BLOCK >= IID_REPOS_SIZE_MAX) + return FAILURE; + } + + int field_size = (rep == &my_iid_repos) ? sizeof (IID_NODE_T*) : sizeof (struct iid_ref); + + if (rep->arr_size) { + + rep->arr.u8 = debugRealloc(rep->arr.u8, (rep->arr_size + IID_REPOS_SIZE_BLOCK) * field_size, -300035); + + } else { + + rep->arr.u8 = debugMalloc(IID_REPOS_SIZE_BLOCK * field_size, -300085); + rep->tot_used = IID_RSVD_MAX+1; + rep->min_free = IID_RSVD_MAX+1; + rep->max_free = IID_RSVD_MAX+1; + } + + memset(&(rep->arr.u8[rep->arr_size * field_size]), 0, IID_REPOS_SIZE_BLOCK * field_size); + + rep->arr_size += IID_REPOS_SIZE_BLOCK; + + return SUCCESS; +} + + +void iid_purge_repos( struct iid_repos *rep ) +{ + + if (rep->arr.u8) + debugFree(rep->arr.u8, -300135); + + memset(rep, 0, sizeof ( struct iid_repos)); + +} + +void iid_free(struct iid_repos *rep, IID_T iid) +{ + int m = (rep == &my_iid_repos); + + assertion(-500330, (iid > IID_RSVD_MAX)); + assertion(-500228, (iid < rep->arr_size && iid < rep->max_free && rep->tot_used > IID_RSVD_MAX)); + assertion(-500229, ((m ? (rep->arr.node[iid] != NULL) : (rep->arr.ref[iid].myIID4x) != 0))); + + if (m) { + rep->arr.node[iid] = NULL; + } else { + rep->arr.ref[iid].myIID4x = 0; + rep->arr.ref[iid].referred_timestamp_sec = 0; + } + + rep->min_free = MIN(rep->min_free, iid); + + if (rep->max_free == iid + 1) { + + IID_T i; + + for (i = iid; i > IID_MIN_USED; i--) { + + if (m ? (rep->arr.node[i - 1] != NULL) : (rep->arr.ref[i - 1].myIID4x) != 0) + break; + } + + rep->max_free = i; + } + + rep->tot_used--; + + dbgf(DBGL_CHANGES, DBGT_INFO, "mine %d, iid %d tot_used %d, min_free %d max_free %d", + m, iid, rep->tot_used, rep->min_free, rep->max_free); + + if (rep->tot_used > 0 && rep->tot_used <= IID_MIN_USED) { + + assertion(-500362, (rep->tot_used == IID_MIN_USED && rep->max_free == IID_MIN_USED && rep->min_free == IID_MIN_USED)); + + iid_purge_repos( rep ); + + } + +} + + + +IID_NODE_T* iid_get_node_by_myIID4x( IID_T myIID4x ) { + + if ( my_iid_repos.max_free <= myIID4x ) + return NULL; + + IID_NODE_T *dhn = my_iid_repos.arr.node[myIID4x]; + + assertion(-500328, (!dhn || dhn->myIID4orig == myIID4x)); + + if (dhn) { + + if (!dhn->on) { + dbgf(DBGL_CHANGES, DBGT_INFO, "myIID4x %d INVALIDATED %d sec ago", + myIID4x, (bmx_time - dhn->referred_timestamp) / 1000); + } + + dhn->referred_timestamp = bmx_time; + } + + + return dhn; +} + + +IID_NODE_T* iid_get_node_by_neighIID4x( IID_NEIGH_T *nn, IID_T neighIID4x ) +{ + + if (!nn || nn->neighIID4x_repos.max_free <= neighIID4x) + return NULL; + + struct iid_ref *ref = &(nn->neighIID4x_repos.arr.ref[neighIID4x]); + + + if (ref->myIID4x && ((((uint16_t) bmx_time_sec) - ref->referred_timestamp_sec) <= + ((MIN_DHASH_TO - (MIN_DHASH_TO / DHASH_TO_TOLERANCE_FK)) / 1000))) { + + ref->referred_timestamp_sec = bmx_time_sec; + + return iid_get_node_by_myIID4x(ref->myIID4x); + } + + return NULL; +} + + + +int8_t iid_set_neighIID4x(struct iid_repos *neigh_rep, IID_T neighIID4x, IID_T myIID4x) +{ + assertion(-500326, (neighIID4x > IID_RSVD_MAX)); + assertion(-500327, (myIID4x > IID_RSVD_MAX)); + assertion(-500384, (neigh_rep && neigh_rep != &my_iid_repos)); + + assertion(-500245, (my_iid_repos.max_free > myIID4x)); + + IID_NODE_T *dhn = my_iid_repos.arr.node[myIID4x]; + + assertion(-500485, (dhn && dhn->on)); + + dhn->referred_timestamp = bmx_time; + + if (neigh_rep->max_free > neighIID4x) { + + struct iid_ref *ref = &(neigh_rep->arr.ref[neighIID4x]); + + if (ref->myIID4x > IID_RSVD_MAX) { + + if (ref->myIID4x == myIID4x || + (((uint16_t)(((uint16_t) bmx_time_sec) - ref->referred_timestamp_sec)) >= + ((MIN_DHASH_TO - (MIN_DHASH_TO / DHASH_TO_TOLERANCE_FK)) / 1000))) { + + neigh_rep->arr.ref[neighIID4x].myIID4x = myIID4x; + neigh_rep->arr.ref[neighIID4x].referred_timestamp_sec = bmx_time_sec; + return SUCCESS; + } + + dbgf(DBGL_SYS, DBGT_ERR, "neighIID4x %d for %s changed (at sec %d ) faster than allowed!!", + neighIID4x, dhn->on->id.name, ref->referred_timestamp_sec); + + return FAILURE; + } + + assertion(-500242, (ref->myIID4x == IID_RSVD_UNUSED)); + } + + + while (neigh_rep->arr_size <= neighIID4x) { + + if (neigh_rep->tot_used < neigh_rep->arr_size / IID_REPOS_USAGE_WARNING) { + dbgf(DBGL_SYS, DBGT_WARN, "IID_REPOS_USAGE_WARNING did %d sid %d arr_size %d used %d", + neighIID4x, myIID4x, neigh_rep->arr_size, neigh_rep->tot_used ); + } + + iid_extend_repos(neigh_rep); + } + + assertion(-500243, ((neigh_rep->arr_size > neighIID4x && + (neigh_rep->max_free <= neighIID4x || neigh_rep->arr.ref[neighIID4x].myIID4x == IID_RSVD_UNUSED)))); + + neigh_rep->tot_used++; + neigh_rep->max_free = MAX( neigh_rep->max_free, neighIID4x+1 ); + + IID_T min = neigh_rep->min_free; + + if (min == neighIID4x) { + for (; min < neigh_rep->arr_size && neigh_rep->arr.ref[min].myIID4x; min++); + } + + paranoia(-500244, (min > neigh_rep->max_free)); + + neigh_rep->min_free = min; + + neigh_rep->arr.ref[neighIID4x].myIID4x = myIID4x; + neigh_rep->arr.ref[neighIID4x].referred_timestamp_sec = bmx_time_sec; + + return SUCCESS; +} + +void iid_free_neighIID4x_by_myIID4x( struct iid_repos *rep, IID_T myIID4x) +{ + assertion(-500282, (rep != &my_iid_repos)); + assertion(-500328, (myIID4x > IID_RSVD_MAX)); + + IID_T p; + + for (p = IID_RSVD_MAX + 1; p < rep->max_free && rep->arr.ref[p].myIID4x != myIID4x; p++); + + if (p < rep->max_free && rep->arr.ref[p].myIID4x == myIID4x) { + + dbgf(DBGL_CHANGES, DBGT_INFO, "removed stale rep->arr.sid[%d] = %d", p, myIID4x); + + iid_free(rep, p); + } +} + +IID_T iid_new_myIID4x(IID_NODE_T *dhn) +{ + + paranoia( -500216, ( my_iid_repos.tot_used > my_iid_repos.arr_size ) ); + + while ( my_iid_repos.tot_used >= my_iid_repos.arr_size ) + iid_extend_repos( &my_iid_repos ); + + IID_T mid = my_iid_repos.min_free; + IID_T pos = mid + 1; + + + my_iid_repos.tot_used++; + my_iid_repos.arr.node[mid] = dhn; + + for (; pos < my_iid_repos.arr_size && (my_iid_repos.arr.node[pos]); pos++); + + my_iid_repos.min_free = pos; + my_iid_repos.max_free = MAX(pos, my_iid_repos.max_free); + + dbgf(DBGL_CHANGES, DBGT_INFO, "mine %d, iid %d tot_used %d, min_free %d max_free %d", + 1, mid, my_iid_repos.tot_used, my_iid_repos.min_free, my_iid_repos.max_free); + + dhn->referred_timestamp = bmx_time; + + return mid; +} +
Added: trunk/bmx/iid.h =================================================================== --- trunk/bmx/iid.h (rev 0) +++ trunk/bmx/iid.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _IID_H +#define _IID_H + +#define IID_T uint16_t + +#define IID_NEIGH_T struct neigh_node +extern IID_NEIGH_T *unused1; // ensure knowledge of this type + +#define IID_NODE_T struct dhash_node +extern IID_NODE_T *unused2; // ensure knowledge of this type + +#define IID_REPOS_SIZE_BLOCK 2 + +#define IID_REPOS_SIZE_MAX ((IID_T)(-1)) +#define IID_REPOS_SIZE_WARN 1024 +#define IID_REPOS_USAGE_WARNING 10 + +#define IID_RSVD_UNUSED 0 +#define IID_RSVD_4YOU 1 +#define IID_RSVD_MAX 1 +#define IID_MIN_USED 2 + + +struct iid_ref { + IID_T myIID4x; + uint16_t referred_timestamp_sec; +}; + +struct iid_repos { + IID_T arr_size; // the number of allocated array fields + IID_T min_free; // the first unused array field from the beginning of the array (might be outside of allocated space) + IID_T max_free; // the first unused array field after the last used field in the array (might be outside of allocated space) + IID_T tot_used; // the total number of used fields in the array + union { + uint8_t *u8; + IID_NODE_T **node; + struct iid_ref *ref; + } arr; +}; + +extern struct iid_repos my_iid_repos; + + + +int8_t iid_extend_repos( struct iid_repos *rep ); + +void iid_purge_repos( struct iid_repos *rep ); + +void iid_free(struct iid_repos *rep, IID_T iid); + +void iid_free_neighIID4x_by_myIID4x( struct iid_repos *rep, IID_T myIID4x); + +int8_t iid_set_neighIID4x(struct iid_repos *neigh_rep, IID_T neighIID4x, IID_T myIID4x); + +IID_T iid_new_myIID4x( IID_NODE_T *dhn ); + + +IID_NODE_T* iid_get_node_by_neighIID4x( IID_NEIGH_T *nn, IID_T neighIID4x ); + + +IID_NODE_T* iid_get_node_by_myIID4x( IID_T myIID4x ); + +#endif
Added: trunk/bmx/lib/Makefile =================================================================== --- trunk/bmx/lib/Makefile (rev 0) +++ trunk/bmx/lib/Makefile 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,11 @@ + +CFLAGS += +LDFLAGS += + +PLUGINS = $(shell find -maxdepth 1 -type d | grep -e '^./bmx_' | sort) + +%: + for d in $(PLUGINS); do $(MAKE) CORE_CFLAGS='$(CORE_CFLAGS)' -C $$d $@ || echo compiling $$d failed; echo;echo; done + echo 2 flags: $(CFLAGS) + +
Added: trunk/bmx/lib/bmx_uci_config/Makefile =================================================================== --- trunk/bmx/lib/bmx_uci_config/Makefile (rev 0) +++ trunk/bmx/lib/bmx_uci_config/Makefile 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,39 @@ + +CFLAGS += $(CORE_CFLAGS) -fpic -I../../ +LDFLAGS += -shared -luci +#-Wl,-soname,bmx_config + +PLUGIN_NAME = bmx_uci_config + +SRC_C = uci_config.c +SRC_H = uci_config.h +OBJS= $(SRC_C:.c=.o) + +PLUGIN_FULLNAME = $(PLUGIN_NAME).so +PLUGIN_SHORTNAME = $(PLUGIN_NAME).so + +LIBDIR = /usr/lib +THISDIR = $(shell pwd ) + +all: $(PLUGIN_FULLNAME) Makefile + + +$(PLUGIN_FULLNAME): $(OBJS) Makefile + $(CC) $(LDFLAGS) $(EXTRA_LDFLAGS) $(OBJS) -o $(PLUGIN_FULLNAME) + ln -f -s $(THISDIR)/$(PLUGIN_FULLNAME) $(THISDIR)/../$(PLUGIN_FULLNAME) + +%.o: %.c %.h Makefile + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c $< -o $@ + + +clean: + rm -f *.o *.so.$(PLUGIN_VER) + + +install: all + mkdir -p $(LIBDIR) + install -D -m 755 $(PLUGIN_FULLNAME) $(LIBDIR)/$(PLUGIN_FULLNAME); /sbin/ldconfig -n $(LIBDIR) + + +strip: all + strip $(PLUGIN_FULLNAME)
Added: trunk/bmx/lib/bmx_uci_config/uci_config.c =================================================================== --- trunk/bmx/lib/bmx_uci_config/uci_config.c (rev 0) +++ trunk/bmx/lib/bmx_uci_config/uci_config.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,923 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include <strings.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <signal.h> +#include <dlfcn.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <uci.h> + +#include "../../bmx.h" +#include "../../plugin.h" +#include "uci_config.h" + +static char conf_path[MAX_PATH_SIZE] = ""; +static char *bmx_conf_name = NULL; + +static char *uci_err; + +static struct uci_context *bmx_ctx = NULL; +static struct uci_ptr bmx_pptr; + +static struct uci_context *net_ctx = NULL; + +static struct opt_type tmp_conf_opt; + + +static void signal_hup_handler( int32_t sig ) { + + dbgf( DBGL_SYS, DBGT_INFO, "reloading config" ); + + struct ctrl_node *cn = create_ctrl_node( STDOUT_FILENO, NULL, YES/*we are root*/ ); + + if ( (apply_stream_opts( ARG_RELOAD_CONFIG, OPT_CHECK, NO/*no cfg by default*/, cn ) == FAILURE) || + (apply_stream_opts( ARG_RELOAD_CONFIG, OPT_APPLY, NO/*no cfg by default*/, cn ) == FAILURE) ) + { + close_ctrl_node( CTRL_CLOSE_STRAIGHT, cn ); + dbg( DBGL_SYS, DBGT_ERR, "reloading config failed! FIX your config NOW!" ); + return; + } + + close_ctrl_node( CTRL_CLOSE_STRAIGHT, cn ); + + respect_opt_order( OPT_APPLY, 0, 99, NULL, NO/*load_cofig*/, OPT_POST, 0/*probably closed*/ ); + + cb_plugin_hooks( NULL, PLUGIN_CB_CONF ); + +} + + + +STATIC_FUNC +int8_t uci_reload_package( struct uci_context *ctx, struct uci_ptr *ptr, char* package ) { + + uci_unload(ctx, ptr->p); + + memset( ptr, 0, sizeof( struct uci_ptr ) ); + ptr->package = package; + + if ( uci_lookup_ptr( ctx, ptr, NULL, false) != SUCCESS ) { + uci_get_errorstr( ctx, &uci_err, "" ); + dbgf( DBGL_SYS, DBGT_ERR, "%s", uci_err ); + return FAILURE; + } + + return SUCCESS; +} + + + +STATIC_FUNC +struct uci_element *uci_lookup( struct uci_context *ctx, struct uci_ptr *ptr, char *name ) { + + dbgf_all( DBGT_INFO, "%s", name ); + + if ( name ) + memset( ptr, 0, sizeof( struct uci_ptr ) ); + + if ( uci_lookup_ptr( ctx, ptr, name, false) != SUCCESS ) { + uci_get_errorstr( ctx, &uci_err, "" ); + dbgf( DBGL_SYS, DBGT_ERR, "%s %s", name, uci_err ); + return NULL; + } + struct uci_element *e = ptr->last; + + if ( !( ptr->flags & UCI_LOOKUP_COMPLETE ) ) { + dbgf_all( DBGT_INFO, "%s %s %s %s is not configured", + name, ptr->package, ptr->section, ptr->option ); + return NULL; + } + + return e; +} + + + +STATIC_FUNC +int uci_save_option( struct uci_context *ctx, char *conf_name, char *sect_name, char *opt_name, char *opt_val, struct ctrl_node *cn ) { + + dbgf_cn( cn, DBGL_CHANGES, DBGT_INFO, "%s.%s.%s=%s", + conf_name, sect_name, opt_name, opt_val ); + + struct uci_ptr ptr; + memset(&ptr, 0, sizeof(ptr)); + ptr.package = conf_name; + ptr.section = sect_name; + + if ( uci_lookup_ptr( ctx, &ptr, NULL, false ) != SUCCESS ) { + uci_get_errorstr( ctx, &uci_err, "" ); + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "uci_lookup_str( %s.%s ): %s", + conf_name, sect_name, uci_err ); + return FAILURE; + } + + ptr.option = opt_name; + ptr.value = opt_val; + + if ( uci_set( ctx, &ptr ) != SUCCESS ) { + uci_get_errorstr( ctx, &uci_err, "" ); + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "uci_set( %s.%s.%s=%s ): %s", + conf_name, sect_name, opt_name, opt_val, uci_err ); + return FAILURE; + } + + if ( uci_save( ctx, ptr.p ) != SUCCESS ) { + uci_get_errorstr( ctx, &uci_err, "" ); + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "uci_save( %s.%s.%s=%s ): %s", + conf_name, sect_name, opt_name, opt_val, uci_err ); + return FAILURE; + } + + return SUCCESS; +} + + + +STATIC_FUNC +int uci_create_section( struct uci_context *ctx, char *conf_name, char *sect_name, char *opt_name, struct ctrl_node *cn ) { + + + struct uci_ptr ptr; + + memset(&ptr, 0, sizeof(ptr)); + ptr.package = conf_name; + + if ( wordlen( sect_name ) ) + ptr.section = sect_name; + + + if ( uci_lookup_ptr( ctx, &ptr, NULL, false ) != SUCCESS ) { + uci_get_errorstr( ctx, &uci_err, "" ); + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "%s %s uci_lookup_str(): %s", + conf_name, opt_name, uci_err ); + return FAILURE; + } + + if ( uci_add_section( ctx, ptr.p, opt_name, &ptr.s ) != SUCCESS ) { + uci_get_errorstr( ctx, &uci_err, "" ); + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "%s %s uci_add_section(): %s", + conf_name, opt_name, uci_err ); + return FAILURE; + } + + /* + if ( uci_save( ctx, ptr.p ) != SUCCESS ) { + uci_get_errorstr( ctx, &uci_err, "" ); + dbgf_fd( fd, DBGL_SYS, DBGT_ERR, "uci_save(): %s", uci_err ); + return FAILURE; + } + */ + + if ( !wordlen( sect_name ) ) + strcpy( sect_name, ptr.s->e.name ); + + dbgf_cn( cn, DBGL_CHANGES, DBGT_INFO, "%s.%s=%s", + conf_name, ptr.s->e.name, opt_name ); + + return SUCCESS; +} + + + +STATIC_FUNC +int uci_remove( struct uci_context *ctx, char *conf_name, char *sect_name, char *opt_name, struct ctrl_node *cn ) { + + dbgf_cn( cn, DBGL_CHANGES, DBGT_INFO, "%s.%s %s", conf_name, sect_name, opt_name ); + + struct uci_ptr ptr; + + memset(&ptr, 0, sizeof(ptr)); + ptr.package = conf_name; + ptr.section = sect_name; + + if ( uci_lookup_ptr( ctx, &ptr, NULL, false ) != SUCCESS ) { + uci_get_errorstr( ctx, &uci_err, "" ); + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "uci_lookup_str(): %s", uci_err ); + return FAILURE; + } + + ptr.option = opt_name; + + if ( uci_delete( ctx, &ptr ) != SUCCESS ) { + uci_get_errorstr( ctx, &uci_err, "" ); + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "uci_delete(): %s", uci_err ); + return FAILURE; + } + + if ( uci_save( ctx, ptr.p ) != SUCCESS ) { + uci_get_errorstr( ctx, &uci_err, "" ); + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "uci_save(): %s", uci_err ); + return FAILURE; + } + + // without doing this we get double free or corruption after config-reload or uci_remove() + uci_reload_package( ctx, &bmx_pptr, conf_name ); + + return SUCCESS; + +} + + + +STATIC_FUNC +int uci_get_sect_name( uint8_t create, struct ctrl_node *cn, struct uci_context *ctx, + char *conf_name, char *sect_name, char *sect_type, char *opt_name, char *opt_val ) +{ + + struct uci_element *e; + struct uci_element *se; + int found=0; + struct uci_ptr ptr; + + dbgf_all( DBGT_INFO, "%s %s %s.*.%s==%s", + create?"create":"get", sect_name, conf_name, opt_name, opt_val ); + + paranoia( -500020, ( !conf_name || !sect_type || !sect_name ) ); + + uint8_t named_section = wordlen(sect_name) ? YES : NO; + + + if ( !(e=uci_lookup( ctx, &ptr, conf_name )) ) + return FAILURE; + + if ( e->type != UCI_TYPE_PACKAGE ) + return FAILURE; + + + uci_foreach_element( &(ptr.p->sections), se) { + + struct uci_section *s = uci_to_section(se); + struct uci_ptr sptr; + char name[MAX_ARG_SIZE]; + + if ( strcmp( sect_type, s->type ) ) + continue; + + if ( opt_name ) { + + sprintf( name, "%s.%s.%s", conf_name, s->e.name, opt_name ); + + if ( !(e=uci_lookup( ctx, &sptr, name )) ) + continue; + + if ( opt_val && !wordsEqual( sptr.o->v.string, opt_val ) ) + continue; + + } + + if ( !found && !named_section ) + strcpy( sect_name, s->e.name ); + + else if ( wordsEqual( sect_name, s->e.name ) ) + return SUCCESS; + + found++; + } + + + if ( found == 0 && create ) { + + if ( named_section && uci_save_option( ctx, conf_name, sect_name, NULL, sect_type, cn ) != SUCCESS ) + return FAILURE; + + if ( !named_section && uci_create_section( ctx, conf_name, sect_name, sect_type, cn ) != SUCCESS ) + return FAILURE; + + if ( opt_name && opt_val ) { + + if ( uci_save_option( ctx, conf_name, sect_name, opt_name, opt_val, cn ) == SUCCESS ) + return SUCCESS; + + return FAILURE; + + } else { + + return SUCCESS; + } + + + return FAILURE; + + } else if ( found == 1 ) { + + return SUCCESS; + + } else { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, + "Found %d matching section with %s.*.%s==%s ! FIX your config NOW!", + found, conf_name, opt_name, opt_val ); + + return FAILURE; + } + +} + + +STATIC_FUNC +int bmx_derive_config ( char *reference, char *derivation, struct ctrl_node *cn ) { + + struct uci_ptr sptr; + char name[MAX_ARG_SIZE]; + struct uci_element *e; + + wordCopy( name, reference + strlen( REFERENCE_KEY_WORD ) ); + + dbgf_all( DBGT_INFO, "going to lookup %s", name ); + + if ( !(e=uci_lookup( net_ctx, &sptr, name )) ) + return FAILURE; + + if ( sptr.o && wordlen( sptr.o->v.string ) && wordlen( sptr.o->v.string ) < MAX_ARG_SIZE ) + wordCopy( derivation, sptr.o->v.string ); + else + return FAILURE; + + uci_unload(net_ctx, sptr.p); + + return SUCCESS; +} + + +STATIC_FUNC +int bmx_save_config ( uint8_t del, struct opt_type *opt, char *p_val, char *c_val, struct ctrl_node *cn ) { + + dbgf( DBGL_CHANGES, DBGT_INFO, "%s p:%s c:%s", opt->long_name, p_val, c_val ); + + char sect_name[MAX_ARG_SIZE]=""; + + paranoia( -500102, !opt ); + + if ( !bmx_ctx || !bmx_conf_name || opt->cfg_t == A_ARG ) + return SUCCESS; + + if ( opt->opt_t == A_PS1 ) { + + // for all general options like ogm_interval, dad_timeout, ... + + if ( del ) { + + return uci_remove( bmx_ctx, bmx_conf_name, DEF_SECT_NAME, opt->long_name, cn ); + + } else { + + uci_get_sect_name( YES/*create*/, cn, bmx_ctx, bmx_conf_name, DEF_SECT_NAME, DEF_SECT_TYPE, NULL, NULL ); + return uci_save_option( bmx_ctx, bmx_conf_name, DEF_SECT_NAME, opt->long_name, c_val, cn ); + } + + } else if ( opt->opt_t == A_PMN ) { + + // all A_PMN-options are saved as sections + // some with only one argument like HNAs, throw-rule, plugin, service + // section->options are processed in the following block + + if ( uci_get_sect_name( ( del ? NO : YES/*create*/ ), + cn, bmx_ctx, + bmx_conf_name, sect_name, opt->long_name, + opt->long_name, p_val ) == FAILURE ) + { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "unknown sect_name %s for %s %s", + sect_name, opt->long_name, p_val ); + + if ( del ) + return SUCCESS; + + return FAILURE; + } + + if ( del ) + return uci_remove( bmx_ctx, bmx_conf_name, sect_name, NULL, cn ); + + else + return uci_save_option( bmx_ctx, bmx_conf_name, sect_name, opt->long_name, c_val, cn ); + + + } else if ( opt->opt_t == A_CS1 && p_val ) { + + // all A_CS1-child options like /ttl=20, /hide=1 from --dev eth0 + + if ( uci_get_sect_name( NO/*create*/, cn, bmx_ctx, + bmx_conf_name, sect_name, opt->d.parent_opt->long_name, + opt->d.parent_opt->long_name, p_val ) == FAILURE ) + { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, + "unknown A_1 sect_name %s sn %s %s on %s %s", + sect_name, opt->d.parent_opt->long_name, p_val, opt->long_name, c_val ); + + if ( del ) + return SUCCESS; + + sect_name[0]=0; + if ( uci_get_sect_name( YES/*create*/, cn, bmx_ctx, + bmx_conf_name, sect_name, opt->d.parent_opt->long_name, + opt->d.parent_opt->long_name, p_val ) == FAILURE ) + return FAILURE; + + } + + if ( del ) + return uci_remove( bmx_ctx, bmx_conf_name, sect_name, opt->long_name, cn ); + + else + return uci_save_option( bmx_ctx, bmx_conf_name, sect_name, opt->long_name, c_val, cn ); + + + } else { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "%20s %20s -- %20s %s", + opt->d.parent_opt?opt->d.parent_opt->long_name:"--", p_val, opt->long_name, c_val ); + + cleanup_all( -501004 ); + } + + return SUCCESS; +} + + +STATIC_FUNC +int bmx_load_config ( uint8_t cmd, struct opt_type *opt, struct ctrl_node *cn ) { + + char name[MAX_PATH_SIZE]=""; + struct uci_ptr sptr, optr; + + if ( !bmx_ctx || !bmx_conf_name ) + return SUCCESS; + + if ( !opt->long_name || opt->cfg_t == A_ARG ) + return SUCCESS; + + paranoia( -500138, ( cmd != OPT_CHECK && cmd != OPT_APPLY ) ); + + + if ( opt->opt_t == A_PS1 ) { + + sprintf( name, "%s.%s.%s", bmx_conf_name, DEF_SECT_NAME, opt->long_name ); + + dbgf_all( DBGT_INFO, "loading A_PS1-option: %s", name ); + + if ( !( uci_lookup( bmx_ctx, &optr, name ) ) ) { + + if ( on_the_fly && //no need to reset a configuration during init + check_apply_parent_option( DEL, cmd, NO/*save*/, opt, 0, cn ) == FAILURE ) + { + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, + "resetting A_PS1 %s.%s.%s to defaults failed", + bmx_conf_name, DEF_SECT_NAME, opt->long_name ); + + return FAILURE; + } + + } else if ( !optr.o || !optr.o->v.string ) { + + return FAILURE; + + } else if ( check_apply_parent_option( ADD, cmd, NO/*save*/, opt, optr.o->v.string, cn ) == FAILURE ) { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "loading A_PS1 %s.%s.%s=%s failed", + bmx_conf_name, DEF_SECT_NAME, opt->long_name, optr.o->v.string ); + + return FAILURE; + } + + + } else if ( opt->opt_t == A_PMN ) { + + // For A_M (multiple) and A_N (N-suboptons) we use a section) + + dbgf_all( DBGT_INFO, "loading A_PMN-option: %s", opt->long_name ); + + struct uci_element *e; + struct uci_element *se; + struct opt_parent *p_tmp; + + struct list_node *pos; + + if ( !(e=uci_lookup( bmx_ctx, &sptr, bmx_conf_name )) ) + return SUCCESS; + + if ( e->type != UCI_TYPE_PACKAGE ) + return SUCCESS; + + // temporary cache all currently configured parents/sections + // so that we can later reset all of them which were not reloaded + del_opt_parent( &tmp_conf_opt, NULL ); + list_for_each( pos, &(opt->d.parents_instance_list) ) { + p_tmp = list_entry( pos, struct opt_parent, list ); + + struct opt_parent *p_dup = add_opt_parent(&tmp_conf_opt); + set_opt_parent_val ( p_dup, p_tmp->p_val ); + set_opt_parent_ref ( p_dup, p_tmp->p_ref ); + } + + uci_foreach_element( &(sptr.p->sections), se) { + + struct uci_section *s = uci_to_section(se); + + if ( strcmp( opt->long_name, s->type ) ) + continue; + + sprintf( name, "%s.%s.%s", bmx_conf_name, s->e.name, opt->long_name ); + + dbgf_all( DBGT_INFO, "looking up: %s", name ); + + + if ( !(e=uci_lookup( bmx_ctx, &optr, name )) ) { + + if ( cmd == OPT_APPLY ) { + dbgf_cn( cn, DBGL_SYS, DBGT_WARN, + "looking up %s.%s.%s failed", + bmx_conf_name, s->e.name, opt->long_name ); + } + + continue; + } + + char config_sect_val[MAX_ARG_SIZE]; + strcpy( config_sect_val, optr.o->v.string ); + + + struct opt_parent *patch = add_opt_parent( &Patch_opt ); + + if ( call_option( ADD, OPT_PATCH, NO/*save*/, opt, patch, config_sect_val, cn ) == FAILURE ) { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "setting sect %s.%s.%s=%s failed", + bmx_conf_name, s->e.name, opt->long_name, config_sect_val ); + + del_opt_parent( &Patch_opt, patch ); + return FAILURE; + } + + + list_for_each( pos, &opt->d.childs_type_list ) { + + struct opt_type *opt_arg = (struct opt_type*)list_entry( pos, struct opt_data, list ); + + sprintf( name, "%s.%s.%s", bmx_conf_name, s->e.name, opt_arg->long_name ); + dbgf_all( DBGT_INFO, "looking up: %s", name ); + + uint8_t del; + char config_sect_opt_val[MAX_ARG_SIZE]; + + if ( (e=uci_lookup( bmx_ctx, &optr, name )) ) { + + strcpy( config_sect_opt_val, optr.o->v.string ); + del = ADD; + + } else { + + if ( !on_the_fly ) + continue; //no need to reset a configuration during init + + config_sect_opt_val[0] = 0; + del = DEL; + } + + if ( call_option( del, OPT_PATCH, NO/*save*/, opt_arg, patch, config_sect_opt_val, cn ) == FAILURE ) { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, + "setting opt %s %s %s.%s.%s=%s failed", + opt->long_name, config_sect_val, + bmx_conf_name, s->e.name, opt_arg->long_name, config_sect_opt_val ); + + del_opt_parent( &Patch_opt, patch ); + return FAILURE; + + } else { + + dbgf_all( DBGT_INFO, + "patched opt %s %s %s.%s.%s=%s", + opt->long_name, config_sect_val, + bmx_conf_name, s->e.name, opt_arg->long_name, config_sect_opt_val ); + } + + } + + if ( call_option( ADD, OPT_ADJUST, NO/*save*/, opt, patch, config_sect_val, cn ) == FAILURE || + call_option( ADD, cmd, NO/*save*/, opt, patch, config_sect_val, cn ) == FAILURE ) { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, + "configuring section %s.%s=%s failed", + bmx_conf_name, s->e.name, opt->long_name ); + + del_opt_parent( &Patch_opt, patch ); + return FAILURE; + } + + // remove all (re)loaded opts from the cached list. They dont have to be resetted later on + if ((p_tmp = get_opt_parent_ref(&tmp_conf_opt, config_sect_val)) || + (p_tmp = get_opt_parent_val(&tmp_conf_opt, patch->p_val))) + del_opt_parent(&tmp_conf_opt, p_tmp); + + + del_opt_parent( &Patch_opt, patch ); + } + + // finally we have to reset all options which were configured previously but not reloaded + list_for_each( pos, &tmp_conf_opt.d.parents_instance_list ) { + + p_tmp = list_entry( pos, struct opt_parent, list ); + + if ( wordsEqual( p_tmp->p_val, BMX_LIB_UCI_CONFIG ) ) { + + dbg_mute( 40, DBGL_SYS, DBGT_WARN, "missing section %s with option %s %s in %s", + ARG_PLUGIN, ARG_PLUGIN, BMX_LIB_UCI_CONFIG, bmx_conf_name ); + + } else if ( check_apply_parent_option( DEL, cmd, NO, opt, p_tmp->p_val, cn ) == FAILURE ) { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "calling %s %s failed", opt->long_name, p_tmp->p_val ); + + return FAILURE; + } + } + + } else { + + dbgf_cn( cn, DBGL_SYS, DBGT_ERR, "opt: %s illegal implementation! %s", opt->long_name, ILLEGAL_STATE ); + + cleanup_all( -500137 ); + + } + + return SUCCESS; +} + + + +STATIC_FUNC +int32_t opt_conf_reload ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + if ( cmd == OPT_CHECK || cmd == OPT_APPLY ) { + + Load_config = 1; + + if ( cmd == OPT_CHECK && bmx_ctx ) { + + // without doing this we get double free or corruption after config-reload or uci_remove() + uci_reload_package( bmx_ctx, &bmx_pptr, bmx_conf_name ); + } + } + + return SUCCESS; +} + + + +STATIC_FUNC +int32_t opt_conf_file ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + char tmp_path[MAX_PATH_SIZE] = ""; + + if ( on_the_fly ) + return SUCCESS; + + + if ( cmd == OPT_REGISTER ) { + + sprintf( conf_path, "%s/%s", UCI_CONFDIR, DEF_CONF_NAME ); + bmx_conf_name = strrchr( conf_path, '/') + 1; + *(strrchr( conf_path, '/'))=0; + + + } else if ( cmd == OPT_CHECK || cmd == OPT_APPLY ) { + + if ( patch->p_diff == DEL ) + return FAILURE; + + char *f = patch->p_val; + + if ( wordlen(f)+1 +strlen(UCI_CONFDIR)+1 >= MAX_PATH_SIZE ) + return FAILURE; + + if ( wordsEqual( f, ARG_NO_CONFIG_FILE ) ) { + + if ( cmd == OPT_APPLY ) + bmx_conf_name = NULL; + + return SUCCESS; + + } else if ( f[0] == '/' ) { + + wordCopy( tmp_path, f ); + + char *tmp_name = strrchr( tmp_path, '/'); + + if ( !tmp_name || tmp_name == tmp_path || + check_file( tmp_path, YES/*writable*/, NO/*executable*/ ) == FAILURE ) + return FAILURE; + + } else if ( strchr( f, '/') == NULL || strchr( f, '/') >= f+wordlen(f) ) { + + snprintf( tmp_path, strlen(UCI_CONFDIR)+1+wordlen(f)+1, "%s/%s", UCI_CONFDIR, f ); + + if ( check_file( tmp_path, YES/*writable*/, NO/*executable*/ ) == FAILURE ) + return FAILURE; + + + } else { + + return FAILURE; + } + + if ( cmd == OPT_APPLY ) { + + strcpy( conf_path, tmp_path ); + bmx_conf_name = strrchr( conf_path, '/') + 1; + *(strrchr( conf_path, '/'))=0; + + } + + return SUCCESS; + + + } else if ( cmd == OPT_SET_POST && bmx_conf_name ) { + + sprintf( tmp_path, "%s/%s", conf_path, bmx_conf_name ); + + if ( check_file( tmp_path, YES/*writable*/, NO/*executable*/ ) == FAILURE ) + return SUCCESS; //no config file used + + bmx_ctx = uci_alloc_context(); + uci_set_confdir( bmx_ctx, conf_path ); + + dbg( DBGL_CHANGES, DBGT_INFO, + "loading uci bmxd backend: file://%s/%s succeeded", conf_path, bmx_conf_name ); + + //initially lookup the bmx package so that we can save future changes + memset(&bmx_pptr, 0, sizeof(bmx_pptr)); + bmx_pptr.package = bmx_conf_name; + uci_lookup_ptr( bmx_ctx, &bmx_pptr, NULL, false); + + net_ctx = uci_alloc_context(); + uci_set_confdir( net_ctx, conf_path ); + + load_config_cb = bmx_load_config; + save_config_cb = bmx_save_config; + derive_config = bmx_derive_config; + + // we are already at OPT_SET_POST order>1 but + // we have nothing OPT_TESTed nor OPT_SET order=0 options, so load it now! + // order > 1 will be OPT_TEST and OPT_SET automatically via load_config_cb = bmx_load_config function + struct list_node *list_pos; + + int8_t test = 1; + while ( test >= 0 && test <= 1 ) { + + list_for_each( list_pos, &opt_list ) { + + struct opt_type *on = (struct opt_type*)list_entry( list_pos, struct opt_data, list ); + + if ( (test && on->order != 1 ) || (!test && on->order == 0) ) { + + if ( bmx_load_config( test?OPT_CHECK:OPT_APPLY, on, cn ) != SUCCESS ) { + + dbgf_all( DBGT_ERR, + "bmx_load_config() %s %s failed", + test?"OPT_TEST":"OPT_SET",on->long_name ); + + return FAILURE; + } + } + } + test--; + } + } + + signal( SIGHUP, signal_hup_handler ); + + return SUCCESS; +} + +static uint8_t show_conf_general = YES; + +STATIC_FUNC +int8_t show_conf ( struct ctrl_node *cn, void *data, struct opt_type *opt, struct opt_parent *p, struct opt_child *c ) { + + if ( show_conf_general && !c && opt->opt_t == A_PS1 ) { + + dbg_printf( cn, "\toption '%s' '%s'\n", opt->long_name, (p->p_ref ? p->p_ref : p->p_val) ); + + } else if ( !show_conf_general && !c && opt->opt_t == A_PMN ) { + + dbg_printf( cn, "\nconfig '%s'\n", opt->long_name ); + dbg_printf( cn, "\toption '%s' '%s'\n", opt->long_name, (p->p_ref ? p->p_ref : p->p_val) ); + + } else if ( !show_conf_general && c && c->c_opt->opt_t == A_CS1 ) { + + dbg_printf( cn, "\toption '%s' '%s'\n", c->c_opt->long_name, (c->c_ref ? c->c_ref : c->c_val) ); + + } + + return SUCCESS; +} + + + +STATIC_FUNC +int32_t opt_show_conf ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + if ( cmd == OPT_APPLY ) { + + dbg_printf( cn, "config '%s' '%s'\n", DEF_SECT_TYPE, DEF_SECT_NAME ); + + show_conf_general = YES; + func_for_each_opt( cn, NULL, "show_conf()", show_conf ); + show_conf_general = NO; + func_for_each_opt( cn, NULL, "show_conf()", show_conf ); + + dbg_printf( cn, "\n" ); + + } + + return SUCCESS; +} + +static struct opt_type config_options[]= { +// ord parent long_name shrt Attributes *ival min max default *func,*syntax,*help + + {ODI,0,0, 0, 5,0,0,0,0,0, 0, 0, 0, 0, 0, + 0, "\nUCI config options:"}, + + + {ODI,0,ARG_CONFIG_FILE, 'f',1,A_PS1,A_ADM,A_INI,A_ARG,A_ANY, 0, 0, 0, 0, opt_conf_file, + ARG_FILE_FORM, "use non-default config file. If defined, this must be the first given option.\n" + " use --" ARG_CONFIG_FILE "=" ARG_NO_CONFIG_FILE " or -f" ARG_NO_CONFIG_FILE " to disable"}, + + {ODI,0,ARG_RELOAD_CONFIG, 0, 1,A_PS0,A_ADM,A_DYN,A_ARG,A_ANY, 0, 0, 0, 0, opt_conf_reload, + 0, "dynamically reload config file"}, + + {ODI,0,ARG_SHOW_CONFIG, 0, 5,A_PS0,A_ADM,A_DYN,A_ARG,A_ANY, 0, 0, 0, 0, opt_show_conf, + 0, "show current config as it could be saved to " ARG_CONFIG_FILE } +}; + + + +STATIC_FUNC +void cleanup_conf( void ) { + + del_opt_parent( &tmp_conf_opt, NULL ); + + load_config_cb = NULL; + save_config_cb = NULL; + + if ( bmx_ctx ) + uci_free_context( bmx_ctx ); + + if ( net_ctx ) + uci_free_context( net_ctx ); + +} + +STATIC_FUNC +int32_t init_conf( void ) { + + memset( &tmp_conf_opt, 0, sizeof( struct opt_type ) ); + LIST_INIT_HEAD( tmp_conf_opt.d.childs_type_list, struct opt_data, list ); + LIST_INIT_HEAD( tmp_conf_opt.d.parents_instance_list, struct opt_parent, list ); + + register_options_array( config_options, sizeof( config_options ) ); + + return SUCCESS; +} + + + +struct plugin_v2* get_plugin_v2( void ) { + + static struct plugin_v2 conf_plugin; + + memset( &conf_plugin, 0, sizeof ( struct plugin_v2 ) ); + + conf_plugin.plugin_version = PLUGIN_VERSION_02; + conf_plugin.plugin_name = "bmx_uci_config_plugin"; + conf_plugin.plugin_size = sizeof ( struct plugin_v2 ); + conf_plugin.plugin_bmx_revision = REVISION_VERSION; + conf_plugin.plugin_bmx_version = SOURCE_VERSION; + conf_plugin.cb_init = init_conf; + conf_plugin.cb_cleanup = cleanup_conf; + + return &conf_plugin; +} +
Added: trunk/bmx/lib/bmx_uci_config/uci_config.h =================================================================== --- trunk/bmx/lib/bmx_uci_config/uci_config.h (rev 0) +++ trunk/bmx/lib/bmx_uci_config/uci_config.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#define DEF_CONF_NAME "bmx" +#define DEF_SECT_NAME "general" +#define DEF_SECT_TYPE "bmx" + +#define ARG_CONFIG_FILE "config_file" +#define ARG_SAVE_CONFIG "save_config" +#define ARG_SHOW_CONFIG "show_config" +#define ARG_RELOAD_CONFIG "reload_config" + +#define ARG_NO_CONFIG_FILE "0" \ No newline at end of file
Added: trunk/bmx/list.c =================================================================== --- trunk/bmx/list.c (rev 0) +++ trunk/bmx/list.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* + * This list implementation is originally based on the + * double-linked list implementaton from the linux-kernel + * which can be found in include/linux/list.h + * + * The following has been to better fit my needs: + * - items counter + * - plist handler (list-node structure with void* item pointer + * - single-linked list instead of double-linked list to save overhead + * - some functions for straight item access! + */ + +#include "bmx.h" +#include "list.h" + +/** + * list_iterate - return pointer to next node maintained in the list or NULL + * @head: list head of maintained nodes + * @node: a node maintained in the list or NULL + */ +void * list_iterate(struct list_head *head, void *node) +{ + struct list_node * ln; + + if (head->prev == (ln = ((node ? + (struct list_node*) (((char*) node) + head->list_node_offset) : + (struct list_node*) head))->next) + ) + return NULL; + + return (((char*) ln) - head->list_node_offset); +} + + + +/** + * list_add_head - add a new entry at the beginning of a list + * @head: list head to add it after + * @new: new entry to be added + */ +void list_add_head(struct list_head *head, struct list_node *new) +{ + + new->next = head->next; + ((struct list_node *) head)->next = new; + + if (head->prev == (struct list_node *) head) + head->prev = new; + + head->items++; + +} + +/** + * list_add_tail - add a new entry + * @head: list head to add it before + * @new: new entry to be added + */ +void list_add_tail(struct list_head *head, struct list_node *new ) +{ + new->next = (struct list_node *) head; + head->prev->next = new; + + head->prev = new; + head->items++; +} + + +void list_add_after(struct list_head *head, struct list_node *pos, struct list_node *new) +{ + new->next = pos->next; + pos->next = new; + head->items++; +} + +/** + * list_del_next - deletes next entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is in an undefined state. + */ +void list_del_next(struct list_head *head, struct list_node *pos) +{ + struct list_node *entry = pos->next; + + if (head->prev == entry) + head->prev = pos; + + pos->next = entry->next; + + head->items--; +} + +void *list_rem_head(struct list_head *head) +{ + if (LIST_EMPTY(head)) + return NULL; + + struct list_node* entry = head->next; + + list_del_next(head, (struct list_node*) head); + + return ((char*) entry) -head->list_node_offset; +} + + + +/** + * plist_get_next - return pointer to next node maintained in the list or NULL + * @head: list head of maintained nodes + * @@pnode: MBZ at beginning! pointing to current plist_node in list + */ +void * plist_iterate(struct list_head *head, struct plist_node **pln) +{ + + if (head->prev == (struct list_node*) + (*pln = *pln ? (struct plist_node*) ((*pln)->list.next) : (struct plist_node*) (head->next))) + return NULL; + + return (*pln)->item; + +/* + if (LIST_EMPTY(head)) + return NULL; + + if (*pln) { + + if (head->prev == (struct list_node*) (*pln = (struct plist_node*) ((*pln)->list->next))) + return NULL; + + } else { + *pln = (struct plist_node*) head->next; + } + + return (*pln)->item; +*/ + +} + + + +static struct plist_node *plist_node_create(void *item) +{ + paranoia(-500266, (!item)); + struct plist_node *plh = debugMalloc(sizeof ( struct plist_node), -300113); + + plh->item = item; + return plh; +} + +void plist_add_head(struct list_head *head, void *item) +{ + list_add_head(head, &((plist_node_create(item))->list)); +} + +void plist_add_tail(struct list_head *head, void *item) +{ + list_add_tail(head, &((plist_node_create(item))->list)); +} + +void * plist_rem_head(struct list_head *head) +{ + struct plist_node *pln = list_rem_head(head); + + if ( !pln ) + return NULL; + + void *item = pln->item; + + debugFree( pln, -300114 ); + + return item; +}
Added: trunk/bmx/list.h =================================================================== --- trunk/bmx/list.h (rev 0) +++ trunk/bmx/list.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/* + * This list implementation is originally based on the + * double-linked list implementaton from the linux-kernel + * which can be found in include/linux/list.h + * + * The following has been changed to better fit my needs and intuition: + * - items counter + * - plist handler (list-node structure with void* item pointer + * - single-linked list instead of double-linked list to save overhead + * - some functions for straight item access! + */ + + +#ifndef _LIST_H +#define _LIST_H + + + +struct list_node { + struct list_node *next; +}; + +struct list_head { + struct list_node *next, *prev; + uint16_t items; + uint16_t list_node_offset; +}; + +struct plist_node { + struct list_node list; + void *item; +}; + + +#define LIST_SIMPEL(lst, element_type, list_node_field) struct list_head lst = { \ + (struct list_node *)&lst, (struct list_node *)&lst, \ + 0, \ + ((unsigned long)(&((element_type *)0)->list_node_field)) } + +#define LIST_INIT_HEAD(ptr, element_type, list_node_field) do { \ + ptr.next = ptr.prev =(struct list_node *)&ptr; \ + ptr.items = 0; \ + ptr.list_node_offset = ((unsigned long)(&((element_type *)0)->list_node_field)); \ +} while (0) + +#define LIST_EMPTY(lst) ((lst)->next == (struct list_node *)(lst)) + + +#define list_get_first(head) ((void*)((LIST_EMPTY(head)) ? NULL : (((char*) (head)->next) - (head)->list_node_offset) )) +#define list_get_last(head) ((void*)((LIST_EMPTY(head)) ? NULL : (((char*) (head)->prev) - (head)->list_node_offset) )) + +void * list_iterate( struct list_head *head, void *node ); + +void list_add_head(struct list_head *head, struct list_node *new); +void list_add_tail(struct list_head *head, struct list_node *new ); +void list_add_after(struct list_head *head, struct list_node *pos, struct list_node *new); +void list_del_next(struct list_head *head, struct list_node *pos); +void *list_rem_head(struct list_head *head); + + +#define plist_get_first(head) (LIST_EMPTY(head) ? NULL : \ + ((struct plist_node*)(((char*) (head)->next) - (head)->list_node_offset))->item ) + +#define plist_get_last(head) (LIST_EMPTY(head) ? NULL : \ + ((struct plist_node*)(((char*) (head)->prev) - (head)->list_node_offset))-item ) + +void * plist_iterate(struct list_head *head, struct plist_node **pln); + +void plist_add_head(struct list_head *head, void *item); +void plist_add_tail(struct list_head *head, void *item); +void *plist_rem_head(struct list_head *head); + + + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) ( (type *)( (char *)(ptr) - (unsigned long)(&((type *)0)->member) ) ) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (struct list_node *)(head); pos = pos->next) + +#define list_for_each_item(pos, head, item, type, member) \ + for (pos = (head)->next; \ + pos != (struct list_node *)(head) && \ + (item = ((type *)( (char *)(pos) - (unsigned long)(&((type *)0)->member) ) )); \ + pos = pos->next) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (struct list_node *)(head); \ + pos = n, n = pos->next) + + +#endif
Added: trunk/bmx/msg.c =================================================================== --- trunk/bmx/msg.c (rev 0) +++ trunk/bmx/msg.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,2291 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#include <unistd.h> +#include <errno.h> + + +//#include <sha.h> + +#include "bmx.h" +#include "msg.h" +#include "hna.h" +#include "schedule.h" + + +static Sha bmx_sha; + +AVL_TREE( description_cache_tree, struct description_cache_node, dhash ); + + + +static int32_t max_udpd_size = DEF_UDPD_SIZE; + +static int32_t aggreg_interval = DEF_AGGREG_INTERVAL; + +int32_t my_ogm_interval = DEF_OGM_INTERVAL; /* orginator message interval in miliseconds */ + +int32_t my_hello_interval = DEF_HELLO_INTERVAL; + +int32_t ogm_resend_attempts = DEF_OGM_RESEND_ATTEMPTS; + +int my_desc0_tlv_len = 0; + +IID_T myIID4me = IID_RSVD_UNUSED; + + +LIST_SIMPEL( ogm_aggreg_list, struct ogm_aggreg_node, list ); +uint32_t ogm_aggreg_pending = 0; +static SQN_T ogm_aggreg_sqn_max; + + +char *tlv_op_str[] = {"TLV_DEL","TLV_TEST","TLV_ADD","TLV_DONE","TLV_DEBUG"}; + + + + +STATIC_FUNC int tx_msg_hello40_reply (struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size); +STATIC_FUNC int tx_msg_helloX0_adv (struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size); +STATIC_FUNC int tx_msg_description0_adv (struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size); + +STATIC_FUNC int tx_msg_dhash0_adv (struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size); + +STATIC_FUNC int tx_msg_dhash0_or_description0_request (struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size); +STATIC_FUNC int tx_msg_ogm_ack (struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size); + +STATIC_FUNC int tx_frame_ogm0_advs (struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size); + + +STATIC_FUNC int rx_frame_hello40_replies (struct packet_buff *pb, struct frame_header *frame); +STATIC_FUNC int rx_frame_helloX0_advs (struct packet_buff *pb, struct frame_header *frame); +STATIC_FUNC int rx_frame_description0_advs (struct packet_buff *pb, struct frame_header *frame); + +STATIC_FUNC int rx_frame_dhash0_advs (struct packet_buff *pb, struct frame_header *frame); + +STATIC_FUNC int rx_frame_dhash0_or_description0_requests (struct packet_buff *pb, struct frame_header *frame); + +STATIC_FUNC int rx_frame_ogm0_advs (struct packet_buff *pb, struct frame_header *frame); +STATIC_FUNC int rx_frame_ogm40_acks (struct packet_buff *pb, struct frame_header *frame); + +/*********************************************************** + The core frame/message structures and handlers + ************************************************************/ + + + +struct description_tlv_handler { + uint16_t reserved1; + uint16_t reserved2; + uint16_t min_msg_size; + uint16_t variable_msg_size; + char *name; + int (*create_tlv) (uint8_t *data, uint16_t max_size); + int (*process_tlv) (struct orig_node *on, struct frame_header *tlv, IDM_T op, struct ctrl_node *cn); +}; + +struct description_tlv_handler description0_tlv_handler[BMX_DSC_TLV_MAX] = { + {0, 0, sizeof (struct description0_msg_ip4), 0, + "desc0tlv_glip4", create_description_tlv_ip4, process_description_tlv_hna4} + , + {0, 0, sizeof (struct description0_msg_hna4), 0, + "desc0tlv_uhna4", create_description_tlv_hna4, process_description_tlv_hna4} +}; + + + +struct pkt_frame_handler { + uint16_t reserved; + uint16_t tx_iterations; + uint16_t min_rtq; + uint16_t data_header_size; + uint16_t min_msg_size; + uint16_t fixed_msg_size; + uint32_t min_tx_interval; + char *name; + int (*tx_frm_creator) (struct tx_task_node * ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size); +/* + * tx_msg_creator() + * expects sufficient buff_size for non-variable_msg_size messages !!! + * returns x=sizeof(send msg), thus (x<=buff_size), if msg was successfully created + * returns (x > buff_size) if variable_msg_size could not be created due to lack of buff_size + * returns 0 if message MUST be send later + * returns FAILRUE if porblem occured and msg-meta data (tx_task_node) MUST be destroyed + */ + int (*tx_msg_creator) (struct tx_task_node * ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size); + int (*rx_frm_receptor) (struct packet_buff *, struct frame_header *); +}; + + +static struct pkt_frame_handler frame_handler[FRAME_TYPE_ARRSZ] = { + {0, 0, 0, 0, 0, 1, 0, NULL, NULL, NULL, NULL}, + {0, 0, 0, 0, 0, 1, 0, NULL, NULL, NULL, NULL}, + + {0, 1, 0, 0, sizeof (struct msg_hello_adv), 1, 0, + "hey0_adv", NULL, tx_msg_helloX0_adv, rx_frame_helloX0_advs}, + + {0, 1, 0, 0, sizeof (struct msg_hello_reply), 1, 0, + "hey0_rep", NULL, tx_msg_hello40_reply, rx_frame_hello40_replies}, + {0, 0, 0, 0, 0, 1, 0, NULL, NULL, NULL, NULL}, + + {0, 0, 1, 0, 0, 1, 0, NULL, NULL, NULL, NULL}, + {0, 0, 1, 0, 0, 1, 0, NULL, NULL, NULL, NULL}, + + {0, 1, MIN_NBDISC_RTQ, 0, sizeof (struct msg_description_request), 1, DEF_TX_DESC0_REQ_TO, // receiverIID, receiverIP4 + "desc0_req", NULL, tx_msg_dhash0_or_description0_request, rx_frame_dhash0_or_description0_requests}, + + {0, 1, MIN_NBDISC_RTQ, 0, sizeof (struct msg_description_adv), 0, DEF_TX_DESC0_ADV_TO, // myIID4x + "desc0_adv", NULL, tx_msg_description0_adv, rx_frame_description0_advs}, + + {0, 1, MIN_NBDISC_RTQ, 0, sizeof (struct msg_dhash_request), 1, DEF_TX_DHASH0_REQ_TO, // receiverIID, receiverIP4 + "dhash0_req", NULL, tx_msg_dhash0_or_description0_request, rx_frame_dhash0_or_description0_requests}, + + {0, 1, MIN_NBDISC_RTQ, 0, sizeof (struct msg_dhash_adv), 1, DEF_TX_DHASH0_ADV_TO, // myIID4x + "dhash_adv", NULL, tx_msg_dhash0_adv, rx_frame_dhash0_advs}, + + {0, 1, 0, sizeof (struct hdr_ogm_adv), sizeof (struct msg_ogm_adv), 1, 0, + "ogm_adv", tx_frame_ogm0_advs, NULL, rx_frame_ogm0_advs}, + + {0, 2, 0, 0, sizeof (struct msg_ogm_ack), 1, 0, + "ogm_ack", NULL, tx_msg_ogm_ack, rx_frame_ogm40_acks}, + + {0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL} + +}; + + + + +STATIC_FUNC +struct description * rem_cached_description(struct description_hash *dhash) +{ + struct description_cache_node *dcn; + + if (!(dcn = avl_find_item(&description_cache_tree, dhash))) + return NULL; + + struct description *desc0 = dcn->description; + + avl_remove(&description_cache_tree, &dcn->dhash, -300206); + debugFree(dcn, -300108); + + return desc0; +} + +STATIC_FUNC +struct description_cache_node *purge_cached_descriptions( IDM_T purge_all ) { + + struct description_cache_node *dcn; + struct description_cache_node *dcn_min = NULL; + struct description_hash tmp_dhash; + memset( &tmp_dhash, 0, sizeof(struct description_hash)); + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s", purge_all ? "purge_all" : "only_expired"); + + paranoia( -500349, (!is_zero((char*)&tmp_dhash, BMX_HASH0_LEN))); + + while ((dcn = avl_next_item(&description_cache_tree, &tmp_dhash))) { + + memcpy(&tmp_dhash, &dcn->dhash, BMX_HASH0_LEN); + + if (purge_all || ((uint32_t) (bmx_time - dcn->timestamp)) > DEF_DESC0_CACHE_TO) { + + avl_remove(&description_cache_tree, &dcn->dhash, -300208); + debugFree(dcn->description, -300100); + debugFree(dcn, -300101); + + } else { + + if (!dcn_min || LESS_U32(dcn->timestamp, dcn_min->timestamp)) + dcn_min = dcn; + } + } + + return dcn_min; +} + +STATIC_FUNC +void cache_description(struct description *desc, struct description_hash *dhash) +{ + struct description_cache_node *dcn; + + uint16_t desc_len = sizeof (struct description) + ntohs(desc->dsc_tlvs_len); + + if ((dcn = avl_find_item(&description_cache_tree, dhash))) { + dcn->timestamp = bmx_time; + return; + } + + dbgf(DBGL_CHANGES, DBGT_INFO, "%8X..", dhash->h.u32[0]); + + + paranoia(-500261, (description_cache_tree.items > DEF_DESC0_CACHE_SIZE)); + + if ( description_cache_tree.items == DEF_DESC0_CACHE_SIZE ) { + + + struct description_cache_node *dcn_min = purge_cached_descriptions( NO ); + + dbgf(DBGL_SYS, DBGT_WARN, "desc0_cache_tree reached %d items! cleaned up %d items!", + DEF_DESC0_CACHE_SIZE, DEF_DESC0_CACHE_SIZE - description_cache_tree.items); + + if (description_cache_tree.items == DEF_DESC0_CACHE_SIZE) { + avl_remove(&description_cache_tree, &dcn_min->dhash, -300209); + debugFree(dcn_min->description, -300102); + debugFree(dcn_min, -300103); + } + } + + paranoia(-500273, (desc_len != sizeof ( struct description) + ntohs(desc->dsc_tlvs_len))); + + dcn = debugMalloc(sizeof ( struct description_cache_node), -300104); + dcn->description = debugMalloc(desc_len, -300105); + memcpy(dcn->description, desc, desc_len); + memcpy( &dcn->dhash, dhash, BMX_HASH0_LEN ); + dcn->timestamp = bmx_time; + avl_insert(&description_cache_tree, dcn, -300145); + +} + +void purge_tx_timestamp_tree(struct dev_node *dev, IDM_T purge_all) +{ + struct avl_node *an; + struct tx_timestamp_node *ttn = NULL; + struct tx_timestamp_key key; + + memset(&key, 0, sizeof (struct tx_timestamp_key)); + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s %s", dev->name, purge_all ? "purge_all" : "only_expired"); + + while ((an = avl_next(&dev->tx_timestamp_tree, (ttn ? &ttn->key : &key)))) { + + ttn = an->item; + + if (purge_all || ((uint32_t) (bmx_time - ttn->timestamp)) > + (DEF_TX_TS_TREE_PURGE_FK * frame_handler[ttn->key.type].min_tx_interval)) { + + memcpy( &key, &ttn->key, sizeof( struct tx_timestamp_key ) ); + + avl_remove( &dev->tx_timestamp_tree, &ttn->key, -300210 ); + debugFree( ttn, -300127 ); + ttn = NULL; + } + } +} + + +STATIC_FUNC +struct tx_timestamp_node * tx_timestamp_add( struct dev_node *dev, struct tx_timestamp_key *key) +{ + ASSERTION( -500263, ( dev && !avl_find( &dev->tx_timestamp_tree, key ) ) ); + + if (dev->tx_timestamp_tree.items > DEF_TX_TS_TREE_SIZE) { + + purge_tx_timestamp_tree(dev, NO); + + if (dev->tx_timestamp_tree.items > DEF_TX_TS_TREE_SIZE) { + dbg_mute(20, DBGL_SYS, DBGT_WARN, "%s tx_ts_tree reached %d %s neighIID4x %u %s %u", + dev->name, dev->tx_timestamp_tree.items, frame_handler[key->type].name, + key->neighIID4x, ipStr(key->myIID4x_or_dest_ip4), key->myIID4x_or_dest_ip4); + } + } + + struct tx_timestamp_node *ttn = debugMalloc(sizeof ( struct tx_timestamp_node), -300126); + memset(ttn, 0, sizeof ( struct tx_timestamp_node)); + memcpy( &ttn->key, key, sizeof(struct tx_timestamp_key ) ); + ttn->timestamp = bmx_time; + avl_insert(&dev->tx_timestamp_tree, ttn, -300146); + + return ttn; +} + +STATIC_FUNC +IDM_T tx_task_obsolete( struct dev_node *dev, uint8_t frame_type, struct tx_task_node *tx_task ) +{ + struct tx_timestamp_node *ttn = NULL; + struct tx_timestamp_key key; + struct dhash_node *dhn = NULL; + + if (tx_task->myIID4x >= IID_MIN_USED && !((dhn = iid_get_node_by_myIID4x(tx_task->myIID4x)) && dhn->on)) { + goto tx_timestamped_deny; + } + + if (!frame_handler[frame_type].min_tx_interval) + return NO; + + memset(&key, 0, sizeof (struct tx_timestamp_key)); + + key.myIID4x_or_dest_ip4 = tx_task->myIID4x ? tx_task->myIID4x : tx_task->dst_ip4; + key.neighIID4x = tx_task->neighIID4x; + key.type = frame_type; + + if (frame_handler[frame_type].min_tx_interval && (ttn = avl_find_item(&dev->tx_timestamp_tree, &key))) { + + if (((uint32_t) (bmx_time - ttn->timestamp) < frame_handler[frame_type].min_tx_interval)) { + + goto tx_timestamped_deny; + } + + ttn->timestamp = bmx_time; + } + + if (!ttn) + ttn = tx_timestamp_add(dev, &key); + + return NO; + + +tx_timestamped_deny: + dbgf(DBGL_CHANGES, DBGT_WARN, + "skipping %s %s myIId4x %d neighIID4x %d %s %s send just %d ms ago", + frame_handler[frame_type].name, dev->name, + tx_task->myIID4x, tx_task->neighIID4x, ipStr(tx_task->dst_ip4), + dhn ? dhn->on->id.name : "???", + ttn ? (int)(bmx_time - ttn->timestamp) : -1); + + return YES; +} + + + + +STATIC_FUNC +IDM_T validate_param(int32_t probe, int32_t min, int32_t max, char *name, struct opt_type *opt) +{ + if (opt) { + min = opt->imin; + max = opt->imax; + name = opt->long_name; + } + + if ( probe < min || probe > max ) { + + dbgf( DBGL_SYS, DBGT_ERR, "Illegal %s parameter value %d ( min %d max %d )", + name, probe, min, max); + + return FAILURE; + } + + return SUCCESS; +} + + + + + +IDM_T process_description_tlvs(struct orig_node *on, struct description *desc_new, IDM_T op, struct ctrl_node *cn) +{ + struct description *desc; + IDM_T tlv_result; + uint16_t pos = 0, t = 0, pt = 0, size = 0, tlv_size = 0; + struct frame_header * tlv = NULL; + + assertion(-500370, (op == TLV_DEL_TEST_ADD || op == TLV_DEBUG)); + + desc = on->desc0; //start with removing the old desc0_tlvs + + do { + if (op == TLV_TEST || op == TLV_ADD) + desc = desc_new; + + if (!desc || (op == TLV_DEL_TEST_ADD && on->blocked)) + continue; + + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s %s dsc_sqn %d size %d ", + tlv_op_str[op], desc->id.name, ntohs(desc->dsc_sqn), ntohs(desc->dsc_tlvs_len)); + + + size = ntohs(desc->dsc_tlvs_len); + tlv = NULL; + pos = t = pt = 0; + + + assertion(-500274, (size <= MAX_DESC0_TLV_SIZE)); // checked in rx_frm_desc0_advs() + + while (pos + sizeof ( struct frame_header) < size) { + + tlv = (struct frame_header*) (((char*) desc) + sizeof (struct description) + pos); + tlv_size = ntohs(tlv->length); + + if ((t = tlv->type) < pt || + tlv_size < sizeof ( struct frame_header) || + tlv_size + pos > size) { + + dbgf(DBGL_SYS, DBGT_ERR, + "illegal sizes %d for type %s", tlv_size, description0_tlv_handler[t].name); + goto process_desc0_tlv_error; + } + + dbgf(DBGL_ALL, DBGT_INFO, + "type %s size %d flags 0x%X", description0_tlv_handler[t].name, tlv_size, tlv->flags); + + + if (t >= BMX_DSC_TLV_ARRSZ || !(description0_tlv_handler[t].process_tlv)) { + + dbgf(DBGL_SYS, DBGT_WARN, + "unsupported type %d ! maybe you need an update?", t); + + if (t >= BMX_DSC_TLV_ARRSZ) + goto process_desc0_tlv_error; + + } else if (tlv_size - sizeof (struct frame_header) < description0_tlv_handler[t].min_msg_size) { + + dbgf(DBGL_SYS, DBGT_ERR, + "too small size %d for type %s", tlv_size, description0_tlv_handler[t].name); + goto process_desc0_tlv_error; + + } else if (!(description0_tlv_handler[t].variable_msg_size) && + (tlv_size - sizeof (struct frame_header)) % description0_tlv_handler[t].min_msg_size) { + + dbgf(DBGL_SYS, DBGT_ERR, + "nonmaching size %d for type %s", tlv_size, description0_tlv_handler[t].name); + goto process_desc0_tlv_error; + + } else if ((tlv_result = (*(description0_tlv_handler[t].process_tlv)) (on, tlv, op, cn)) != TLVS_SUCCESS) { + + assertion(-500356, (op == TLV_TEST)); + + dbgf(DBGL_SYS, DBGT_ERR, + "%s size %d %s", description0_tlv_handler[t].name, tlv_size, + tlv_result == TLVS_BLOCKED ? "BLOCKED" : "FAILURE"); + + if (tlv_result == TLVS_BLOCKED) { + + on->blocked = YES; + + if (!avl_find(&blocked_tree, &on->id)) + avl_insert(&blocked_tree, on, -300165); + + return TLVS_BLOCKED; + } + + goto process_desc0_tlv_msg_error; + } + + pt = t; + pos += tlv_size; + } + + if (pos != size) { + dbgf(DBGL_SYS, DBGT_ERR, "nonmaching tlvs pos %d != size %d", pos, size ); + + goto process_desc0_tlv_error; + } + + } while (++op < TLV_DONE); + + if ( op == TLV_DONE ) { + on->blocked = NO; + avl_remove(&blocked_tree, &on->id, -300211); + } + + return SUCCESS; + + +process_desc0_tlv_msg_error: + dbgf(DBGL_SYS, DBGT_WARN, + "rcvd problematic message"); + +process_desc0_tlv_error: + dbgf(DBGL_SYS, DBGT_WARN, + "rcvd problematic frame type %s last %s frm_size %d pos %d ", + description0_tlv_handler[t].name, description0_tlv_handler[pt].name, tlv_size, pos); + + return TLVS_FAILURE; +} + +//BMX3 (done) +struct dhash_node * process_description(struct packet_buff *pb, struct description *desc, struct description_hash *dhash) +{ + assertion(-500262, (pb && pb->ln && desc)); + assertion(-500381, (!avl_find( &dhash_tree, dhash ))); + + struct dhash_node *dhn; + struct orig_node *on = NULL; + int id_name_len; + + + dbgf( DBGL_CHANGES, DBGT_INFO, "via dev: %s NB %s:dhash %8X.. id.rand %jX", + pb->iif->name, pb->neigh_str, dhash->h.u32[0], desc->id.rand.u64[0]); + + if ( + (id_name_len = strlen(desc->id.name)) >= DESCRIPTION0_ID_NAME_LEN || + !is_zero(&desc->id.name[id_name_len], DESCRIPTION0_ID_NAME_LEN - id_name_len) || + validate_name(desc->id.name) == FAILURE) { + + dbg(DBGL_SYS, DBGT_ERR, "illegal hostname .. %jX", desc->id.rand.u64[0]); + goto process_desc0_error; + } + + if ( + validate_param(ntohs(desc->ogm_sqn_range), MIN_OGM0_SQN_RANGE, MAX_OGM0_SQN_RANGE, ARG_OGM0_SQN_RANGE, NULL) || + validate_param(desc->ogm_sqn_pq_bits, MIN_OGM0_PQ_BITS, MAX_OGM0_PQ_BITS, ARG_OGM0_PQ_BITS, NULL) || + validate_param(ntohs(desc->path_ogi), 0, 0, NULL, get_option(0, 0, ARG_OGM_INTERVAL)) || + validate_param(desc->ttl_max, 0, 0, NULL, get_option(0, 0, ARG_TTL)) || + validate_param(ntohs(desc->path_window_size), 0, 0, NULL, get_option(0, 0, ARG_PWS)) || + validate_param(ntohs(desc->path_lounge_size), 0, 0, NULL, get_option(0, 0, ARG_PATH_LOUNGE)) || + validate_param(desc->path_hystere, 0, 0, NULL, get_option(0, 0, ARG_PATH_HYST)) || +// validate_param(dsc->hop_penalty, 0, 0, NULL, get_option(0, 0, ARG_HOP_PENALTY)) || + validate_param(desc->late_penalty, 0, 0, NULL, get_option(0, 0, ARG_LATE_PENAL)) || + validate_param(desc->asym_weight, 0, 0, NULL, get_option(0, 0, ARG_ASYM_WEIGHT)) || +// validate_param(dsc->sym_weight, 0, 0, NULL, get_option(0, 0, ARG_SYM_WEIGHT)) || + 0 + ) { + + goto process_desc0_error; + } + + + if ((on = avl_find_item(&orig_tree, &desc->id))) { + + assertion(-500383, (on->dhn)); + + if (((uint32_t) (bmx_time - on->dhn->referred_timestamp)) < (uint32_t) dad_to) { + + if ( ((SQN_T)(ntohs(desc->dsc_sqn) - (on->desc0_sqn + 1))) > SQN_DAD_RANGE ) { + + dbgf(DBGL_SYS, DBGT_ERR, "DAD-Alert: new dsc_sqn %d not > old %d + 1", + ntohs(desc->dsc_sqn), on->desc0_sqn); + + goto process_desc0_ignore; + } + + if (LESS_SQN(ntohs(desc->ogm_sqn_min), (on->ogm_sqn_min + MAX_OGM0_SQN_RANGE))) { + + dbgf(DBGL_SYS, DBGT_ERR, "DAD-Alert: new ogm_sqn_min %d not > old %d + %d", + ntohs(desc->ogm_sqn_min), on->ogm_sqn_min, MAX_OGM0_SQN_RANGE); + + goto process_desc0_ignore; + } + } + + + } else { + // create new orig: + on = debugMalloc( sizeof( struct orig_node ), -300128 ); + memset( on, 0, sizeof( struct orig_node ) ); + memcpy(&on->id, &desc->id, sizeof ( struct description_id)); +/* + on->id.rand.u32[0] = ntohl( desc->id.rand.u32[0] ); + on->id.rand.u32[1] = ntohl( desc->id.rand.u32[1] ); +*/ + AVL_INIT_TREE(on->router_tree, struct router_node, key ); + avl_insert(&orig_tree, on, -300148); + } + + dbgf(DBGL_CHANGES, DBGT_INFO, "rcvd new desc SQN %d (old %d) from %s via %s NB %s", + ntohs(desc->dsc_sqn), on->desc0_sqn, desc->id.name, pb->iif->name, pb->neigh_str); + + if (process_description_tlvs(on, desc, TLV_DEL_TEST_ADD, NULL) == TLVS_FAILURE) + goto process_desc0_error; + + // might result in TLVS_BLOCKED !! + + + on->updated_timestamp = bmx_time; + on->desc0_sqn = ntohs(desc->dsc_sqn); + + on->ogm_sqn_min = ntohs(desc->ogm_sqn_min); + on->ogm_sqn_range = ntohs(desc->ogm_sqn_range); + on->ogm_sqn_pq_bits = desc->ogm_sqn_pq_bits; + +// on->ogm_sqn_mask = (MAX_SQN << on->ogm_sqn_pq_bits); +// on->ogm_sqn_steps = (0x01 << on->ogm_sqn_pq_bits); + + + struct metric_algo test_algo; + memset(&test_algo, 0, sizeof (struct metric_algo)); + + test_algo.sqn_mask = (MAX_SQN << on->ogm_sqn_pq_bits); + test_algo.sqn_steps = (0x01 << on->ogm_sqn_pq_bits); + test_algo.regression = ntohs(desc->path_window_size) / test_algo.sqn_steps / 2; + test_algo.sqn_lounge = ntohs(desc->path_lounge_size); + test_algo.sqn_window = ntohs(desc->path_window_size); + test_algo.metric_max = PROBE_RANGE; + + + if ( validate_metric_algo( &test_algo, NULL ) == FAILURE ) + goto process_desc0_error; + + memcpy(&on->path_metric_algo, &test_algo, sizeof (struct metric_algo)); + + on->ogm_sqn_max_rcvd = on->ogm_sqn_aggregated = on->ogm_sqn_to_be_send = (on->ogm_sqn_min & on->path_metric_algo.sqn_mask); + + + // migrate current router_nodes->mr.clr position to new sqn_range: + struct router_node *rn; + struct avl_node *an; + for (an = NULL; (rn = avl_iterate_item(&on->router_tree, &an));) + rn->mr.clr = on->ogm_sqn_max_rcvd /*- on->path_metric_algo.sqn_steps*/; + + + if (on->desc0) + debugFree(on->desc0, -300111); + + on->desc0 = desc; + desc = NULL; + + struct neigh_node *dhn_neigh = NULL; + + if (on->dhn) { + dhn_neigh = on->dhn->neigh; + on->dhn->neigh = NULL; + on->dhn->on = NULL; + invalidate_dhash_node(on->dhn); + } + + on->dhn = dhn = create_dhash_node(dhash, on); + + if ( dhn_neigh ) { + dhn_neigh->dhn = dhn; + dhn->neigh = dhn_neigh; + } + + assertion(-500309, (dhn == avl_find_item(&dhash_tree, &dhn->dhash) && dhn->on == on)); + + assertion(-500310, (on == avl_find_item(&orig_tree, &on->desc0->id) && on->dhn == dhn)); + + return dhn; + +process_desc0_error: + + if (on) + free_orig_node(on); + + blacklist_neighbor(pb); + +process_desc0_ignore: + + dbgf(DBGL_SYS, DBGT_WARN, "%jX rcvd via %s NB %s", desc ? desc->id.rand.u64[0] : 0, pb->iif->name, pb->neigh_str); + + if (desc) + debugFree(desc, -300109); + + return NULL; +} + +IDM_T freed_tx_task_node(struct tx_task_node *ttn, int tx_creator_result, struct list_node *lprev) +{ + + assertion( -500372, (ttn && (ttn)->dev )); + + if (tx_creator_result != ((int) ((ttn)->frame_data_length_target))) { + + dbgf(DBGL_CHANGES, DBGT_WARN, "msg %s to %s via %s mid4o %d send_data %d != target_data %d ", + frame_handler[(ttn)->frame_type].name, + ipStr((ttn)->dst_ip4), + (ttn)->dev->name, + (ttn)->myIID4x, + tx_creator_result, + (ttn)->frame_data_length_target); + } + + if ((--((ttn)->tx_iterations)) == 0) { + + (ttn)->dev->tx_frames_data_len_target -= (ttn)->frame_data_length_target; + + if (lprev) + list_del_next(&((ttn)->dev->tx_tasks_list[(ttn)->frame_type]), lprev); + else + (ttn)->dev->my_tx_tasks[(ttn)->frame_type] = NULL; + + debugFree((ttn), -300169); + + return YES; + } + + (ttn)->tx_timestamp = bmx_time; + + return NO; +} + + +void schedule_tx_task(struct dev_node *dev_out, struct link_dev_node *lndev_out, + uint16_t frame_type, uint16_t frame_data_len, SQN_T sqn, IID_T myIID4x, IID_T neighIID4x) +{ + struct tx_task_node *ttn; + IP4_T dest_ip4 = lndev_out && lndev_out->link ? lndev_out->link->llip4 : 0; + +// struct link_node *ln_out = lndev_out ? lndev_out->link : NULL; + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s %s %s sqn %d myIID4x %d neighIID4x %d", + frame_handler[frame_type].name, ipStr(dest_ip4), dev_out->name, sqn, myIID4x, neighIID4x); + + if (frame_handler[ frame_type ].min_rtq > (lndev_out ? lndev_out->mr[SQR_RTQ].val : 0)) { + return; + } + + assertion(-500380, (myIID4x != myIID4me)); // should have been translated to IID_RSVD_4YOU by calling function + + assertion(-500433, (frame_handler[frame_type].tx_iterations)); + + if (myIID4x == IID_RSVD_4YOU) { + + if ((ttn = dev_out->my_tx_tasks[frame_type])) { + + dbgf(DBGL_SYS, DBGT_WARN, "my_tx_task for %s %s myIID4x %d neighIID4x %d dst %s NOT NULL", + frame_handler[frame_type].name, dev_out->name, myIID4x, neighIID4x, ipStr(dest_ip4)); + + assertion(-500442, (ttn->myIID4x == IID_RSVD_4YOU)); + + dev_out->tx_frames_data_len_target -= ttn->frame_data_length_target; + + } else { + ttn = dev_out->my_tx_tasks[frame_type] = debugMalloc(sizeof ( struct tx_task_node), -300026); + } + + } else { + + ttn = debugMalloc(sizeof ( struct tx_task_node), -300026); + list_add_tail(&(dev_out->tx_tasks_list[frame_type]), &ttn->list); + } + + ttn->sqn = sqn; + ttn->dst_ip4 = dest_ip4; + ttn->myIID4x = myIID4x; + ttn->neighIID4x = neighIID4x; + ttn->frame_type = frame_type; + ttn->tx_iterations = frame_handler[frame_type].tx_iterations; + ttn->tx_timestamp = bmx_time - 1; + ttn->dev = dev_out; + + if (!frame_data_len && frame_handler[frame_type].fixed_msg_size && !frame_handler[frame_type].data_header_size) { + + ttn->frame_data_length_target = frame_handler[ frame_type ].min_msg_size; + + } else { + + assertion( -500371, (frame_data_len)); + + ttn->frame_data_length_target = frame_data_len; + } + + dev_out->tx_frames_data_len_target += ttn->frame_data_length_target; + + + uint16_t estimated_packet_len = + (sizeof (struct packet_header)) + + (sizeof (struct frame_header) * FRAME_TYPE_ARRSZ) + + (dev_out->tx_frames_data_len_target) + + (sizeof (struct msg_ogm_adv) * ogm_aggreg_pending); + + + if (estimated_packet_len > max_udpd_size) { + remove_task(tx_packet, dev_out); + register_task(1, tx_packet, dev_out); + } + + +} + + + + +void purge_dev_tx_list( struct dev_node *dev ) { + int i; + + paranoia( -500203, !dev ); + + for (i = 0; i < FRAME_TYPE_ARRSZ; i++) { + + struct tx_task_node * dtn; + + while ((dtn=list_rem_head( &(dev->tx_tasks_list[i]) ) )) + debugFree(dtn, -300066); + + if ((dtn = dev->my_tx_tasks[i])) { + debugFree(dtn, -300066); + dev->my_tx_tasks[i]=NULL; + } + + } +} + +STATIC_FUNC +void create_ogm_aggregation(void) +{ + + dbgf(DBGL_CHANGES, DBGT_INFO, " "); + + uint32_t n = 0; + + uint32_t target_aggregations = MIN(MAX_OGMS_PER_AGGREG, + ((ogm_aggreg_pending < ((DEF_OGMS_PER_AGGREG / 3)*4)) ? ogm_aggreg_pending : DEF_OGMS_PER_AGGREG)); + + struct msg_ogm_adv* ogm = debugMalloc(target_aggregations * sizeof (struct msg_ogm_adv), -300177); + + static IID_T curr_iid = IID_MIN_USED; + + if (curr_iid >= my_iid_repos.max_free) + curr_iid = IID_MIN_USED; + + IID_T start_iid = curr_iid; + + dbgf(DBGL_CHANGES, DBGT_INFO, "pending %d target %d start %d", + ogm_aggreg_pending, target_aggregations, start_iid); + + do { + IID_NODE_T *dhn = my_iid_repos.arr.node[curr_iid]; + struct orig_node *on; + int warn = 0; + + while (dhn && (on = dhn->on) && + GREAT_SQN(on->ogm_sqn_to_be_send & on->path_metric_algo.sqn_mask, on->ogm_sqn_aggregated)) { + + if (GREAT_SQN(on->ogm_sqn_to_be_send & on->path_metric_algo.sqn_mask, + on->ogm_sqn_aggregated + on->path_metric_algo.sqn_steps)) + warn++; + + dbgf(warn ? DBGL_CHANGES : DBGL_ALL, warn ? DBGT_WARN : DBGT_INFO, "%s %s %d < %d", + on->id.name, warn ? "delayed!" : "in-time", + on->ogm_sqn_aggregated, on->ogm_sqn_to_be_send); + + on->ogm_sqn_aggregated = on->ogm_sqn_to_be_send & on->path_metric_algo.sqn_mask; + + ogm[n].orig_sqn = htons(on->ogm_sqn_to_be_send); + ogm[n].transmitterIID4x = htons(dhn->myIID4orig); + + if ((++n) == target_aggregations) + goto create_ogm_aggregation_done; + } + + if ((++curr_iid) >= my_iid_repos.max_free) + curr_iid = IID_MIN_USED; + + } while (curr_iid != start_iid); + +create_ogm_aggregation_done: + + if (curr_iid == start_iid) + ogm_aggreg_pending = 0; + else + ogm_aggreg_pending -= n; + + if (ogm_aggreg_pending) { + dbgf(DBGL_SYS, DBGT_WARN, "%d ogms left for immediate next aggregation", ogm_aggreg_pending); + } + + if (!n) { + debugFree( ogm, -300219); + return; + } + + struct ogm_aggreg_node *oan = debugMalloc(sizeof (struct ogm_aggreg_node), -300179); + oan->aggregated_ogms = n; + oan->ogm_advs = ogm; + oan->tx_attempts = 0; + oan->tx_timestamp = (bmx_time - DEF_OGM_RESEND_INTERVAL); + oan->sqn = ++ogm_aggreg_sqn_max; + + dbgf(DBGL_CHANGES, DBGT_INFO, "aggregation_sqn %d aggregated_ogms %d", oan->sqn, n); + + list_add_tail(&ogm_aggreg_list, &oan->list); + + struct avl_node *an = NULL; + struct neigh_node *nn; + + while ((nn = avl_iterate_item(&neigh_tree, &an))) { + + bit_set(nn->ogm_aggregations_acked, OGM_AGGREG_SQN_CACHE_RANGE, ogm_aggreg_sqn_max, 0); + + } + + return; +} + +STATIC_FUNC +struct link_dev_node **get_best_lndevs_by_criteria(struct ogm_aggreg_node *oan_criteria, struct dhash_node *dhn_criteria) +{ + + static struct link_dev_node **lndev_arr = NULL; + static uint16_t lndev_arr_items = 0; + struct avl_node *an; + struct neigh_node *nn; + struct dev_node *dev; + uint16_t d = 0; + + if (lndev_arr_items < dev_ip4_tree.items + 1) { + + if (lndev_arr) + debugFree(lndev_arr, -300180); + + lndev_arr_items = dev_ip4_tree.items + 1; + lndev_arr = debugMalloc((lndev_arr_items * sizeof (struct link_dev_node*)), -300182); + } + + if ( oan_criteria || dhn_criteria ) { + + if (oan_criteria) { + dbgf(DBGL_CHANGES, DBGT_INFO, "aggreg_sqn %d ", oan_criteria->sqn); + } else if (dhn_criteria) { + dbgf(DBGL_CHANGES, DBGT_INFO, "NOT %s ", dhn_criteria->on->id.name); + } + + for (an = NULL; (dev = avl_iterate_item(&dev_ip4_tree, &an));) + dev->misc_flag = NO; + + + for (an = NULL; (nn = avl_iterate_item(&neigh_tree, &an));) { + + if (oan_criteria && + (bit_get(nn->ogm_aggregations_acked, OGM_AGGREG_SQN_CACHE_RANGE, oan_criteria->sqn) || + nn->best_rtq->mr[SQR_RTQ].val <= MIN_OGM_ACK_RTQ) ) + continue; + + if (dhn_criteria && dhn_criteria->neigh == nn) + continue; + + assertion(-500445, (nn->best_rtq)); + assertion(-500446, (nn->best_rtq->key.dev)); + assertion(-500447, (nn->best_rtq->key.dev->active)); + + dbgf(DBGL_CHANGES, DBGT_INFO, " via %s to %s (redundant %d)", + nn->best_rtq->key.dev->name, nn->best_rtq->link->llip4_str, + nn->best_rtq->key.dev->misc_flag); + + if (nn->best_rtq->key.dev->misc_flag == NO) { + + lndev_arr[d++] = nn->best_rtq; + + nn->best_rtq->key.dev->misc_flag = YES; + } + + assertion(-500444, (d <= dev_ip4_tree.items)); + + } + } + + lndev_arr[d] = NULL; + + return lndev_arr; +} + +STATIC_FUNC +void schedule_and_purge_ogm_aggregations(struct dev_node *dev) +{ + static uint32_t timestamp = 0; + + if (dev) { + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s max %d active aggregations %d pending ogms %d expiery in %d ms", + dev->name, ogm_aggreg_sqn_max, ogm_aggreg_list.items, ogm_aggreg_pending, + (DEF_OGM_AGGREG_INTERVAL - ((uint32_t) (bmx_time - timestamp)))); + + while (ogm_aggreg_pending && ((((uint32_t) bmx_time - timestamp)) >= DEF_OGM_AGGREG_INTERVAL)) { + + struct ogm_aggreg_node *oan = list_get_first(&ogm_aggreg_list); + + if (oan && ((SQN_T) ((ogm_aggreg_sqn_max + 1) - oan->sqn)) >= OGM_AGGREG_SQN_CACHE_RANGE) { + + dbgf(DBGL_SYS, DBGT_WARN, + "ogm_aggreg_list full min %d max %d items %d unaggregated %d", + oan->sqn, ogm_aggreg_sqn_max, ogm_aggreg_list.items, ogm_aggreg_pending); + + debugFree(oan->ogm_advs, -300185); + debugFree(oan, -300186); + list_del_next(&ogm_aggreg_list, ((struct list_node*) & ogm_aggreg_list)); + } + + create_ogm_aggregation(); + + if (!ogm_aggreg_pending) + timestamp = bmx_time; + } + } + +#ifndef NO_PARANOIA + if (!ogm_aggreg_pending) { + IID_T i; + struct dhash_node *dhn; + for (i = IID_MIN_USED; i < my_iid_repos.max_free && (dhn = my_iid_repos.arr.node[i]); i++) { + struct orig_node * on = dhn->on; + assertion(-500473, + (!(on && GREAT_SQN(on->ogm_sqn_to_be_send & on->path_metric_algo.sqn_mask, on->ogm_sqn_aggregated)))); + } + } +#endif + + struct list_node *lpos, *tpos, *lprev = (struct list_node*) & ogm_aggreg_list; + + list_for_each_safe(lpos, tpos, &ogm_aggreg_list) + { + struct ogm_aggreg_node *oan = list_entry(lpos, struct ogm_aggreg_node, list); + + if (dev == NULL/*purge_all*/ || + ((((uint32_t) (bmx_time - oan->tx_timestamp)) > DEF_OGM_RESEND_INTERVAL) && + oan->tx_attempts > ogm_resend_attempts)) { + + list_del_next(&ogm_aggreg_list, lprev); + debugFree(oan->ogm_advs, -300183); + debugFree(oan, -300184); + + continue; + + } else if ((((uint32_t) (bmx_time - oan->tx_timestamp)) >= DEF_OGM_RESEND_INTERVAL) && + oan->tx_attempts <= ogm_resend_attempts) { + + struct link_dev_node **lndev_arr = get_best_lndevs_by_criteria(oan, NULL); + int d; + + for (d = 0; (lndev_arr[d]); d++) { + schedule_tx_task((lndev_arr[d])->key.dev, NULL, FRAME_TYPE_OGM0_ADVS, + (sizeof (struct hdr_ogm_adv) + (oan->aggregated_ogms * sizeof (struct msg_ogm_adv))), + oan->sqn, 0, 0); + } + + if (!lndev_arr[0]) + oan->tx_attempts = ogm_resend_attempts; + + oan->tx_timestamp = bmx_time; + oan->tx_attempts++; + } + + lprev = lpos; + } +} + + +STATIC_FUNC +int tx_msg_hello40_reply(struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size) +{ + struct msg_hello_reply *hey0_rep = (struct msg_hello_reply *) (tx_buff); + + hey0_rep->receiver_ip4 = ttn->dst_ip4; + hey0_rep->hello_dev_sqn = htons(ttn->sqn); + + dbgf(DBGL_CHANGES, DBGT_INFO, "dev %s %s to %s SQN %d",// dest_sid: %d", + ttn->dev->name, ttn->dev->ip4_str, ipStr(ttn->dst_ip4), ttn->sqn/*, dtn->my_id_for_neigh*/); + + return sizeof (struct msg_hello_reply); +} + + + +STATIC_FUNC +int tx_msg_helloX0_adv(struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size) +{ + + struct msg_hello_adv *hey0_adv = (struct msg_hello_adv *) (tx_buff); + + hey0_adv->hello_dev_sqn = htons(++(ttn->dev->ogm_sqn)); + + struct avl_node *it; + struct link_dev_node *lndev; + + for (it = NULL; (lndev = avl_iterate_item(&link_dev_tree, &it));) { + // we dont want to clean the sqr of other devs + // update_link_node(ln, ttn->dev, ttn->dev->ogm_sqn, ttn->dev->ogm_sqn, SQR_RTQ, 0); + if (lndev->key.dev != ttn->dev) + continue; + +// update_lounged_metric(0, local_rtq_lounge, ttn->dev->ogm_sqn, ttn->dev->ogm_sqn, &lndev->sqr[SQR_RTQ], local_lws); + + update_metric(&lndev->mr[SQR_RTQ], &link_metric_algo[SQR_RTQ], ttn->dev->ogm_sqn, ttn->dev->ogm_sqn, 0); + } + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s %s SQN %d", ttn->dev->name, ttn->dev->ip4_str, ttn->dev->ogm_sqn); + + return sizeof (struct msg_hello_adv); +} + +STATIC_FUNC +int tx_msg_dhash0_or_description0_request(struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size) +{ + assertion( -500366 , (sizeof ( struct msg_description_request) == sizeof( struct msg_dhash_request))); + + struct msg_dhash_request *req = (struct msg_dhash_request *) (tx_buff); + + req->receiver_ip4 = ttn->dst_ip4; + req->receiverIID4x = htons(ttn->neighIID4x); + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s oif %s to %s requesting orig_did %d", + frame_handler[ttn->frame_type].name, ttn->dev->name, ipStr(ttn->dst_ip4), ttn->neighIID4x); + + return sizeof ( struct msg_description_request); +} + + +STATIC_FUNC +int tx_msg_description0_adv(struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size) +{ + struct dhash_node *dhn = NULL; + struct msg_description_adv *desc0_adv = (struct msg_description_adv *) tx_buff; + struct description *desc0; + + dbgf_all(DBGT_INFO, "ttn->myIID4x %d", ttn->myIID4x); + + if (ttn->myIID4x == IID_RSVD_4YOU) { + + desc0_adv->transmitterIID4x = htons(myIID4me); + desc0 = my_orig_node.desc0; + *flags |= FRAME_FLAG_firstIsSender; + + } else if ((dhn = iid_get_node_by_myIID4x(ttn->myIID4x)) && dhn->on) { + + assertion(-500437, (dhn->on && dhn->on->desc0)); + + desc0_adv->transmitterIID4x = htons(ttn->myIID4x); + desc0 = dhn->on->desc0; + + } else { + + dbgf(DBGL_SYS, DBGT_WARN, "unknown myIID4x %d !", ttn->myIID4x); + return FAILURE; + } + + uint16_t tlvs_len = ntohs(desc0->dsc_tlvs_len); + + if (sizeof (struct msg_description_adv) + tlvs_len > buff_size) + return (buff_size + 1); + + memcpy((char*) & desc0_adv->desc, (char*) desc0, sizeof (struct description) + tlvs_len); + + return sizeof (struct msg_description_adv) + tlvs_len; +} + + + + +STATIC_FUNC +int tx_msg_dhash0_adv(struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size) +{ + struct msg_dhash_adv *dhash0_adv = (struct msg_dhash_adv *) tx_buff; + struct dhash_node *dhn; + + if (ttn->myIID4x == IID_RSVD_4YOU) { + + dhash0_adv->transmitterIID4x = htons(myIID4me); + dhn = my_orig_node.dhn; + *flags |= FRAME_FLAG_firstIsSender; + + } else if ((dhn = iid_get_node_by_myIID4x(ttn->myIID4x)) && dhn->on) { + + assertion(-500259, (dhn->on && dhn->on->desc0)); + + dhash0_adv->transmitterIID4x = htons(ttn->myIID4x); + + } else { + + dbgf(DBGL_SYS, DBGT_WARN, "unknown myIID4x %d !", ttn->myIID4x); + return FAILURE; + } + + memcpy((char*) & dhash0_adv->dhash, (char*) & dhn->dhash, sizeof ( struct description_hash)); + + return sizeof (struct msg_dhash_adv); +} + + + + +STATIC_FUNC +int tx_frame_ogm0_advs(struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size) +{ + struct list_node *list_pos; + + assertion(-500428, (ttn->frame_data_length_target <= buff_size)); + + dbgf(DBGL_CHANGES, DBGT_INFO, " aggregation_sqn %d", ttn->sqn ); + + list_for_each( list_pos, &ogm_aggreg_list ) + { + struct ogm_aggreg_node *oan = list_entry(list_pos, struct ogm_aggreg_node, list); + + if ( oan->sqn == ttn->sqn ) { + + ((struct hdr_ogm_adv*) tx_buff)->aggregation_sqn = htons(ttn->sqn); + + memcpy(tx_buff + sizeof (struct hdr_ogm_adv), + oan->ogm_advs, oan->aggregated_ogms * sizeof (struct msg_ogm_adv)); + + assertion(-500429, (ttn->frame_data_length_target == + (sizeof (struct hdr_ogm_adv) + oan->aggregated_ogms * sizeof (struct msg_ogm_adv)))); + + return ttn->frame_data_length_target; + + } + } + + return FAILURE; +} + + +STATIC_FUNC +int tx_msg_ogm_ack(struct tx_task_node *ttn, uint8_t *flags, uint8_t *tx_buff, uint16_t buff_size) +{ + struct msg_ogm_ack *ack = (struct msg_ogm_ack *) tx_buff; + + ack->receiver_ip4 = ttn->dst_ip4; + ack->aggregation_sqn = htons(ttn->sqn); + + dbgf(DBGL_CHANGES, DBGT_INFO, " aggreg_sqn %d to %s", ttn->sqn, ipStr(ttn->dst_ip4)); + + return sizeof (struct msg_ogm_ack); +} + + +STATIC_FUNC +int rx_frame_ogm0_advs(struct packet_buff *pb, struct frame_header *frame) +{ + struct hdr_ogm_adv *hdr = (struct hdr_ogm_adv *) frame->data; + struct msg_ogm_adv *ogm = hdr->msg; + struct neigh_node *nn = pb->ln->neigh; + + SQN_T aggregation_sqn = ntohs(hdr->aggregation_sqn); + + uint16_t msgs = (frame->length - (sizeof (struct frame_header) + sizeof (struct hdr_ogm_adv))) / + sizeof (struct msg_ogm_adv); + + dbgf(DBGL_CHANGES, DBGT_INFO, " "); + + if (!nn) { + dbgf(DBGL_CHANGES, DBGT_INFO, "via unknown neigh %s", pb->neigh_str ); + schedule_tx_task(pb->iif, pb->lndev, FRAME_TYPE_DHS0_REQS, 0, 0, 0, IID_RSVD_4YOU); + return frame->length; + } + + schedule_tx_task(nn->best_rtq->key.dev, nn->best_rtq, FRAME_TYPE_OGM0_ACKS, 0, aggregation_sqn, 0, 0); + + if ((SQN_T) (nn->ogm_aggregation_rcvd_max - aggregation_sqn) < OGM_AGGREG_SQN_CACHE_RANGE) { + + if (bit_get(nn->ogm_aggregations_rcvd, OGM_AGGREG_SQN_CACHE_RANGE, aggregation_sqn)) { + + dbgf(DBGL_CHANGES, DBGT_INFO, "already known ogm_aggregation_sqn %d from neigh %s", + aggregation_sqn, nn->dhn->on->id.name); + + return frame->length; + } + + if (((uint16_t) (nn->ogm_aggregation_rcvd_max - aggregation_sqn)) > OGM_AGGREG_SQN_CACHE_WARN) { + + dbgf( DBGL_SYS, DBGT_WARN, "neigh %s with unknown %s aggregation_sqn %d max %d ogms %d", + pb->neigh_str, "OLD", aggregation_sqn, nn->ogm_aggregation_rcvd_max, msgs); + } + + bit_set(nn->ogm_aggregations_rcvd, OGM_AGGREG_SQN_CACHE_RANGE, aggregation_sqn, 1); + + } else { + + if (((uint16_t) (aggregation_sqn - nn->ogm_aggregation_rcvd_max)) > OGM_AGGREG_SQN_CACHE_WARN) { + + dbgf( DBGL_SYS, DBGT_WARN, "neigh %s with unknown %s aggregation_sqn %d max %d ogms %d", + pb->neigh_str, "LOST", aggregation_sqn, nn->ogm_aggregation_rcvd_max, msgs ); + } + + bit_clear(nn->ogm_aggregations_rcvd, OGM_AGGREG_SQN_CACHE_RANGE, nn->ogm_aggregation_rcvd_max + 1, aggregation_sqn); + + bit_set(nn->ogm_aggregations_rcvd, OGM_AGGREG_SQN_CACHE_RANGE, aggregation_sqn, 1); + nn->ogm_aggregation_rcvd_max = aggregation_sqn; + + } + + dbgf(DBGL_CHANGES, DBGT_INFO, "neigh %s with unknown %s aggregation_sqn %d max %d ogms %d", + pb->neigh_str, "NEW", aggregation_sqn, nn->ogm_aggregation_rcvd_max, msgs); + + uint16_t m; + for (m = 0; m < msgs; m++) { + + IID_T neighIID4x = ntohs(ogm[m].transmitterIID4x); + SQN_T orig_sqn = ntohs(ogm[m].orig_sqn); + + IID_NODE_T *dhn = iid_get_node_by_neighIID4x(nn, neighIID4x ); + struct orig_node *on = NULL; + + if ( dhn == my_orig_node.dhn ) + continue; + + + if (dhn && (on = dhn->on) && ((SQN_T) (orig_sqn - on->ogm_sqn_min)) < on->ogm_sqn_range) { + + dbgf(DBGL_CHANGES, DBGT_INFO, " new orig_sqn %d / %d from %s neighIID4x %d via %s ", + orig_sqn, on->ogm_sqn_to_be_send, on->id.name, neighIID4x, pb->neigh_str); + + update_orig_metrics(pb, on, orig_sqn); + + + } else { + dbgf(!dhn ? DBGL_CHANGES : DBGL_SYS, DBGT_WARN, + " %s orig_sqn %d or neighIID4x %d via %s orig %s sqn_min %d sqn_range %d", + !dhn ? "UNKNOWN" : on ? "EXCEEDED OGM_SQN RANGE" : "INVALIDATED", + orig_sqn, neighIID4x, pb->neigh_str, on ? on->id.name:"---", + on ? on->ogm_sqn_min : 0, on ? on->ogm_sqn_range : 0); + + if (dhn && dhn->on) + invalidate_dhash_node(dhn); + + schedule_tx_task(nn->best_rtq->key.dev, nn->best_rtq, FRAME_TYPE_DHS0_REQS, 0, 0, 0, neighIID4x); + } + } + + return frame->length; +} + +STATIC_FUNC +int rx_frame_ogm40_acks(struct packet_buff *pb, struct frame_header *frame) +{ + struct msg_ogm_ack *ack = (struct msg_ogm_ack *) frame->data; + struct neigh_node *nn = pb->ln->neigh; + + uint16_t msgs = (frame->length - (sizeof (struct frame_header))) / sizeof (struct msg_ogm_ack); + + if (!nn) { + dbgf(DBGL_CHANGES, DBGT_WARN, "%s neigh %s", "unknown", pb->neigh_str); + schedule_tx_task(pb->iif, pb->lndev, FRAME_TYPE_DHS0_REQS, 0, 0, 0, IID_RSVD_4YOU); + return frame->length; + } + + uint16_t m; + for (m = 0; m < msgs; m++) { + + if ( ack[m].receiver_ip4 != pb->iif->ip4_addr ) + continue; + + SQN_T aggregation_sqn = ntohs(ack[m].aggregation_sqn); + + if (((uint32_t) (ogm_aggreg_sqn_max - aggregation_sqn) < OGM_AGGREG_SQN_CACHE_RANGE)) { + + bit_set(pb->ln->neigh->ogm_aggregations_acked, OGM_AGGREG_SQN_CACHE_RANGE, aggregation_sqn, 1); + + dbgf(DBGL_CHANGES, DBGT_INFO, "neigh %s sqn %d <= sqn_max %d", + pb->neigh_str, aggregation_sqn, ogm_aggreg_sqn_max); + + + } else { + + dbgf(DBGL_SYS, DBGT_ERR, "neigh %s sqn %d <= sqn_max %d", + pb->neigh_str, aggregation_sqn, ogm_aggreg_sqn_max); + + } + } + return frame->length; +} + + +STATIC_FUNC +struct dhash_node * +process_dhash_description_neighIID4x +(struct packet_buff *pb, struct description_hash *dhash, struct description *dsc, IID_T neighIID4x, IDM_T is_sender) +{ + struct dhash_node *orig_dhn = NULL; + struct link_node *ln = pb->ln; + IDM_T invalid = NO; + struct description *cache = NULL; + + if (avl_find(&dhash_invalid_tree, dhash)) { + + invalid = YES; + + } else if ((orig_dhn = avl_find_item(&dhash_tree, dhash))) { + + if (ln->neigh) { + + // this was just for testing eh??? assertion(-500375, (ln->neigh->dhn == orig_dhn)); + + if (orig_dhn == my_orig_node.dhn) { + + dbgf(DBGL_CHANGES, DBGT_INFO, + "msg refers myself via %s neighIID4neigh %d neighIID4me %d", + pb->neigh_str, ln->neigh->neighIID4neigh, neighIID4x); + + if (is_sender) + return FAILURE_POINTER; + + + } else if (orig_dhn == ln->neigh->dhn) { + + if (!is_sender) { + + dbgf(DBGL_SYS, DBGT_ERR, "%s via %s IS NOT sender (%d != %d)", + orig_dhn->on->id.name, pb->neigh_str, + ln->neigh->neighIID4neigh, neighIID4x); + + return FAILURE_POINTER; + + } else if((ln->neigh->neighIID4neigh != neighIID4x)) { + + dbgf(DBGL_CHANGES, DBGT_WARN, "%s via %s first contact NOT VIA SENDER ??? %d != %d", + orig_dhn->on->id.name, pb->neigh_str, + ln->neigh->neighIID4neigh, neighIID4x); + + } + } + + if (is_sender) + update_neigh_node(ln, orig_dhn, neighIID4x); + + else if (update_neighIID4x_repository(ln->neigh, neighIID4x, orig_dhn) == FAILURE) + return FAILURE_POINTER; + + + } else { + + if (is_sender) { + update_neigh_node(ln, orig_dhn, neighIID4x); + } else { + //update_neigh_node(ln, dhn, IID_RSVD_UNUSED); + //update_neigh_iid_repos(ln->neigh, neighIID4x, dhn); + } + } + + } else { + + // its just the easiest to cache and remove because cache description is doing all the checks for us + if (dsc) + cache_description(dsc, dhash); + + if (is_sender && (cache = rem_cached_description(dhash))) { + + if ((orig_dhn = process_description(pb, cache, dhash))) { + + update_neigh_node(ln, orig_dhn, neighIID4x); + } + + } else if (ln->neigh && (cache = rem_cached_description(dhash))) { + + orig_dhn = process_description(pb, cache, dhash); + + if (orig_dhn && update_neighIID4x_repository(ln->neigh, neighIID4x, orig_dhn) == FAILURE) + return FAILURE_POINTER; + + } + } + + + dbgf(DBGL_CHANGES, DBGT_INFO, "via dev: %s NB %s:dhash %8X.. %s neighIID4x %d is_sender %d %s", + pb->iif->name, pb->neigh_str, dhash->h.u32[0], + (dsc ? "DESCRIPTION" : (cache ? "CACHED_DESCRIPTION" : (orig_dhn?"KNOWN":"UNDESCRIBED"))), + neighIID4x, is_sender, + invalid ? "INVALIDATED" : (orig_dhn && orig_dhn->on ? orig_dhn->on->id.name : "---")); + + + return orig_dhn; +} + + +STATIC_FUNC +int rx_frame_dhash0_advs(struct packet_buff *pb, struct frame_header *frame) +{ + + assertion(-500373, (pb->ln && pb->lndev && pb->lndev->mr[SQR_RTQ].val >= MIN_NBDISC_RTQ)); + + int m, n = (frame->length - sizeof (struct frame_header)) / sizeof ( struct msg_dhash_adv); + + for (m = 0; m < n; m++) { + + struct msg_dhash_adv *msg = &(((struct msg_dhash_adv*) (frame->data))[m]); + IDM_T is_sender = (m == 0 && (frame->flags & FRAME_FLAG_firstIsSender)); + IID_T neighIID4x = ntohs(msg->transmitterIID4x); + struct dhash_node *dhn; + + if ( neighIID4x <= IID_RSVD_MAX ) + return FAILURE; + + dhn = process_dhash_description_neighIID4x(pb, &msg->dhash, NULL, neighIID4x, is_sender); + + if (dhn == FAILURE_POINTER) { + + return FAILURE; + + } else if (!dhn) { + + schedule_tx_task(pb->iif, pb->lndev, FRAME_TYPE_DSC0_REQS, 0, 0, 0, neighIID4x); + + }/* else if (dhn && is_sender && !pb->ln->neigh) { + + schedule_tx_task(pb->iif, pb->lndev, FRAME_TYPE_DHS0_REQS, 0, 0, 0, IID_RSVD_4YOU); + }*/ + + paranoia(-500488, (dhn && is_sender && !pb->ln->neigh)); + + + } + return frame->length; +} + + +STATIC_FUNC +int rx_frame_description0_advs(struct packet_buff *pb, struct frame_header *frame) +{ + + uint16_t pos = sizeof ( struct frame_header); + + struct description_hash dhash0; + + while (pos + sizeof ( struct msg_description_adv) <= frame->length) { + + IDM_T is_sender = (pos == sizeof ( struct frame_header) && (frame->flags & FRAME_FLAG_firstIsSender)); + struct msg_description_adv *adv = ((struct msg_description_adv*) (((char*)frame)+pos)); + struct description *desc0 = &adv->desc; + uint16_t tlvs_len = ntohs(desc0->dsc_tlvs_len); + IID_T neighIID4x = ntohs(adv->transmitterIID4x); + struct dhash_node *dhn; + + if ( neighIID4x <= IID_RSVD_MAX ) + return FAILURE; + + if (tlvs_len > MAX_DESC0_TLV_SIZE || + pos + sizeof ( struct msg_description_adv) + tlvs_len > frame->length) { + + dbgf( DBGL_SYS, DBGT_ERR, "illegal pos %d + %lu tlvs_len %d > frm_size %d", + pos, sizeof ( struct msg_description_adv), tlvs_len, frame->length ); + + return FAILURE; + } + + ShaUpdate(&bmx_sha, (byte*) desc0, (sizeof (struct description) + tlvs_len)); + ShaFinal(&bmx_sha, (byte*)&dhash0); + + dhn = process_dhash_description_neighIID4x(pb, &dhash0, desc0, neighIID4x, is_sender); + + dbgf(DBGL_CHANGES, DBGT_INFO, "rcvd %s desc0: %jX via %s NB %s", + dhn ? "accepted" : "denied", desc0->id.rand.u64[0], pb->iif->name, pb->neigh_str); + + + if (dhn == FAILURE_POINTER) { + + return FAILURE; + + } else if (dhn && dhn->on->updated_timestamp == bmx_time && pb->ln->neigh && DEF_UNSOLICITED_DESCRIPTIONS) { + + struct link_dev_node **lndev_arr = get_best_lndevs_by_criteria(NULL, pb->ln->neigh->dhn); + int d; + + uint16_t desc_len = sizeof ( struct msg_description_adv) + ntohs(dhn->on->desc0->dsc_tlvs_len); + + for (d = 0; (lndev_arr[d]); d++) + schedule_tx_task(lndev_arr[d]->key.dev, lndev_arr[d], FRAME_TYPE_DSC0_ADVS, desc_len, 0, dhn->myIID4orig, 0); + + + } + + + paranoia(-500379, (dhn && is_sender && !pb->ln->neigh)); + + pos += sizeof ( struct msg_description_adv) + tlvs_len; + } + + if (frame->length != pos) + return FAILURE; + + + return frame->length; +} + +STATIC_FUNC +int rx_frame_dhash0_or_description0_requests(struct packet_buff *pb, struct frame_header *frame) +{ + assertion( -500365 , (sizeof( struct msg_description_request ) == sizeof( struct msg_dhash_request))); + + int m, n = (frame->length - sizeof (struct frame_header)) / sizeof ( struct msg_description_request); + + uint8_t frame_type = frame->type; + + + for (m = 0; m < n; m++) { + + struct msg_description_request *req = &(((struct msg_description_request*) (frame->data))[m]); + IID_T myIID4x = ntohs(req->receiverIID4x); + uint16_t desc0_len = 0; + + dbg(DBGL_CHANGES, DBGT_INFO, "%s dest_llip4 %s myIID4x %d", + frame_handler[frame_type].name, ipStr(req->receiver_ip4), myIID4x); + + if ( req->receiver_ip4 != pb->iif->ip4_addr ) // if I am not asked + continue; + + + if (myIID4x == IID_RSVD_4YOU || myIID4x == myIID4me) { + + myIID4x = IID_RSVD_4YOU; + + desc0_len = sizeof ( struct msg_description_adv) + my_desc0_tlv_len; + + } else { // if I am asked for somebody else description + + struct dhash_node *dhn = iid_get_node_by_myIID4x(myIID4x); + + assertion(-500270, (!(dhn && dhn->on && !dhn->on->desc0))); + + if (myIID4x <= IID_RSVD_MAX || !dhn || !dhn->on || + ((uint32_t) (bmx_time - dhn->referred_timestamp)) > DEF_DESC0_REFERRED_TO) { + + dbgf(DBGL_SYS, DBGT_WARN, + "%s from %s via %s myIID4x %d requesting %s", + frame_handler[frame_type].name, pb->ln->llip4_str, pb->iif->name, myIID4x, + !dhn ? "UNKNOWN" : (dhn->on ? "OUTDATED" : "INVALID")); + + continue; + } + + assertion(-500251, (dhn && dhn->on && dhn->myIID4orig == myIID4x)); + desc0_len = sizeof ( struct msg_description_adv) + ntohs(dhn->on->desc0->dsc_tlvs_len); + } + + + if ( frame_type == FRAME_TYPE_DSC0_REQS) { + + schedule_tx_task(pb->iif, pb->lndev, FRAME_TYPE_DSC0_ADVS, desc0_len, 0, myIID4x, 0); + + } else { + + schedule_tx_task(pb->iif, pb->lndev, FRAME_TYPE_DHS0_ADVS, 0, 0, myIID4x, 0); + } + } + + return frame->length; +} + +STATIC_FUNC +int rx_frame_hello40_replies(struct packet_buff *pb, struct frame_header *frame) +{ + + int found = 0, m, n = (frame->length - sizeof (struct frame_header)) / sizeof ( struct msg_hello_reply); + + for (m = 0; m < n; m++) { + + struct msg_hello_reply *msg = &(((struct msg_hello_reply*) (frame->data))[m]); + + SQN_T sqn = ntohs(msg->hello_dev_sqn); + + dbgf(DBGL_CHANGES, DBGT_INFO, "via NB %s dev %s %s to %s SQN %d", + pb->neigh_str, pb->iif->name, pb->iif->ip4_str, ipStr(msg->receiver_ip4), sqn); + + if ( msg->receiver_ip4 != pb->iif->ip4_addr ) + continue; + + + if ((SQN_T) (pb->iif->ogm_sqn - sqn) > (SQN_T) local_rtq_lounge) { + + dbgf(DBGL_SYS, DBGT_INFO, "DAD-Alert invalid Link-Local SQN %d!=%d from %s via %s", + sqn, pb->iif->ogm_sqn, pb->neigh_str, pb->iif->name); + + return FAILURE; + } + + found++; + + if( !pb->ln->neigh) + schedule_tx_task(pb->iif, pb->lndev, FRAME_TYPE_DHS0_REQS, 0, 0, 0, IID_RSVD_4YOU); + + update_link_node(pb->ln, pb->iif, sqn, pb->iif->ogm_sqn, SQR_RTQ, PROBE_RANGE); + + } + + if (found > 1) { + dbgf(DBGL_SYS, DBGT_WARN, + "rcvd %d %s messages in %d-bytes frame", + found, frame_handler[FRAME_TYPE_HI40_REPS].name, frame->length); + } + + return frame->length; + +} + + + +STATIC_FUNC +int rx_frame_helloX0_advs( struct packet_buff *pb, struct frame_header *frame ) +{ + + struct link_node *ln = pb->ln; + + int m, n = (frame->length - sizeof (struct frame_header)) / sizeof ( struct msg_hello_adv); + + if (frame->length != sizeof ( struct frame_header) + sizeof ( struct msg_hello_adv)) { + dbgf(DBGL_SYS, DBGT_WARN, + "rcvd %d %s messages in %d-bytes frame", + n, frame_handler[FRAME_TYPE_HI40_ADVS].name, frame->length); + } + + for (m = 0; m < n; m++) { + + struct msg_hello_adv *msg = &(((struct msg_hello_adv*) (frame->data))[m]); + + SQN_T sqn = ntohs(msg->hello_dev_sqn); + + dbgf(DBGL_CHANGES, DBGT_INFO, "NB %s via %s SQN %d ", pb->neigh_str, pb->iif->name, sqn); + + // skip updateing link_node if this SQN is known but not new + if ((ln->rq_sqn_max || ln->rq_time_max) && + ((SQN_T) (sqn - ln->rq_sqn_max) > SQN_DAD_RANGE) && + ((uint32_t) (bmx_time - ln->rq_time_max) < (uint32_t) dad_to)) { + + dbgf(DBGL_SYS, DBGT_INFO, "DAD-Alert NB %s via %s SQN %d rq_sqn_max %d", + pb->neigh_str, pb->iif->name, sqn, ln->rq_sqn_max); + + return FAILURE; + } + + schedule_tx_task(pb->iif, pb->lndev, FRAME_TYPE_HI40_REPS, 0, sqn, 0, 0); + + update_link_node(ln, pb->iif, sqn, sqn, SQR_RQ, PROBE_RANGE); + + } + + return frame->length; +} + + + +int rx_frames(struct packet_buff *pb, uint8_t* fdata, uint16_t fsize) +{ + + uint16_t frm_pos = 0; + int t = 0, pt = 0; + + struct frame_header *fhdr = NULL; + uint16_t flength = 0; + + while (frm_pos + sizeof ( struct frame_header) < fsize) { + + fhdr = (struct frame_header*) & (fdata[frm_pos]); + flength = fhdr->length = ntohs(fhdr->length); + int receptor_result; + + if ((t = fhdr->type) < pt || flength < sizeof ( struct frame_header) || flength + frm_pos > fsize) { + + goto rx_frames_frm_error; + } + + struct pkt_frame_handler *fhdl = &frame_handler[t]; + + dbgf(DBGL_CHANGES, DBGT_INFO, "type %s size %d flags 0x%X", + fhdl->name, flength, fhdr->flags); + + + if (t > FRAME_TYPE_NOP || !(fhdl->rx_frm_receptor)) { + + dbgf(DBGL_SYS, DBGT_WARN, "unsupported type %d ! maybe you need an update?", t); + + if (t > FRAME_TYPE_NOP) + goto rx_frames_frm_error; + + } else if (flength - sizeof (struct frame_header) < fhdl->data_header_size + fhdl->min_msg_size) { + + dbgf(DBGL_SYS, DBGT_WARN, + "too small size %d for type %s", flength, fhdl->name); + goto rx_frames_frm_error; + + } else if (fhdl->fixed_msg_size && + (flength - (sizeof (struct frame_header) + fhdl->data_header_size)) % fhdl->min_msg_size) { + + dbgf(DBGL_SYS, DBGT_WARN, + "nonmaching size %d for type %s", flength, fhdl->name); + goto rx_frames_frm_error; + + } else if (fhdl->min_rtq && (!pb->lndev || pb->lndev->mr[SQR_RTQ].val < fhdl->min_rtq)) { + + dbg_mute(60, DBGL_SYS, DBGT_WARN, + "non-sufficient bidirectional link %s - %s (rtq %d), skipping frame type %s", + pb->iif->ip4_str, pb->neigh_str, + pb->lndev ? pb->lndev->mr[SQR_RTQ].val : 0, fhdl->name); + + } else if (flength != (receptor_result = (*(fhdl->rx_frm_receptor)) (pb, fhdr))) { + + if (receptor_result == FAILURE) + blacklist_neighbor(pb); + + goto rx_frames_msg_error; + + } + + pt = t; + frm_pos += flength; + } + + if ( frm_pos != fsize ) + goto rx_frames_frm_error; + + return SUCCESS; + +rx_frames_msg_error: + dbgf(DBGL_SYS, DBGT_WARN, "rcvd problematic message"); + +rx_frames_frm_error: + dbgf(DBGL_SYS, DBGT_WARN, + "rcvd problematic frame type %s last %s frm_size %d pos %d ", + frame_handler[t].name, frame_handler[pt].name, flength, frm_pos); + + return FAILURE; +} + + + + +STATIC_FUNC +int8_t send_udp_packet( unsigned char *upd_data, int32_t udp_len, struct sockaddr_in *dst, int32_t send_sock ) { + + int status; + + dbgf_all( DBGT_INFO, "len %d", udp_len ); + + if ( send_sock == 0 ) + return 0; + + /* + static struct iovec iov; + iov.iov_base = udp_data; + iov.iov_len = udp_data_len; + + static struct msghdr m = { 0, sizeof( struct sockaddr_in ), &iov, 1, NULL, 0, 0 }; + m.msg_name = dst; + + status = sendmsg( send_sock, &m, 0 ); + */ + + status = sendto( send_sock, upd_data, udp_len, 0, (struct sockaddr *)dst, sizeof(struct sockaddr_in) ); + + if ( status < 0 ) { + + if ( errno == 1 ) { + + dbg_mute( 60, DBGL_SYS, DBGT_ERR, + "can't send udp packet: %s. Does your firewall allow outgoing packets on port %i ?", + strerror(errno), ntohs(dst->sin_port)); + + } else { + + dbg_mute( 60, DBGL_SYS, DBGT_ERR, "can't send udp packet via fd %d: %s", send_sock, strerror(errno)); + + } + + return -1; + } + + return 0; +} + + + +void tx_packet( void *dev_node ) +{ + static unsigned char tx_buff[MAX_UDPD_SIZE+1]; + struct dev_node *dev = dev_node; + struct packet_header *packet_hdr = (struct packet_header *) tx_buff; + memset( packet_hdr, 0, sizeof( tx_buff )); + uint16_t packet_size = sizeof( struct packet_header ); + IDM_T packet_full = NO; + + assertion( -500204, dev ); + assertion( -500205, dev->active ); + + //remove_task( tx_packet, dev ); + register_task(aggreg_interval - (1 + rand_num(aggreg_interval / 10)), tx_packet, dev); + + schedule_and_purge_ogm_aggregations(dev); + + uint16_t type = 0; // the currently processed frame_type + + while (type <= FRAME_TYPE_NOP) { + + uint16_t length = sizeof (struct frame_header); + uint8_t flags = 0; + + int creator_result; + struct pkt_frame_handler *fhdl = &frame_handler[type]; + struct tx_task_node *ttn; + IDM_T frame_full = NO; + + assertion(-500351, (!(fhdl->tx_frm_creator && fhdl->tx_msg_creator))); + + if ((ttn = dev->my_tx_tasks[type])) { + + assertion(-500422, (!fhdl->data_header_size)); + assertion(-500424, (fhdl->tx_msg_creator)); + assertion(-500441, (ttn->myIID4x == IID_RSVD_4YOU)); + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s type %d=%s %s", + dev->name, type, fhdl->name, "from dev->my_tx_tasks"); + + if (tx_task_obsolete(dev, type, ttn)) { + + creator_result = FAILURE; + + } else if (packet_full || + (fhdl->fixed_msg_size && + packet_size + length + ttn->frame_data_length_target > (uint16_t) max_udpd_size) + ) { + + creator_result = max_udpd_size + 1; + + } else { + + creator_result = (*(fhdl->tx_msg_creator)) + (ttn, &flags, (tx_buff + packet_size + length), + (max_udpd_size - (packet_size + length))); + + } + + if (creator_result > (max_udpd_size - (packet_size + length))) { + + assertion(-500431, + (packet_size > sizeof ( struct packet_header) || + length > sizeof (struct frame_header))); + + packet_full = YES; + + } else if (creator_result) { + + if (creator_result > 0) + length += creator_result; + + freed_tx_task_node(ttn, creator_result, NULL); + + } + } + + struct list_node *lprev = (struct list_node*) &(dev->tx_tasks_list[type]); + struct list_node *lpos, *ltmp; + + list_for_each_safe(lpos, ltmp, &(dev->tx_tasks_list[type])) + { + ttn = list_entry(lpos, struct tx_task_node, list); + + assertion(-500440, (ttn->frame_type == type)); + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s type %d=%s %s", + dev->name, type, fhdl->name, "from dev->tx_tasks_list"); + + if (ttn->tx_timestamp == bmx_time) { + + // just send! send again later; + creator_result = 0; + + } else if (tx_task_obsolete(dev, type, ttn)) { + + creator_result = FAILURE; + + } else if (packet_full || + (fhdl->fixed_msg_size && + packet_size + length + ttn->frame_data_length_target > (uint16_t) max_udpd_size) + ) { + + creator_result = max_udpd_size + 1; + + } else if (fhdl->tx_frm_creator) { + + creator_result = ((*(fhdl->tx_frm_creator)) + (ttn, &flags, (tx_buff + packet_size + length), + (max_udpd_size - (packet_size + length)))); + + frame_full = YES; + + } else { + + assertion(-500425, (fhdl->tx_msg_creator)); + assertion(-500426, (!fhdl->data_header_size)); // to be implemented... + + creator_result = (*(fhdl->tx_msg_creator)) + (ttn, &flags, (tx_buff + packet_size + length), + (max_udpd_size - (packet_size + length))); + + } + + if (creator_result > (max_udpd_size - (packet_size + length))) { + + assertion(-500430, + (packet_size > sizeof ( struct packet_header) || + length > sizeof (struct frame_header))); + + packet_full = YES; + break; + + } else if (creator_result) { + + if (creator_result > 0) + length += creator_result; + + if (freed_tx_task_node(ttn, creator_result, lprev) == NO) + lprev = lpos; + + if (frame_full) + break; + + continue; + + } else { + lprev = lpos; + } + } + + + if (length > sizeof (struct frame_header)) { + + struct frame_header *frame_hdr = (struct frame_header *) (tx_buff + packet_size); + packet_size += length; + + frame_hdr->type = type; + frame_hdr->length = htons(length); + frame_hdr->flags |= flags; + + dbgf(DBGL_ALL, DBGT_INFO, "send frame type %s size %d", fhdl->name, length); + } + + + if (packet_full || (type == FRAME_TYPE_NOP && packet_size > sizeof ( struct packet_header))) { + + assertion(-500208, (packet_size <= max_udpd_size)); + assertion(-500412, (packet_size >= sizeof ( struct packet_header))); + + packet_hdr->bmx_version = COMPAT_VERSION; + packet_hdr->bmx_capabilities = 0; + packet_hdr->pkt_length = htons(packet_size); + packet_hdr->pkt_dev_sqn = htons(++(dev->packet_sqn)); + + send_udp_packet(tx_buff, packet_size, &dev->ip4_netwbrc_addr, dev->unicast_sock); + + dbgf(DBGL_ALL, DBGT_INFO, "send packet size %d via dev %s", packet_size, dev->name); + + packet_size = sizeof ( struct packet_header); + packet_full = NO; + } + + if (type == FRAME_TYPE_NOP || + !dev->tx_tasks_list[type].items || + ((struct tx_task_node*) (list_get_last(&(dev->tx_tasks_list[type]))))->tx_timestamp == bmx_time) { + + CHECK_INTEGRITY(); + + type++; + } + } +} + + + +void schedule_my_hello_message( void* dev_node ) { + + struct dev_node * dev = dev_node; + + paranoia( -500206, !dev ); + paranoia( -500207, !dev->active ); + + register_task( my_hello_interval, schedule_my_hello_message, dev ); + + dbgf(DBGL_CHANGES, DBGT_INFO, "%s", dev->name); + + if (!LIST_EMPTY(&(dev->tx_tasks_list[FRAME_TYPE_HI40_ADVS]))) { + dbgf( DBGL_SYS, DBGT_ERR, " "); + } + + schedule_tx_task( dev, NULL, FRAME_TYPE_HI40_ADVS, 0, 0, 0, 0 ); +} + + +void schedule_my_originator_message( void* unused ) +{ + my_orig_node.ogm_sqn_to_be_send += my_orig_node.path_metric_algo.sqn_steps; //ogm_sqn_steps; + my_orig_node.dhn->referred_timestamp = bmx_time; + + register_task(my_ogm_interval, schedule_my_originator_message, NULL); + + if (((uint32_t) (my_orig_node.ogm_sqn_to_be_send + 1 - my_orig_node.ogm_sqn_min)) < my_orig_node.ogm_sqn_range) { + + ogm_aggreg_pending++; + dbgf(DBGL_CHANGES, DBGT_INFO, "ogm_sqn %d", my_orig_node.ogm_sqn_to_be_send); + + } else { + + update_my_description_adv(); + + } +} + + + + +void update_my_description_adv(void) +{ + struct description_hash dhash; + struct description *dsc = my_orig_node.desc0; + + if ( terminating() ) + return; + + // put obligatory stuff: + memset(dsc, 0, MAX_PKT_MSG_SIZE); + + memcpy(&dsc->id, &my_orig_node.id, sizeof(struct description_id)); +/* + dsc->id.rand.u32[0] = htonl(my_orig_node.id.rand.u32[0]); + dsc->id.rand.u32[1] = htonl(my_orig_node.id.rand.u32[1]); +*/ + + + my_orig_node.ogm_sqn_pq_bits = DEF_OGM0_PQ_BITS; +// my_orig_node.ogm_sqn_mask = (MAX_SQN << my_orig_node.ogm_sqn_pq_bits); +// my_orig_node.ogm_sqn_steps = (0x01 << my_orig_node.ogm_sqn_pq_bits); + + // add some randomness to the ogm_sqn_range, that not all nodes invalidate at the same time: + uint16_t random_range = ((DEF_OGM0_SQN_RANGE - (DEF_OGM0_SQN_RANGE/5)) > MIN_OGM0_SQN_RANGE) ? + DEF_OGM0_SQN_RANGE - rand_num(DEF_OGM0_SQN_RANGE/5) : DEF_OGM0_SQN_RANGE + rand_num(DEF_OGM0_SQN_RANGE/5); + + my_orig_node.ogm_sqn_range = ((random_range + my_orig_node.path_metric_algo.sqn_steps - 1) & my_orig_node.path_metric_algo.sqn_mask); + + my_orig_node.ogm_sqn_min = ((my_orig_node.ogm_sqn_min + MAX_OGM0_SQN_RANGE + (0x01 << MAX_OGM0_PQ_BITS)) & my_orig_node.path_metric_algo.sqn_mask); + + my_orig_node.ogm_sqn_aggregated = my_orig_node.ogm_sqn_min; + my_orig_node.ogm_sqn_to_be_send = my_orig_node.ogm_sqn_min + my_orig_node.path_metric_algo.sqn_steps - 1; + + + dsc->path_window_size = htons(my_orig_node.path_metric_algo.sqn_window); + dsc->path_lounge_size = htons(my_orig_node.path_metric_algo.sqn_lounge); + + + dsc->ttl_max = my_ttl; + dsc->path_hystere = my_path_hystere; + + dsc->hop_penalty = my_hop_penalty; + dsc->late_penalty = my_late_penalty; + dsc->asym_weight = asym_weight; + dsc->sym_weight = sym_weight; + + + dsc->ogm_sqn_pq_bits = my_orig_node.ogm_sqn_pq_bits; + dsc->ogm_sqn_min = htons(my_orig_node.ogm_sqn_min); + dsc->ogm_sqn_range = htons(my_orig_node.ogm_sqn_range); + + dsc->dsc_sqn = htons(++(my_orig_node.desc0_sqn)); + + + dsc->path_ogi = htons(my_ogm_interval); + + + // add all tlv options: + my_desc0_tlv_len = 0; + uint8_t tlvt; + for ( tlvt=0; tlvt < BMX_DSC_TLV_ARRSZ; tlvt++) { + + struct frame_header *tlv = (struct frame_header*) (((char*) dsc) + sizeof (struct description) + my_desc0_tlv_len); + + uint16_t rsvd = my_desc0_tlv_len + sizeof (struct frame_header); + + if (rsvd <= MAX_DESC0_TLV_SIZE && (description0_tlv_handler[tlvt].create_tlv)) { + + uint16_t msgs_size = (*(description0_tlv_handler[tlvt].create_tlv)) + (tlv->data, MAX_DESC0_TLV_SIZE - rsvd); + + if ( msgs_size ) { + + assertion(-500355, (description0_tlv_handler[tlvt].variable_msg_size || + msgs_size % description0_tlv_handler[tlvt].min_msg_size == 0)); + + tlv->type = tlvt; + tlv->length = htons(sizeof (struct frame_header) + msgs_size); + + my_desc0_tlv_len += sizeof (struct frame_header) + msgs_size; + + dbgf(DBGL_CHANGES, DBGT_INFO, "added %s size %d", + description0_tlv_handler[tlvt].name, msgs_size); + + } + + } else { + cleanup_all( -500352 ); + } + } + + dsc->dsc_tlvs_len = htons(my_desc0_tlv_len); + + dbgf(DBGL_CHANGES, DBGT_INFO, "added tlv total of %d ", my_desc0_tlv_len); + + // calculate hash: like shown in CTaoCrypt Usage Reference: + ShaUpdate(&bmx_sha, (byte*)dsc, (sizeof (struct description) + my_desc0_tlv_len)); + ShaFinal(&bmx_sha, (byte*) &dhash); + + if ( my_orig_node.dhn ) { + my_orig_node.dhn->on = NULL; + invalidate_dhash_node( my_orig_node.dhn ); + } + + my_orig_node.dhn = create_dhash_node(&dhash, &my_orig_node); + + myIID4me = my_orig_node.dhn->myIID4orig; + + if (DEF_UNSOLICITED_DESCRIPTIONS) { + uint16_t desc_len = sizeof ( struct msg_description_adv) + my_desc0_tlv_len; + struct link_dev_node **lndev_arr = get_best_lndevs_by_criteria(NULL, my_orig_node.dhn); + int d; + + for (d = 0; (lndev_arr[d]); d++) + schedule_tx_task(lndev_arr[d]->key.dev, lndev_arr[d], FRAME_TYPE_DSC0_ADVS, desc_len, 0, IID_RSVD_4YOU, 0); + } + +/* + remove_task(schedule_my_originator_message, NULL); + register_task(1, schedule_my_originator_message, NULL); +*/ + + +} + + + +static struct opt_type msg_options[]= +{ +// ord parent long_name shrt Attributes *ival min max default *func,*syntax,*help + + {ODI, 0,0, 0, 5,0,0,0,0,0, 0, 0, 0, 0, 0, + 0, "\nMessage options:"} +, + {ODI, 0, ARG_UDPD_SIZE, 0, 5, A_PS1, A_ADM, A_DYI, A_CFA, A_ANY, &max_udpd_size, MIN_UDPD_SIZE, MAX_UDPD_SIZE, DEF_UDPD_SIZE, 0, + ARG_VALUE_FORM, "set preferred udp-data size for send packets"} +, + {ODI, 0, ARG_AGGREG_INTERVAL, 0, 5, A_PS1, A_ADM, A_DYI, A_CFA, A_ANY, &aggreg_interval, MIN_AGGREG_INTERVAL,MAX_AGGREG_INTERVAL,DEF_AGGREG_INTERVAL,0, + ARG_VALUE_FORM, "set aggregation interval (SHOULD be smaller than the half of your and others OGM interval)"} +, + {ODI, 0, ARG_OGM_RESEND_ATTEMPTS, 0, 5, A_PS1, A_ADM, A_DYI, A_CFA, A_ANY, &ogm_resend_attempts,MIN_OGM_RESEND_ATTEMPTS,MAX_OGM_RESEND_ATTEMPTS,DEF_OGM_RESEND_ATTEMPTS,0, + ARG_VALUE_FORM, "set maximum resend attempts for ogm aggregations"} +, + {ODI, 0, ARG_HELLO_INTERVAL, 'O',5, A_PS1, A_ADM, A_DYI, A_CFA, A_ANY, &my_hello_interval,MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL, DEF_HELLO_INTERVAL, 0, + ARG_VALUE_FORM, "set interval in ms with which new originator message (OGM) are send"} +, + {ODI, 0, ARG_OGM_INTERVAL, 'o',5, A_PS1, A_ADM, A_DYI, A_CFA, A_ANY, &my_ogm_interval, MIN_OGM_INTERVAL, MAX_OGM_INTERVAL, DEF_OGM_INTERVAL, opt_update_description, + ARG_VALUE_FORM, "set interval in ms with which new originator message (OGM) are send"} + +}; + + +void init_msg( void ) +{ + + paranoia( -500347, ( sizeof(struct description_hash) != BMX_HASH0_LEN)); + + ogm_aggreg_sqn_max = rand_num(MAX_SQN); + + register_options_array( msg_options, sizeof( msg_options ) ); + + InitSha(&bmx_sha); + + register_task(rand_num(RAND_INIT_DELAY), schedule_my_originator_message, NULL); + +} + +void cleanup_msg( void ) +{ + schedule_and_purge_ogm_aggregations(NULL /*purge_all*/); + + debugFree(get_best_lndevs_by_criteria(NULL, NULL), -300218); + + purge_cached_descriptions(YES); + +}
Added: trunk/bmx/msg.h =================================================================== --- trunk/bmx/msg.h (rev 0) +++ trunk/bmx/msg.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include <stdint.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <linux/if.h> + + +#define MIN_UDPD_SIZE 256 //(6+4+(22+8)+32)+184=72+56=128 +#define DEF_UDPD_SIZE 512 +#define MAX_UDPD_SIZE 1400 +#define ARG_UDPD_SIZE "udp_data_size" + + +#define MIN_AGGREG_INTERVAL 35 +#define MAX_AGGREG_INTERVAL 4000 +#define DEF_AGGREG_INTERVAL 500 +#define ARG_AGGREG_INTERVAL "aggregation_interval" + +#define ARG_HELLO_INTERVAL "hello_interval" +#define DEF_HELLO_INTERVAL DEF_OGM_AGGREG_INTERVAL +#define MIN_HELLO_INTERVAL 50 +#define MAX_HELLO_INTERVAL 10000 +extern int32_t my_hello_interval; + + +#define ARG_OGM_INTERVAL "ogm_interval" +#define DEF_OGM_INTERVAL 2000 +#define MIN_OGM_INTERVAL 200 +#define MAX_OGM_INTERVAL 60000 +extern int32_t my_ogm_interval; + + +#define DEF_TX_TS_TREE_SIZE 100 +#define DEF_TX_TS_TREE_PURGE_FK 5 + + + +#define MIN_OGM_AGGREG_INTERVAL ( MIN_OGM_INTERVAL/10 ) +#define MAX_OGM_AGGREG_INTERVAL ( DEF_OGM_INTERVAL/2 ) +#define DEF_OGM_AGGREG_INTERVAL ( DEF_OGM_INTERVAL/5 ) + +#define MAX_OGM_RESEND_INTERVAL (MAX_AGGREG_INTERVAL * 2) +#define DEF_OGM_RESEND_INTERVAL ((DEF_AGGREG_INTERVAL * 2) / 3) + +#define MIN_OGM_RESEND_ATTEMPTS 0 +#define MAX_OGM_RESEND_ATTEMPTS 5 +#define DEF_OGM_RESEND_ATTEMPTS 3 +#define ARG_OGM_RESEND_ATTEMPTS "ogm_resend_attempts" + +#define MIN_NBDISC_RTQ (PROBE_RANGE / 8) +#define MIN_OGM_ACK_RTQ (PROBE_RANGE / 4) + + + + +//TODO: set REQ_TO to 1 (in a non-packet-loss testenvironment this may be set to 1000 for testing) +#define DEF_TX_DESC0_REQ_TO 1000 +#define DEF_TX_DESC0_ADV_TO 500 +#define DEF_TX_DHASH0_REQ_TO 1000 +#define DEF_TX_DHASH0_ADV_TO 500 + +#define MIN_DESC0_REFERRED_TO 10000 +#define MAX_DESC0_REFERRED_TO 100000 +#define DEF_DESC0_REFERRED_TO 10000 + +#define MAX_PKT_MSG_SIZE (MAX_UDPD_SIZE - sizeof(struct packet_header) - sizeof(struct frame_header)) + +#define MAX_DESC0_TLV_SIZE (MAX_PKT_MSG_SIZE - sizeof(struct msg_description_adv) ) + + + +struct packet_header +{ + uint8_t bmx_version; + uint8_t bmx_capabilities; + uint16_t pkt_length; // the relevant data size in bytes (including the bmx_header) + uint16_t pkt_dev_sqn; + uint8_t pkt_data[]; // encapulating packet data tlvs +} __attribute__((packed)); + + +#define FRAME_TYPE_RESERVED0 0 +#define FRAME_TYPE_RESERVED1 1 +#define FRAME_TYPE_HI40_ADVS 2 // most-simple BMX-NG hello (nb-discovery) advertisements +#define FRAME_TYPE_HI40_REPS 3 // most-simple BMX-NG hello (nb-discovery) replies +#define FRAME_TYPE_HI60_REPS 4 // +#define FRAME_TYPE_RESERVED4 5 // BMX_FRM_CRT0_REQS +#define FRAME_TYPE_RESERVED5 6 // BMX_FRM_CRT0_ADV +#define FRAME_TYPE_DSC0_REQS 7 // ... +#define FRAME_TYPE_DSC0_ADVS 8 // descriptions are send as individual advertisement frames +#define FRAME_TYPE_DHS0_REQS 9 // Hash-for-description-of-OG-ID requests +#define FRAME_TYPE_DHS0_ADVS 10 // Hash-for-description-of-OG-ID advertisements +#define FRAME_TYPE_OGM0_ADVS 11 // most simple BMX-NG (type 0) OGM advertisements +#define FRAME_TYPE_OGM0_ACKS 12 // most simple BMX-NG (type 0) OGM advertisements +#define FRAME_TYPE_NOP 13 +#define FRAME_TYPE_ARRSZ 14 +#define FRAME_TYPE_MAX 0xFF + + +/* + * dhash0_adv or description0_adv specific frame flag + * firstIsSender flag is usefull to accelerate neighbor (and neighIID) discovery process + */ + +#define FRAME_FLAG_firstIsSender 0x01 // first message references transmitter of packet containing this message +#define FRAME_FLAGS_MAX 255 + + +struct frame_header { + uint8_t type; // frame type + uint8_t flags; // frame-type specific (8-bit) value containing flags or other data (e.g. sqn, mark,..) + uint16_t length; // lenght of frame including frame_header and variable data field + uint8_t data[]; // frame-type specific data consisting of 0-1 data headers and 0-n data messages +} __attribute__((packed)); + + +struct msg_hello_adv { + SQN_T hello_dev_sqn; +} __attribute__((packed)); +/* + * reception trigger: + * - update_link_nodes() + * - msg_hellp40_reply[] + * + */ + +struct msg_hello_reply { + IP4_T receiver_ip4; + SQN_T hello_dev_sqn; +} __attribute__((packed)); +/* + * reception triggers: + * - update_link_nodes() + * - (if NOT link->neigh->dhash->orig) msg_dhash0_request[ ... orig_did = IID_RSVD_4YOU ] + * + */ + + +/* +struct hdr_dhash_request { + IP4_T receiver_ip4; +} __attribute__((packed)); +*/ +struct msg_dhash_request { + IP4_T receiver_ip4; // dest_llip4; + IID_T receiverIID4x; //(orig_did) IID_RSVD_4YOU to ask for neighbours' dhash0 +} __attribute__((packed)); +/* + * reception triggers: + * - msg_dhash0_adv[ ] + * + */ + + +struct msg_dhash_adv { + IID_T transmitterIID4x; //orig_sid + struct description_hash dhash; +} __attribute__((packed)); +/* + * reception triggers: + * - (if description is known AND is neighbor) creation of link <-> neigh <-> dhash <-> orig <-> description + * - (else if description is known ) creation of dhash <-> orig <-> description + * - (else) msg_description0_request[] + */ + + +struct msg_description_request { + IP4_T receiver_ip4; + IID_T receiverIID4x; //(orig_did) IID_RSVD_4YOU to ask for neighbours' description0 +} __attribute__((packed)); +/* + * reception triggers: + * - msg_description0_adv[] + * + */ + + + +struct description { + struct description_id id; + + uint16_t dsc_tlvs_len; + SQN_T dsc_sqn; + + SQN_T ogm_sqn_min; + SQN_T ogm_sqn_range; + uint8_t ogm_sqn_pq_bits; + uint8_t dsc_rsvd; + + uint16_t path_ogi; + + uint16_t path_window_size; + uint16_t path_lounge_size; + + uint8_t path_hystere; + uint8_t ttl_max; + + uint8_t hop_penalty; + uint8_t late_penalty; + uint8_t asym_weight; + uint8_t sym_weight; + + uint16_t rsvd_u16; + uint32_t reserved[2]; //ensure traditional message size + +// uint8_t tlv_frames[]; +} __attribute__((packed)); + + +#define MSG_DESCRIPTION0_ADV_UNHASHED_SIZE 6 +#define MSG_DESCRIPTION0_ADV_HASHED_SIZE (sizeof( struct description_id) + (8 * 4)) +#define MSG_DESCRIPTION0_ADV_SIZE (MSG_DESCRIPTION0_ADV_UNHASHED_SIZE + MSG_DESCRIPTION0_ADV_HASHED_SIZE) + +struct msg_description_adv { + + // the unhashed part: + IID_T transmitterIID4x; // orig_sid + uint8_t ttl; + uint8_t reserved0; + uint16_t reserved1; + + // the hashed pard: + struct description desc; + +} __attribute__((packed)); +/* + * reception triggers: + * - creation of dhash_node <-> description_node + * + */ + +#define MAX_OGMS_PER_AGGREG ( MIN((FRAME_FLAGS_MAX-1),((MIN_UDPD_SIZE - (sizeof(struct packet_header) + sizeof(struct frame_header))) / sizeof(struct msg_ogm_adv))) ) +#define MIN_OGMS_PER_AGGREG ( MAX_OGMS_PER_AGGREG / 8 ) +#define DEF_OGMS_PER_AGGREG ( MAX_OGMS_PER_AGGREG / 2 ) + +//struct msg_ogm0_adv; + +struct msg_ogm_adv { + IID_T transmitterIID4x; //orig_sid + SQN_T orig_sqn; +} __attribute__((packed)); + +struct hdr_ogm_adv { + SQN_T aggregation_sqn; + struct msg_ogm_adv msg[]; +} __attribute__((packed)); + +/* + * reception triggers: + * - (if link <-> neigh <-... is known and orig_sid is NOT known) msg_dhash0_request[ ... orig_did = orig_sid ] + * - else update_orig(orig_sid, orig_sqn) + */ + +struct msg_ogm_ack { +// IID_T transmitterIID4receiver; + IP4_T receiver_ip4; + SQN_T aggregation_sqn; +} __attribute__((packed)); +/* + * reception triggers: + * - (if link <-> neigh <-... is known and orig_sid is NOT known) msg_dhash0_request[ ... orig_did = orig_sid ] + * - else update_orig(orig_sid, orig_sqn) + */ + + + + + +#define BMX_DSC_TLV_GLIP4 0x00 +#define BMX_DSC_TLV_UHNA4 0x01 +#define BMX_DSC_TLV_ARRSZ 0x02 +#define BMX_DSC_TLV_MAX 0xFF + +#define TLVS_SUCCESS SUCCESS +#define TLVS_FAILURE FAILURE +#define TLVS_BLOCKED 1 + +enum { + TLV_DEL_TEST_ADD = 0, + TLV_TEST = 1, + TLV_ADD = 2, + TLV_DONE = 3, + TLV_DEBUG = 4 +}; + + +struct description0_msg_ip4 { + IP4_T ip4; +} __attribute__((packed)); + +struct description0_msg_hna4 { + uint8_t prefix_len; + uint8_t reserved; + IP4_T ip4; + uint32_t metric; +} __attribute__((packed)); + + + + + + +struct description_cache_node { + struct description_hash dhash; + uint32_t timestamp; + struct description *description; +}; + +#define DEF_DESC0_CACHE_SIZE 3 +#define DEF_DESC0_CACHE_TO 100000 + +#define DEF_UNSOLICITED_DESCRIPTIONS YES + + +extern char *tlv_op_str[]; + +extern uint32_t ogm_aggreg_pending; + +/*********************************************************** + The core frame/message structures and handlers +************************************************************/ + +void update_my_description_adv( void ); + +void purge_tx_timestamp_tree(struct dev_node *dev, IDM_T purge_all); +void purge_dev_tx_list( struct dev_node *dev ); + +void tx_packet( void* dev_node ); + +int rx_frames(struct packet_buff *pb, uint8_t* fdata, uint16_t fsize); + +void schedule_my_hello_message( void* dev_node ); + +IDM_T process_description_tlvs(struct orig_node *on, struct description *dsc_new, IDM_T op, struct ctrl_node *cn); + +void init_msg( void ); + +void cleanup_msg( void ); + +
Added: trunk/bmx/plugin.c =================================================================== --- trunk/bmx/plugin.c (rev 0) +++ trunk/bmx/plugin.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,569 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <dlfcn.h> + +#include "bmx.h" +#include "msg.h" +#include "plugin.h" +#include "schedule.h" +#include "hna.h" + +LIST_SIMPEL(cb_fd_list, struct cb_fd_node, list); +static LIST_SIMPEL(cb_packet_list, struct cb_packet_node, list); +static LIST_SIMPEL(cb_ogm_list, struct cb_ogm_node, list); +static LIST_SIMPEL(plugin_list, struct plugin_node, list); + + +int32_t plugin_data_registries[PLUGIN_DATA_SIZE]; + + +void cb_plugin_hooks( void* data, int32_t cb_id ) { + + struct list_node *list_pos; + struct plugin_node *pn, *prev_pn = NULL; + + list_for_each( list_pos, &plugin_list ) { + + pn = list_entry( list_pos, struct plugin_node, list ); + + if ( prev_pn && prev_pn->plugin_v2 && prev_pn->plugin_v2->cb_plugin_handler[cb_id] ) + (*(prev_pn->plugin_v2->cb_plugin_handler[cb_id])) ( data ); + + prev_pn = pn; + } + + if ( prev_pn && prev_pn->plugin_v2 && prev_pn->plugin_v2->cb_plugin_handler[cb_id] ) + (*(prev_pn->plugin_v2->cb_plugin_handler[cb_id])) (data); + +} + + + +STATIC_FUNC +int32_t configure_thread_hook( int32_t cb_type, void (*cb_handler) (void), int8_t del, struct list_node *cb_list ) { + + struct list_node *list_pos, *tmp_pos, *prev_pos = cb_list; + struct cb_node *cbn; + + if ( !cb_type || !cb_handler ) { + cleanup_all( -500143 ); + } + + list_for_each_safe( list_pos, tmp_pos, cb_list ) { + + cbn = list_entry( list_pos, struct cb_node, list ); + + if ( cb_type == cbn->cb_type && cb_handler == cbn->cb_handler ) { + + if ( del ) { + + list_del_next(((struct list_head*) cb_list), prev_pos); + debugFree( cbn, -300069 ); + return SUCCESS; + + } else { + cleanup_all( -500144 ); + //dbgf( DBGL_SYS, DBGT_ERR, "cb_hook for cb_type %d and cb_handler already registered", cb_type ); + //return FAILURE; + } + + } else { + + prev_pos = &cbn->list; + + } + + } + + if ( del ) { + + cleanup_all( -500145 ); + return FAILURE; + + } else { + + cbn = debugMalloc( sizeof( struct cb_node), -300027 ); + memset( cbn, 0, sizeof( struct cb_node) ); + + cbn->cb_type = cb_type; + cbn->cb_handler = cb_handler; + list_add_tail(((struct list_head*) cb_list), &cbn->list); + + return SUCCESS; + } +} + + + +int32_t set_fd_hook( int32_t fd, void (*cb_fd_handler) (int32_t fd), int8_t del ) { + + int32_t ret = configure_thread_hook( fd, (void (*) (void)) cb_fd_handler, del, (struct list_node*)&cb_fd_list ); + + change_selects(); + return ret; +} + + + + +int32_t set_packet_hook( int32_t packet_type, void (*cb_packet_handler) (struct packet_buff *), int8_t del ) { + + return configure_thread_hook( packet_type, (void (*) (void)) cb_packet_handler, del, (struct list_node*)&cb_packet_list ); +} + + +//notify interested plugins of rcvd packet... +// THIS MAY CRASH when one plugin unregisteres two packet_hooks while being called with cb_packet_handler() +// TODO: find solution to prevent this ??? +uint32_t cb_packet_hooks( int32_t packet_type, struct packet_buff *pb ) { + + struct list_node *list_pos; + struct cb_packet_node *cpn, *prev_cpn = NULL; + int calls = 0; + + list_for_each( list_pos, &cb_packet_list ) { + + cpn = list_entry( list_pos, struct cb_packet_node, list ); + + if ( prev_cpn && prev_cpn->packet_type == packet_type ) { + + (*(prev_cpn->cb_packet_handler)) (pb); + + calls++; + } + + prev_cpn = cpn; + + } + + if ( prev_cpn && prev_cpn->packet_type == packet_type ) + (*(prev_cpn->cb_packet_handler)) (pb); + + return calls; +} + + +int32_t set_ogm_hook( int32_t (*cb_ogm_handler) ( struct packet_buff *, uint16_t oCtx, struct router_node *old_router ), int8_t del ) { + + return configure_thread_hook( 1, (void (*) (void)) cb_ogm_handler, del, (struct list_node*)&cb_ogm_list ); +} + + +int32_t cb_ogm_hooks( struct packet_buff *pb, uint16_t oCtx, struct router_node *old_router ) { + + + struct list_node *list_pos; + struct cb_ogm_node *con, *prev_con = NULL; + + list_for_each( list_pos, &cb_ogm_list ) { + + con = list_entry( list_pos, struct cb_ogm_node, list ); + + if ( prev_con ) { + + if ( ((*(prev_con->cb_ogm_handler)) (pb, oCtx, old_router)) == CB_OGM_REJECT ) { + + return CB_OGM_REJECT; + } + + } + + prev_con = con; + + } + + if ( prev_con ) { + + if ( ((*(prev_con->cb_ogm_handler)) (pb, oCtx, old_router)) == FAILURE ) { + + return CB_OGM_REJECT; + } + + } + + return CB_OGM_ACCEPT; +} + + +#ifdef BMX2_TODO +int32_t set_snd_ext_hook( uint16_t ext_type, int32_t (*cb_snd_ext_handler) ( unsigned char* ext_buff ), int8_t del ) { + + static uint8_t initialized = NO; + + if ( !initialized ) { + memset( &snd_ext_hooks[0], 0, sizeof( snd_ext_hooks ) ); + initialized = YES; + } + + if ( ext_type > EXT_TYPE_MAX ) + return FAILURE; + + if ( del && snd_ext_hooks[ext_type].cb_snd_ext_handler == cb_snd_ext_handler ) { + + snd_ext_hooks[ext_type].cb_snd_ext_handler = NULL; + return SUCCESS; + + } else if ( !del && snd_ext_hooks[ext_type].cb_snd_ext_handler == NULL ) { + + snd_ext_hooks[ext_type].cb_snd_ext_handler = cb_snd_ext_handler; + return SUCCESS; + } + + return FAILURE; +} + +int32_t cb_snd_ext_hook( uint16_t ext_type, unsigned char* ext_buff ) { + + if ( snd_ext_hooks[ext_type].cb_snd_ext_handler ) + return ((*(snd_ext_hooks[ext_type].cb_snd_ext_handler))( ext_buff )); + + else + return SUCCESS; + +} + + +#endif + + +int32_t reg_plugin_data( uint8_t data_type ) { + + static int initialized = NO; + + if ( !initialized ) { + memset( plugin_data_registries, 0, sizeof( plugin_data_registries ) ); + initialized=YES; + } + + if ( on_the_fly || data_type >= PLUGIN_DATA_SIZE ) + return FAILURE; + + // do NOT return the incremented value! + plugin_data_registries[data_type]++; + + return (plugin_data_registries[data_type] - 1); +} + +#ifdef WITHUNUSED +void **get_plugin_data( void *data, uint8_t data_type, int32_t registry ) { + + if ( data_type >= PLUGIN_DATA_SIZE || registry > plugin_data_registries[data_type] ) { + cleanup_all( -500145 ); + //dbgf( DBGL_SYS, DBGT_ERR, "requested to deliver data for unknown registry !"); + //return NULL; + } + + if ( data_type == PLUGIN_DATA_ORIG ) + return &(((struct orig_node*)data)->plugin_data[registry]); + + return NULL; +} +#endif + +STATIC_FUNC +int is_plugin_active( void *plugin ) { + + struct list_node *list_pos; + + list_for_each( list_pos, &plugin_list ) { + + if ( ((struct plugin_node *) (list_entry( list_pos, struct plugin_node, list )))->plugin == plugin ) + return YES; + + } + + return NO; +} + +STATIC_FUNC +int activate_plugin( void *p, int32_t version, void *dlhandle, const char *dl_name ) { + + if ( p == NULL || version != PLUGIN_VERSION_02 ) + return FAILURE; + + if ( is_plugin_active( p ) ) + return FAILURE; + + + if ( version == PLUGIN_VERSION_02 ) { + + struct plugin_v2 *pv1 = (struct plugin_v2*)p; + + if (pv1->plugin_size != sizeof ( struct plugin_v2) || + strcmp(pv1->plugin_bmx_version, SOURCE_VERSION) || + (pv1->plugin_bmx_revision != REVISION_VERSION)) { + + dbgf( DBGL_SYS, DBGT_ERR, + "plugin with unexpected size %d != %lu, version %s != %s, revision %d != %d", + pv1->plugin_size, sizeof ( struct plugin_v2), + pv1->plugin_bmx_version, SOURCE_VERSION, + pv1->plugin_bmx_revision, REVISION_VERSION + ); + + return FAILURE; + } + + + if ( pv1->cb_init == NULL || ((*( pv1->cb_init )) ()) == FAILURE ) { + + dbg( DBGL_SYS, DBGT_ERR, "could not init plugin"); + return FAILURE; + } + + struct plugin_node *pn = debugMalloc( sizeof( struct plugin_node), -300028); + memset( pn, 0, sizeof( struct plugin_node) ); + + pn->version = PLUGIN_VERSION_02; + pn->plugin_v2 = pv1; + pn->plugin = p; + pn->dlhandle = dlhandle; + + list_add_tail(&plugin_list, &pn->list); + + dbgf_all( DBGT_INFO, "%s SUCCESS", pn->plugin_v2->plugin_name ); + + if ( dl_name ) { + pn->dlname = debugMalloc( strlen(dl_name)+1, -300029 ); + strcpy( pn->dlname, dl_name ); + } + + return SUCCESS; + + } + + return FAILURE; + +} + +STATIC_FUNC +void deactivate_plugin( void *p ) { + + if ( !is_plugin_active( p ) ) { + cleanup_all( -500190 ); + } + + struct list_node *list_pos, *tmp_pos, *prev_pos = (struct list_node*)&plugin_list; + + list_for_each_safe( list_pos, tmp_pos, &plugin_list ) { + + struct plugin_node *pn = list_entry( list_pos, struct plugin_node, list ); + + if ( pn->plugin == p ) { + + list_del_next(&plugin_list, prev_pos); + + if ( pn->version != PLUGIN_VERSION_02 ) + cleanup_all( -500098 ); + + dbg( DBGL_CHANGES, DBGT_INFO, "deactivating plugin %s", pn->plugin_v2->plugin_name ); + + if ( pn->plugin_v2->cb_cleanup ) + (*( pn->plugin_v2->cb_cleanup )) (); + + + if ( pn->dlname) + debugFree( pn->dlname, -300070); + + debugFree( pn, -300071); + + } else { + + prev_pos = &pn->list; + + } + + } + +} + +STATIC_FUNC +int8_t activate_dyn_plugin( const char* name ) { + + struct plugin_v2* (*get_plugin_v2) ( void ) = NULL; + + void *dlhandle; + struct plugin_v2 *pv1; + char dl_path[1000]; + + char *My_libs = getenv(BMX_ENV_LIB_PATH); + + if ( !name ) + return FAILURE; + + // dl_open sigfaults on some systems without reason. + // removing the dl files from BMX_DEF_LIB_PATH is a way to prevent calling dl_open. + // Therefore we restrict dl search to BMX_DEF_LIB_PATH and BMX_ENV_LIB_PATH and ensure that dl_open + // is only called if a file with the requested dl name could be found. + + if ( My_libs ) + sprintf( dl_path, "%s/%s", My_libs, name ); + else + sprintf( dl_path, "%s/%s", BMX_DEF_LIB_PATH, name ); + + + dbgf_all( DBGT_INFO, "trying to load dl %s", dl_path ); + + int dl_tried = 0; + + if ( check_file( dl_path, NO, YES ) == SUCCESS && + (dl_tried = 1) && (dlhandle = dlopen( dl_path, RTLD_NOW )) ) + { + + dbgf_all( DBGT_INFO, "succesfully loaded dynamic library %s", dl_path ); + + } else { + + dbg( dl_tried ? DBGL_SYS : DBGL_CHANGES, dl_tried ? DBGT_ERR : DBGT_WARN, + "failed loading dl %s %s (maybe incompatible binary/lib versions?)", + dl_path, dl_tried?dlerror():"" ); + + return FAILURE; + + } + + dbgf_all( DBGT_INFO, "survived dlopen()!" ); + + + typedef struct plugin_v2* (*sdl_init_function_type) ( void ); + + union { + sdl_init_function_type func; + void * obj; + } alias; + + alias.obj = dlsym( dlhandle, "get_plugin_v2"); + + if ( !( get_plugin_v2 = alias.func ) ) { + dbgf( DBGL_SYS, DBGT_ERR, "dlsym( %s ) failed: %s", name, dlerror() ); + return FAILURE; + } + + + if ( !(pv1 = get_plugin_v2()) ) { + + dbgf( DBGL_SYS, DBGT_ERR, "get_plugin_v2( %s ) failed", name ); + return FAILURE; + + } + + if ( is_plugin_active( pv1 ) ) + return SUCCESS; + + + if ( activate_plugin( pv1, PLUGIN_VERSION_02, dlhandle, name ) == FAILURE ) { + + dbgf( DBGL_SYS, DBGT_ERR, "activate_plugin( %s ) failed", dl_path ); + return FAILURE; + + } + + dbg( DBGL_CHANGES, DBGT_INFO, + "loading and activating %s dl %s succeeded", + My_libs ? "customized" : "default", dl_path ); + + return SUCCESS; +} + +STATIC_FUNC +int32_t opt_plugin ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + dbgf_all( DBGT_INFO, "%s %d", opt_cmd2str[cmd], _save ); + + char tmp_name[MAX_PATH_SIZE] = ""; + + + if ( cmd == OPT_CHECK ) { + + dbgf_all( DBGT_INFO, "about to load dl %s", patch->p_val ); + + if ( wordlen(patch->p_val)+1 >= MAX_PATH_SIZE || patch->p_val[0] == '/' ) + return FAILURE; + + wordCopy( tmp_name, patch->p_val ); + + if ( get_opt_parent_val( opt, tmp_name ) ) + return SUCCESS; + + if ( activate_dyn_plugin( tmp_name ) == FAILURE ) + return FAILURE; + + } + + return SUCCESS; +} + +static struct opt_type plugin_options[]= +{ +// ord parent long_name shrt Attributes *ival min max default *func,*syntax,*help + + //order> config-file order to be loaded by config file, order < ARG_CONNECT oder to appera first in help text + {ODI,0,ARG_PLUGIN, 0, 2,A_PMN,A_ADM,A_INI,A_CFA,A_ANY, 0, 0, 0, 0, opt_plugin, + ARG_FILE_FORM, "load plugin. "ARG_FILE_FORM" must be in LD_LIBRARY_PATH or " BMX_ENV_LIB_PATH + "\n path (e.g. --plugin bmx_howto_plugin.so )\n"} +}; + + +void init_plugin( void ) { + + +// set_snd_ext_hook( 0, NULL, YES ); //ensure correct initialization of extension hooks + reg_plugin_data( PLUGIN_DATA_SIZE );// ensure correct initialization of plugin_data + + struct plugin_v2 *pv1; + + pv1=NULL; + + // first try loading config plugin, if succesfull, continue loading optinal plugins depending on config + activate_dyn_plugin( BMX_LIB_UCI_CONFIG ); + + register_options_array( plugin_options, sizeof( plugin_options ) ); + + + if ( (pv1 = hna_get_plugin_v2()) != NULL ) + activate_plugin( pv1, PLUGIN_VERSION_02, NULL, NULL ); + + +#ifdef BMX2_TODO +#ifndef NOVIS + if ( (pv1 = vis_get_plugin_v1()) != NULL ) + activate_plugin( pv1, PLUGIN_VERSION_02, NULL, NULL ); +#endif + +#ifndef NOTUNNEL + if ( (pv1 = tun_get_plugin_v1()) != NULL ) + activate_plugin( pv1, PLUGIN_VERSION_02, NULL, NULL ); +#endif + +#ifndef NOSRV + if ( (pv1 = srv_get_plugin_v1()) != NULL ) + activate_plugin( pv1, PLUGIN_VERSION_02, NULL, NULL ); +#endif + +#endif +} + +void cleanup_plugin( void ) { + + while ( !LIST_EMPTY( &plugin_list ) ) + deactivate_plugin( ((struct plugin_node*)(list_entry( (&plugin_list)->next, struct plugin_node, list)))->plugin ); + +}
Added: trunk/bmx/plugin.h =================================================================== --- trunk/bmx/plugin.h (rev 0) +++ trunk/bmx/plugin.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#define PLUGIN_VERSION_02 0x02 +#define BMX_LIB_UCI_CONFIG "bmx_uci_config.so" + +#define ARG_PLUGIN "plugin" + +extern struct list_head cb_fd_list; + + + +enum { + PLUGIN_CB_CONF, + PLUGIN_CB_ORIG_CREATE, + PLUGIN_CB_ORIG_FLUSH, + PLUGIN_CB_ORIG_DESTROY, + PLUGIN_CB_TERM, + PLUGIN_CB_SIZE +}; + +struct plugin_v2 { + uint32_t plugin_version; + uint32_t plugin_size; + uint32_t plugin_bmx_revision; + char *plugin_bmx_version; + char *plugin_name; + int32_t (*cb_init) ( void ); + void (*cb_cleanup) ( void ); + + //some more advanced (rarely called) callbacks hooks + void (*cb_plugin_handler[PLUGIN_CB_SIZE]) (void*); + + //some other attributes + uint8_t link_flags; + +}; + + + +struct plugin_node { + struct list_node list; + int32_t version; + void *plugin; + struct plugin_v2 *plugin_v2; + void *dlhandle; + char *dlname; +}; + + +struct cb_ogm_node { + struct list_node list; + int32_t cb_type; + int32_t (*cb_ogm_handler) ( struct packet_buff *, uint16_t oCtx, struct router_node *old_router ); +}; + + +struct cb_fd_node { + struct list_node list; + int32_t fd; + void (*cb_fd_handler) (int32_t fd); +}; + + +struct cb_packet_node { + struct list_node list; + int32_t packet_type; + void (*cb_packet_handler) (struct packet_buff *); +}; + + +struct cb_node { + struct list_node list; + int32_t cb_type; + void (*cb_handler) ( void ); +}; + + + +struct cb_snd_ext { + int32_t (*cb_snd_ext_handler) ( unsigned char* ext_buff ); +}; + + +// cb_fd_handler is called when fd received data +// called function may remove itself +int32_t set_fd_hook( int32_t fd, void (*cb_fd_handler) (int32_t fd), int8_t del ); + +int32_t set_packet_hook( int32_t packet_type, void (*cb_packet_handler) (struct packet_buff *), int8_t del ); + +#define CB_OGM_ACCEPT 0 +#define CB_OGM_REJECT -1 + +/* +enum cb_ogm_t { + CB_OGM_ACCEPT, + CB_OGM_REJECT +}; +*/ + +// only one cb_ogm_hook per plugin +int32_t set_ogm_hook( int32_t (*cb_ogm_handler) ( struct packet_buff *, uint16_t oCtx, struct router_node *old_router ), int8_t del ); + +//int32_t set_snd_ext_hook( uint16_t ext_type, int32_t (*cb_snd_ext_handler) ( unsigned char* ext_buff ), int8_t del ); + + + +//for registering data hooks: + +enum { + PLUGIN_DATA_ORIG, + PLUGIN_DATA_SIZE +}; + +extern int32_t plugin_data_registries[PLUGIN_DATA_SIZE]; + + +int32_t reg_plugin_data( uint8_t data_type ); + +#ifdef WITHUNUSED +void **get_plugin_data( void *data, uint8_t data_type, int32_t registry ); +#endif + + + + +/************************************** + *to be used by bmx sceleton... + */ +void init_plugin( void ); +void cleanup_plugin( void ); + + +//void cb_config_hooks( void ); +void cb_plugin_hooks( void* data, int32_t cb_id ); + +//returns number of called packet hooks for this packet_type +uint32_t cb_packet_hooks( int32_t packet_type, struct packet_buff *pb ); + +//return value FAILURE means that ogm or extension header is inacceptible and must be dropped ! +int32_t cb_ogm_hooks( struct packet_buff *pb, uint16_t oCtx, struct router_node *old_router ); + +int32_t cb_snd_ext_hook( uint16_t ext_type, unsigned char* ext_buff ); + +// use void change_selects( void ) to trigger cb_fd_handler() + +
Added: trunk/bmx/route.c =================================================================== --- trunk/bmx/route.c (rev 0) +++ trunk/bmx/route.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,2218 @@ +/* + * Copyright (C) 2006 BATMAN/BMX contributors: + * Marek Lindner, Thomas Lopatic, Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <stdlib.h> +#include <arpa/inet.h> /* inet_ntop() */ +#include <errno.h> +#include <unistd.h> /* close() */ +#include <linux/if.h> /* ifr_if, ifr_tun */ +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +//from linux/wireless.h +#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ + +#include "bmx.h" +#include "msg.h" +#include "plugin.h" +#include "schedule.h" +#include "route.h" + + + + +int32_t base_port = DEF_BASE_PORT; + + +#define ARG_RT_TABLE "rt_table_offset" +#define DEF_RT_TABLE 64 +#define MIN_RT_TABLE 0 +#define MAX_RT_TABLE 254 +#define RT_TABLE_HOSTS_OFFS 0 +#define RT_TABLE_NETS_OFFS 1 +#define RT_TABLE_TUNS_OFFS 2 +#define RT_TABLE_MAX_OFFS 2 +static int32_t Rt_table = DEF_RT_TABLE; + + +#define ARG_RT_PRIO "prio_rules_offset" +#define MIN_RT_PRIO 3 +#define MAX_RT_PRIO 32765 +#define DEF_RT_PRIO 6400 +#define RT_PRIO_HOSTS (Rt_prio + 0) +#define RT_PRIO_NETWORKS (Rt_prio + 1) +#define RT_PRIO_TUNNEL (Rt_prio + 2) +static int32_t Rt_prio = DEF_RT_PRIO; + + + +#define ARG_PRIO_RULES "prio_rules" +#define ARG_THROW_RULES "throw_rules" +#define ARG_NO_POLICY_RT "no_policy_routing" +#define ARG_PEDANTIC_CLEANUP "pedantic_cleanup" + +#define DEF_PRIO_RULES 1 +static int32_t prio_rules = DEF_PRIO_RULES; + +#define DEF_THROW_RULES 1 +static int32_t throw_rules = DEF_THROW_RULES; + +#define DEF_PEDANT_CLNUP NO +static int32_t Pedantic_cleanup = DEF_PEDANT_CLNUP; + +#define DEF_LO_RULE 1 +static int32_t Lo_rule = DEF_LO_RULE; + +static uint8_t touched_systen_config = NO; + +static int nl_sk = -1; +static int flush_sk = -1; + +static int rt_sock = 0; + + +static int32_t forward_orig=-1, if_rp_filter_all_orig=-1, if_rp_filter_default_orig=-1, if_send_redirects_all_orig=-1, if_send_redirects_default_orig=-1; + +static LIST_SIMPEL( rules_list, struct rules_node, list ); +static LIST_SIMPEL( routes_list, struct routes_node, list ); +static LIST_SIMPEL( throw_list, struct throw_node, list ); + +uint8_t if_conf_soft_changed = NO; // temporary enabled to trigger changed interface configuration +uint8_t if_conf_hard_changed = NO; // temporary enabled to trigger changed interface configuration + + +int Mtu_min = MAX_MTU; + + + +static struct { + struct nlmsghdr nlh; + struct rtmsg rtm; + char buff[ 256 ]; +} req; + +STATIC_FUNC +void add_rtattr(int rta_type, char *data, int data_len) +{ + + struct rtattr *rta = (struct rtattr *)(((char *) &req) + NLMSG_ALIGN(req.nlh.nlmsg_len)); + + req.nlh.nlmsg_len = NLMSG_ALIGN( req.nlh.nlmsg_len ) + RTA_LENGTH(data_len); + + paranoia( -50173, (NLMSG_ALIGN( req.nlh.nlmsg_len ) > sizeof( req )) ); + // if this fails then double req buff size !! + + rta->rta_type = rta_type; + rta->rta_len = RTA_LENGTH(data_len); + memcpy( RTA_DATA(rta), data, data_len ); +} + +STATIC_FUNC +char *rt2str( uint8_t t ) { + if ( t == RTN_UNICAST ) + return "RTN_UNICAST"; + else if ( t == RTN_THROW ) + return "RTN_THROW "; + + return "RTN_ILLEGAL"; +} + + +STATIC_FUNC +char *trackt2str( uint8_t t ) { + if ( t == TRACK_NO ) + return "TRACK_NO"; + else if ( t == TRACK_STANDARD ) + return "TRACK_STANDARD"; + else if ( t == TRACK_MY_HNA ) + return "TRACK_MY_HNA"; + else if ( t == TRACK_MY_HNA ) + return "TRACK_MY_NET"; + else if ( t == TRACK_MY_NET ) + return "TRACK_MY_HNA"; + else if ( t == TRACK_OTHER_HOST ) + return "TRACK_OTHER_HOST"; + else if ( t == TRACK_OTHER_HNA ) + return "TRACK_OTHER_HNA"; + else if ( t == TRACK_TUNNEL ) + + return "TRACK_TUNNEL"; + + return "TRACK_ILLEGAL"; +} + + + +STATIC_FUNC +int open_netlink_socket( void ) { + + int sock = 0; + if ( ( sock = socket( AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE ) ) < 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, "can't create netlink socket for routing table manipulation: %s", + strerror(errno) ); + + return -1; + + } + + + if ( fcntl( sock, F_SETFL, O_NONBLOCK) < 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, "can't set netlink socket nonblocking : (%s)", strerror(errno)); + close(sock); + return -1; + } + + return sock; + +} + + +STATIC_FUNC +int rt_macro_to_table( int rt_macro ) { + + dbgf_all( DBGT_INFO, "rt_macro %d", rt_macro ); + + if ( rt_macro == RT_TABLE_HOSTS ) + return Rt_table + RT_TABLE_HOSTS_OFFS; + + else if ( rt_macro == RT_TABLE_NETWORKS ) + return Rt_table + RT_TABLE_NETS_OFFS; + + else if ( rt_macro == RT_TABLE_TUNNEL ) + return Rt_table + RT_TABLE_TUNS_OFFS; + + else if ( rt_macro > MAX_RT_TABLE ) + cleanup_all( -500170 ); + + else if ( rt_macro >= 0 ) + return rt_macro; + + cleanup_all( -500171 ); + + return 0; + +} + +STATIC_FUNC +int8_t track_rule_and_proceed(uint32_t network, int16_t mask, uint32_t prio, int16_t rt_table, char* iif, + int16_t rule_type, int8_t del, int8_t track_t) +{ + + struct list_node *list_pos, *tmp_pos, *first_found_pos=NULL, *first_found_prev=NULL; + struct list_node *prev_pos = (struct list_node*)&rules_list; + struct rules_node *first_found_rn=NULL; + uint32_t found_rns=0; + + paranoia( -500176, ( !del && track_t == TRACK_NO ) ); + + + list_for_each_safe( list_pos, tmp_pos, &rules_list ) { + + struct rules_node *tmp_rn = list_entry( list_pos, struct rules_node, list ); + + if ( tmp_rn->network == network && + tmp_rn->netmask == mask && + tmp_rn->rt_table == rt_table && + tmp_rn->prio == prio && + tmp_rn->iif == iif && + tmp_rn->rta_type == rule_type ) + { + + // the kernel-ip-stack does not care about my track_t when adding the same rule twice + // but found_rns is evaluated for this + if ( !first_found_rn && (tmp_rn->track_t == track_t || track_t == TRACK_NO) ) { + first_found_rn = tmp_rn; + first_found_pos = list_pos; + first_found_prev = prev_pos; + } + + found_rns++; + + } + + prev_pos = &tmp_rn->list; + + } + + if ( (track_t == TRACK_NO) || + ( del && !first_found_rn ) || + ( del && found_rns != 1 ) || + ( !del && found_rns > 0 ) ) + { + + dbg( (track_t == TRACK_NO || (del && !first_found_rn)) ? DBGL_SYS : DBGL_CHANGES, + (track_t == TRACK_NO || (del && !first_found_rn)) ? DBGT_ERR : DBGT_INFO, + " %s rule from %s/%d table %d prio %d iif %s type %d " + "%s exists %d tims with at least %d exact match", + del?"del":"add", ipStr(network), mask, rt_table, prio, iif, rule_type, + trackt2str(track_t), found_rns, (first_found_rn?1:0) ); + } + + if ( track_t == TRACK_NO ) + return YES; + + if ( del ) { + + if ( first_found_rn ) { + + list_del_next(&rules_list, first_found_prev); + debugFree(first_found_rn, -300072); + + if (found_rns > 1) + return NO; + + } else { + return NO; + } + + } else { + + struct rules_node *tmp_rn = debugMalloc( sizeof( struct rules_node ), -300030 ); + memset( tmp_rn, 0, sizeof( struct rules_node ) ); + + tmp_rn->network = network; + tmp_rn->netmask = mask; + tmp_rn->rt_table = rt_table; + tmp_rn->prio = prio; + tmp_rn->iif = iif; + tmp_rn->rta_type = rule_type; + tmp_rn->track_t = track_t; + + list_add_tail(&rules_list, &tmp_rn->list); + + if ( found_rns > 0 ) + return NO; + + } + + return YES; +} + + +STATIC_FUNC +void configure_rule( uint32_t network, int16_t mask, int16_t rt_table_macro, uint32_t prio, char* iif, + int16_t rta_type, int8_t del, int8_t track_t) +{ + + char buf[4096], str1[16]; + struct sockaddr_nl nladdr; + struct msghdr msg; + struct nlmsghdr *nh; + struct iovec iov; + memset(&iov, 0, sizeof (struct iovec)); + iov.iov_base = buf; + iov.iov_len = sizeof (buf); + + uint16_t rt_table = rt_macro_to_table(rt_table_macro); + + inet_ntop( AF_INET, &network, str1, sizeof (str1) ); + + if (track_rule_and_proceed(network, mask, prio, rt_table, iif, rta_type, del, track_t) == NO) + return; + + + dbg( DBGL_CHANGES, DBGT_INFO, "%s rule from %s/%d table %d prio %d iif %s type %d", + del?"del":"add", str1, mask, rt_table, prio, iif, rta_type ); + + + memset( &nladdr, 0, sizeof(struct sockaddr_nl) ); + memset( &req, 0, sizeof(req) ); + memset( &msg, 0, sizeof(struct msghdr) ); + + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.nlh.nlmsg_pid = My_pid; + + req.rtm.rtm_family = AF_INET; + req.rtm.rtm_table = rt_table; + + dbgf_all( DBGT_INFO, "%s ip rule pref %d iif %s %d %s/%d lookup table %d", + (del?"del":"add"), prio, iif, rta_type, str1, mask, rt_table ); + + + if ( del ) { + + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.nlh.nlmsg_type = RTM_DELRULE; + + req.rtm.rtm_scope = RT_SCOPE_NOWHERE; + + } else { + + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL; + req.nlh.nlmsg_type = RTM_NEWRULE; + + req.rtm.rtm_scope = RT_SCOPE_UNIVERSE; + req.rtm.rtm_protocol = RTPROT_STATIC; + req.rtm.rtm_type = RTN_UNICAST; + + } + + + if (rta_type == RTA_DST) { + + req.rtm.rtm_dst_len = mask; + add_rtattr( rta_type, (char*)&network, sizeof(network) ); + + } else if ( rta_type == RTA_SRC ) { + + req.rtm.rtm_src_len = mask; + add_rtattr( rta_type, (char*)&network, sizeof(network) ); + } + + + if ( iif ) + add_rtattr( RTA_IIF, iif, strlen(iif)+1 ); + + + if ( prio ) + add_rtattr( RTA_PRIORITY, (char*)&prio, sizeof(prio) ); + + + if ( sendto( nl_sk, &req, req.nlh.nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(struct sockaddr_nl) ) < 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, + "can't send message to kernel via netlink socket for routing rule manipulation: %s", + strerror(errno) ); + return; + + } + + + while ( 1 ) { + + msg.msg_name = (void *)&nladdr; + msg.msg_namelen = sizeof(nladdr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + int32_t len = recvmsg( nl_sk, &msg, 0 ); + + + if ( len < 0 ) { + + if ( errno == EINTR ) { + dbgf( DBGL_SYS, DBGT_WARN, "(EINTR) %s", strerror(errno) ); + continue; + } + + if ( errno == EWOULDBLOCK || errno == EAGAIN ) { + //dbgf( DBGL_SYS, DBGT_WARN, "(EWOULDBLOCK || EAGAIN) %s", strerror(errno) ); + break; + } + + dbgf( DBGL_SYS, DBGT_ERR, "%s", strerror(errno) ); + + continue; + + } + + if ( len == 0 ) { + dbgf( DBGL_SYS, DBGT_ERR, "netlink EOF" ); + } + + nh = (struct nlmsghdr *)buf; + + while ( NLMSG_OK(nh, (uint32_t)len) ) { + + if ( nh->nlmsg_type == NLMSG_DONE ) + return; + + if ( ( nh->nlmsg_type == NLMSG_ERROR ) && ( ((struct nlmsgerr*)NLMSG_DATA(nh))->error != 0 ) ) { + + dbg( DBGL_SYS, DBGT_WARN, "can't %s rule %s %s/%i iif %s table %d, prio %d: %s " + "(if busy: Help me! Howto avoid this ERROR message?)", + del ? "delete" : "add", + ( rta_type == RTA_DST ? "to" : + ( rta_type == RTA_SRC ? "from" : + ( rta_type == RTA_IIF ? "iif" : "???") ) ), + str1, mask, iif, rt_table, prio, + strerror(-((struct nlmsgerr*)NLMSG_DATA(nh))->error) ); + } + + nh = NLMSG_NEXT( nh, len ); + + } + } +} + +STATIC_FUNC +void flush_tracked_rules( int8_t track_type ) { + + dbgf_all( DBGT_INFO, "%s", trackt2str(track_type) ); + + struct list_node *list_pos; + struct rules_node *rn, *p_rn=NULL; + + + list_for_each( list_pos, &rules_list ) { + + rn = list_entry( list_pos, struct rules_node, list ); + + if ( p_rn ) + configure_rule( p_rn->network, p_rn->netmask, + p_rn->rt_table, p_rn->prio, p_rn->iif, p_rn->rta_type, DEL, p_rn->track_t ); + + if ( track_type == rn->track_t || track_type == TRACK_NO ) + p_rn = rn; + else + p_rn = NULL; + + } + + if ( p_rn ) + configure_rule( p_rn->network, p_rn->netmask, + p_rn->rt_table, p_rn->prio, p_rn->iif, p_rn->rta_type, DEL, p_rn->track_t ); + +} + +static void flush_tracked_routes( int8_t track_type ) { + + dbgf_all( DBGT_INFO, "%s", trackt2str(track_type) ); + + struct list_node *list_pos; + struct routes_node *rn, *p_rn=NULL; + + + list_for_each( list_pos, &routes_list ) { + + rn = list_entry( list_pos, struct routes_node, list ); + + if ( p_rn ) + configure_route( p_rn->dest, p_rn->netmask, p_rn->metric, 0, 0, 0, 0, + p_rn->rt_table, p_rn->rta_type, DEL, p_rn->track_t ); + + if ( track_type == rn->track_t || track_type == TRACK_NO ) + p_rn = rn; + else + p_rn = NULL; + + } + + if ( p_rn ) + configure_route( p_rn->dest, p_rn->netmask, p_rn->metric, 0, 0, 0, 0, + p_rn->rt_table, p_rn->rta_type, DEL, p_rn->track_t ); + +} + +static void flush_routes_rules( int8_t is_rule ) { + + dbgf_all( DBGT_INFO, "is_rule %d", is_rule); + + size_t len; + int rtl; + char buf[8192]; + struct sockaddr_nl nladdr; + struct msghdr msg; + struct nlmsghdr *nh; + struct rtmsg *rtm; + struct { + struct nlmsghdr nlh; + struct rtmsg rtm; + } req; + struct rtattr *rtap; + + struct iovec iov; + + memset(&iov, 0, sizeof (struct iovec)); + iov.iov_base = buf; + iov.iov_len = sizeof (buf); + + memset( &nladdr, 0, sizeof(struct sockaddr_nl) ); + memset( &req, 0, sizeof(req) ); + + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.nlh.nlmsg_pid = My_pid; + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.nlh.nlmsg_type = ( is_rule ? RTM_GETRULE : RTM_GETROUTE ); + + req.rtm.rtm_family = AF_INET; + req.rtm.rtm_scope = RTN_UNICAST; + + if ( sendto( flush_sk, &req, req.nlh.nlmsg_len, 0, + (struct sockaddr *)&nladdr, sizeof(struct sockaddr_nl) ) < 0 ) + { + + dbg( DBGL_SYS, DBGT_ERR, + "can't send message to kernel via netlink socket for flushing the routing table: %s", + strerror(errno) ); + + cleanup_all( CLEANUP_FAILURE ); + } + + uint8_t nlm_f_multi = NO; + + do { + memset(&msg, 0, sizeof(struct msghdr)); + memset(&nladdr, 0, sizeof(struct sockaddr_nl)); + + nladdr.nl_family = AF_NETLINK; + + msg.msg_name = (void *) & nladdr; + msg.msg_namelen = sizeof(nladdr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + + len = recvmsg(flush_sk, &msg, 0); + nh = (struct nlmsghdr *) buf; + + dbgf_all(DBGT_INFO, "searching: %s NLMSG_OK: %d len: %zd", + is_rule ? "rules" : "routes", NLMSG_OK(nh, len), len); + + + while (NLMSG_OK(nh, len)) { + + if ( nh->nlmsg_flags & NLM_F_MULTI ) + nlm_f_multi = YES; + + if (nh->nlmsg_type == NLMSG_DONE) { + dbgf_all( DBGT_INFO, "found NLMSG_DONE"); + break; + } + + if ((nh->nlmsg_type == NLMSG_ERROR) && (((struct nlmsgerr*) NLMSG_DATA(nh))->error != 0)) { + + dbg(DBGL_SYS, DBGT_ERR, "can't flush %s: %s", + (is_rule ? "routing rules" : "routing table"), + strerror(-((struct nlmsgerr*) NLMSG_DATA(nh))->error)); + + cleanup_all( CLEANUP_FAILURE ); + } + + rtm = (struct rtmsg *) NLMSG_DATA(nh); + rtap = (struct rtattr *) RTM_RTA(rtm); + rtl = RTM_PAYLOAD(nh); + + nh = NLMSG_NEXT(nh, len); + + if ( /* rtm->rtm_table == 0 || */ + rtm->rtm_table == Rt_table + RT_TABLE_NETS_OFFS || + rtm->rtm_table == Rt_table + RT_TABLE_HOSTS_OFFS || + rtm->rtm_table == Rt_table + RT_TABLE_TUNS_OFFS ) { + + int8_t rule_type = RTA_UNSPEC; + uint32_t dest = 0; + uint32_t router = 0; + uint16_t mask = 0; + int32_t oif = 0; + uint32_t prio = 0; + char *iif = NULL; + + while (RTA_OK(rtap, rtl)) { + + dbgf_all( DBGT_INFO, "found rtm_table %d, rta_type %d rta_len %d", + rtm->rtm_table, rtap->rta_type, rtap->rta_len); + + + switch (rtap->rta_type) { + + case RTA_DST: + dest = *((int32_t *) RTA_DATA(rtap)); + mask = rtm->rtm_dst_len; + rule_type = RTA_DST; + break; + + case RTA_SRC: + dest = *((int32_t *) RTA_DATA(rtap)); + mask = rtm->rtm_src_len; + rule_type = RTA_SRC; + break; + + case RTA_GATEWAY: + router = *((int32_t *) RTA_DATA(rtap)); + break; + + case RTA_OIF: + oif = *((int32_t *) RTA_DATA(rtap)); + break; + + case RTA_PRIORITY: + prio = *((uint32_t *) RTA_DATA(rtap)); + break; + + case RTA_IIF: + iif = ((char *) RTA_DATA(rtap)); + break; + + case 15: // FIXME: RTA_TABLE is not always available - not needed but avoid warning + break; + + case RTA_PREFSRC: // rta_type 7 - not needed but avoid warning + break; + + default: + dbg(DBGL_SYS, DBGT_ERR, "unknown rta type: %i", rtap->rta_type); + break; + + } + + rtap = RTA_NEXT(rtap, rtl); + + } + + + if (is_rule) { + + configure_rule( dest, mask, rtm->rtm_table, prio, iif, rule_type, DEL, TRACK_NO); + + + dbg(DBGL_SYS, DBGT_WARN, "flushing orphan rule type %d dest? %s/%d table %d prio %d", + rule_type, ipStr(dest), mask , rtm->rtm_table, prio); + + } else { + + configure_route(dest, mask, prio, router, 0, oif, "unknown", rtm->rtm_table, rtm->rtm_type, DEL, TRACK_NO); + + dbg(DBGL_SYS, DBGT_WARN, "flushing orphan route to %s via %s type %d table %d", + ipStr(dest), ipStr(router), rtm->rtm_type, rtm->rtm_table); + } + } + } + + } while ( !( nlm_f_multi == NO || ( ( NLMSG_OK(nh, len) && (nh->nlmsg_type == NLMSG_DONE) ) ) ) ); + +} + +static void check_proc_sys( char *file, int32_t desired, int32_t *backup ) { + + FILE *f; + int32_t state = 0; + char filename[MAX_PATH_SIZE]; + int trash; + + + sprintf( filename, "/proc/sys/%s", file ); + + if((f = fopen(filename, "r" )) == NULL) { + + dbgf( DBGL_SYS, DBGT_ERR, "can't open %s for reading! retry later..", filename ); + + if ( backup ) + cleanup_all( CLEANUP_FAILURE ); + + return; + } + + trash=fscanf(f, "%d", &state); + fclose(f); + + if ( backup ) + *backup = state; + + // other routing protocols are probably not able to handle this therefore + // it is probably better to leave the routing configuration operational as it is! + if ( !backup && !Pedantic_cleanup && state != desired ) { + + dbg_mute( 50, DBGL_SYS, DBGT_INFO, + "NOT restoring %s to NOT mess up other routing protocols. " + "Use --%s=1 to enforce proper cleanup", + file, ARG_PEDANTIC_CLEANUP ); + + return; + } + + + if ( state != desired ) { + + touched_systen_config = YES; + + dbg( DBGL_SYS, DBGT_INFO, "changing %s from %d to %d", filename, state, desired ); + + if((f = fopen(filename, "w" )) == NULL) { + + dbgf( DBGL_SYS, DBGT_ERR, + "can't open %s for writing! retry later...", filename ); + return; + } + + fprintf(f, "%d", desired?1:0 ); + fclose(f); + + } +} + + +static void restore_kernel_config ( struct dev_node *dev_node ) { + + if ( !touched_systen_config ) + return; + + + if ( dev_node ) { + + char filename[100]; + + if (dev_node->rp_filter_orig > -1) { + sprintf( filename, "net/ipv4/conf/%s/rp_filter", dev_node->name_phy); + check_proc_sys( filename, dev_node->rp_filter_orig, NULL ); + } + + dev_node->rp_filter_orig = -1; + + + if (dev_node->send_redirects_orig > -1) { + sprintf( filename, "net/ipv4/conf/%s/send_redirects", dev_node->name_phy); + check_proc_sys( filename, dev_node->send_redirects_orig, NULL ); + } + + dev_node->send_redirects_orig = -1; + + } else { + + if( if_rp_filter_all_orig != -1 ) + check_proc_sys( "net/ipv4/conf/all/rp_filter", if_rp_filter_all_orig, NULL ); + + if_rp_filter_all_orig = -1; + + if( if_rp_filter_default_orig != -1 ) + check_proc_sys( "net/ipv4/conf/default/rp_filter", if_rp_filter_default_orig, NULL ); + + if_rp_filter_default_orig = -1; + + if( if_send_redirects_all_orig != -1 ) + check_proc_sys( "net/ipv4/conf/all/send_redirects", if_send_redirects_all_orig, NULL ); + + if_send_redirects_all_orig = -1; + + if( if_send_redirects_default_orig != -1 ) + check_proc_sys( "net/ipv4/conf/default/send_redirects", if_send_redirects_default_orig, NULL ); + + if_send_redirects_default_orig = -1; + + if( forward_orig != -1 ) + check_proc_sys( "net/ipv4/ip_forward", forward_orig, NULL ); + + forward_orig = -1; + + } + +} + + +static int8_t get_if_req( char* dev_name, struct ifreq *int_req, int siocgi_req ) { + + memset( int_req, 0, sizeof (struct ifreq) ); + strncpy( int_req->ifr_name, dev_name, IFNAMSIZ - 1 ); + + if ( ioctl( rt_sock, siocgi_req, int_req ) < 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, "can't get SIOCGI %d of interface %s: %s", siocgi_req, dev_name, strerror(errno) ); + return FAILURE; + } + + return SUCCESS; + +} + +static int8_t dev_validate( uint8_t set, struct dev_node *bif, char* dev_name, uint8_t reduced ) { + + struct ifreq int_req; + + + if ( get_if_req( dev_name, &int_req, SIOCGIFADDR ) == FAILURE ) + goto if_validate_failure; + + if ( set ) { + + bif->ip4_addr = ((struct sockaddr_in *)&int_req.ifr_addr)->sin_addr.s_addr; + addr_to_str( bif->ip4_addr, bif->ip4_str ); + + } else if ( bif->ip4_addr != ((struct sockaddr_in *)&int_req.ifr_addr)->sin_addr.s_addr ) { + + dbg( DBGL_SYS, DBGT_WARN, "IP address of %s: changed from %s to %s !", + dev_name, ipStr(bif->ip4_addr), + ipStr(((struct sockaddr_in *)&int_req.ifr_addr)->sin_addr.s_addr) ); + goto if_validate_failure; + } + + if ( !bif->ip4_addr ) { + + dbg( DBGL_SYS, DBGT_ERR, "invalid IP address: %s %s", dev_name, ipStr(0)); + goto if_validate_failure; + } + + + if ( get_if_req( dev_name, &int_req, SIOCGIFNETMASK ) == FAILURE ) + goto if_validate_failure; + + if ( set ) { + + bif->ip4_netaddr = ( ((struct sockaddr_in *)&int_req.ifr_addr)->sin_addr.s_addr & bif->ip4_addr ); + + } else if ( bif->ip4_netaddr != ( ((struct sockaddr_in *)&int_req.ifr_addr)->sin_addr.s_addr & bif->ip4_addr ) ) { + + dbg( DBGL_SYS, DBGT_WARN, "Net address of interface %s changed", dev_name ); + goto if_validate_failure; + } + + if ( set ) { + + bif->ip4_netmask = ((struct sockaddr_in *)&int_req.ifr_addr)->sin_addr.s_addr; + bif->ip4_prefix_length = bits_count( bif->ip4_netmask ); + + } else if ( bif->ip4_netmask != ((struct sockaddr_in *)&int_req.ifr_addr)->sin_addr.s_addr ) { + + dbg( DBGL_SYS, DBGT_WARN, "Prefix length of interface %s changed from %d to %d", + dev_name, bif->ip4_prefix_length, + bits_count( ((struct sockaddr_in *)&int_req.ifr_addr)->sin_addr.s_addr ) ); + goto if_validate_failure; + } + + if ( !bif->ip4_prefix_length ) { + + dbg( DBGL_SYS, DBGT_WARN, "Prefix length of IF %s must be > 0", dev_name ); + goto if_validate_failure; + } + + + if ( reduced ) + return SUCCESS; + + + if ( get_if_req( dev_name, &int_req, SIOCGIFINDEX ) == FAILURE ) + goto if_validate_failure; + + if ( set ) { + + bif->index = int_req.ifr_ifindex; + + } else if ( bif->index != int_req.ifr_ifindex ) { + + dbg( DBGL_SYS, DBGT_WARN, "Interface index of %s %s: changed from %d to %d !", + dev_name, ipStr(bif->ip4_addr), + bif->index, int_req.ifr_ifindex ); + + } + + + + if ( get_if_req( dev_name, &int_req, SIOCGIFBRDADDR ) == FAILURE ) + goto if_validate_failure; + + if ( set ) { + + bif->ip4_broad = ((struct sockaddr_in *)&int_req.ifr_broadaddr)->sin_addr.s_addr; + + } else if ( bif->ip4_broad != ((struct sockaddr_in *)&int_req.ifr_broadaddr)->sin_addr.s_addr ) { + + dbg( DBGL_SYS, DBGT_WARN, "Broadcast address of %s changed", dev_name ); + goto if_validate_failure; + } + + if ( !bif->ip4_broad && bif->linklayer != VAL_DEV_LL_LO) { + + dbg( DBGL_SYS, DBGT_ERR, "invalid broadcast address: %s %s", dev_name, ipStr(0) ); + goto if_validate_failure; + } + + + + + if ( get_if_req( dev_name, &int_req, SIOCGIFMTU ) == FAILURE ) + goto if_validate_failure; + + if ( set ) { + + bif->ip4_mtu = int_req.ifr_mtu; + + } else if ( bif->ip4_mtu != int_req.ifr_mtu ) { + + dbg( DBGL_SYS, DBGT_WARN, "MTU of interface %s changed from %d to %d", + dev_name, bif->ip4_mtu, int_req.ifr_mtu ); + goto if_validate_failure; + } + + + return SUCCESS; + +if_validate_failure: + + bif->ip4_addr = 0; + addr_to_str( 0, bif->ip4_str ); + return FAILURE; +} + + + +static char *get_ip4conf_buffer ( struct ifconf *ifc ) { + + /* use netlink, netdevice and rtnetlink (7) + * to resolve ipv6 addresses. SIOCGIFCONF will only resolv ipv4 addresses !!! + */ + + char *buf; + + int32_t len = 10 * sizeof(struct ifreq); /* initial buffer size guess (10 interfaces) */ + + while ( 1 ) { + + buf = debugMalloc( len, -300031 ); + memset( buf, 0, len); + memset( ifc, 0, sizeof( struct ifconf )); + + ifc->ifc_len = len; + ifc->ifc_buf = buf; + + if ( ioctl( rt_sock, SIOCGIFCONF, ifc ) < 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, "can't add interface rules (SIOCGIFCONF): %s", strerror(errno) ); + //close( tmp_fd ); + debugFree( buf, -300073); + return NULL; + + } else { + + if ( ifc->ifc_len < len ) + break; + + } + + len += 10 * sizeof(struct ifreq); + debugFree( buf, -300074 ); + + } + + return buf; +} + + +static int dev_up(char *name) +{ + struct ifreq int_req; + + memset(&int_req, 0, sizeof (struct ifreq)); + strncpy(int_req.ifr_name, name, IFNAMSIZ-1); + + if (ioctl(rt_sock, SIOCGIFFLAGS, &int_req) < 0) + return 0; + + if (!(int_req.ifr_flags & IFF_UP)) + return 0; + + if (ioctl(rt_sock, SIOCGIFADDR, &int_req) < 0) + return 0; + + return 1; + +} + + + + +static void dev_reconfigure_soft( struct dev_node *bif) { + + if ( !bif->active ) { + + dbg( DBGL_SYS, DBGT_INFO, + "skipping invalid IF %s ", bif->name ); + + } else if ( bif->linklayer == VAL_DEV_LL_LO ) { + + dbg( DBGL_SYS, DBGT_INFO, + "enabled loopback %s %s %s/%d brc %s", + ARG_DEV, bif->name, ipStr(bif->ip4_addr), bif->ip4_prefix_length, ipStr(bif->ip4_broad) ); + + } else if ( bif->linklayer == VAL_DEV_LL_WLAN ) { + + dbg( DBGL_SYS, DBGT_INFO, + "enabled wireless %s %s %s/%d brc %s (use %s /l=%d to optimize for ethernet)", + ARG_DEV, bif->name, ipStr(bif->ip4_addr), bif->ip4_prefix_length, ipStr(bif->ip4_broad), bif->name, VAL_DEV_LL_LAN ); + + bif->send_clones = wl_clones; + + } else { + + dbg( DBGL_SYS, DBGT_INFO, + "enabled ethernet %s %s %s/%d brc %s (use %s /l=%d to optimize for wireless)", + ARG_DEV, bif->name, ipStr(bif->ip4_addr), bif->ip4_prefix_length, ipStr(bif->ip4_broad), bif->name, VAL_DEV_LL_WLAN ); + + bif->send_clones = DEF_LAN_CLONES; + + } + + + if ( bif == primary_if ) + bif->announce = YES; + else + bif->announce = DEF_DEV_ANNOUNCE; + + + bif->antenna_diversity = 1; + + if ( bif->send_clones_conf != -1 ) + bif->send_clones = bif->send_clones_conf; + + if ( bif->antenna_diversity_conf != -1 ) + bif->antenna_diversity = bif->antenna_diversity_conf; + + if ( bif->announce_conf != -1 ) + bif->announce = bif->announce_conf; + + bif->link_activity_timestamp = bmx_time; + bif->next_powersave_hardbeat = bmx_time; + + bif->soft_conf_changed = NO; + + update_my_description_adv(); + +} + +static int8_t bind_to_iface( int32_t sock, char *dev ) { + + errno=0; + + if ( setsockopt( sock, SOL_SOCKET, SO_BINDTODEVICE, dev, strlen( dev ) + 1 ) < 0 ) { + dbg( DBGL_SYS, DBGT_ERR, "Cannot bind socket to device %s : %s", dev, strerror(errno)); + return -1; + } + + return 1; +} + + + +static void dev_activate( struct dev_node *bif ) { + + if ( dev_validate( YES/*set*/, bif, bif->name, NO/*reduced check*/ ) == FAILURE ) + goto error; + + if ( wordsEqual( "lo", bif->name_phy ) ) { + + bif->linklayer = VAL_DEV_LL_LO; + + } else if ( bif->linklayer_conf != -1 ) { + + //FIXME: when this parameter is changed only if_reconfigure_soft is called + bif->linklayer = bif->linklayer_conf; + + } else /* check if interface is a wireless interface */ { + + struct ifreq int_req; + memset( &int_req, 0, sizeof (struct ifreq) ); + strncpy( int_req.ifr_name, bif->name_phy, IFNAMSIZ - 1 ); + + bif->linklayer = + (ioctl( rt_sock, SIOCGIWNAME, &int_req ) < 0 ? VAL_DEV_LL_LAN : VAL_DEV_LL_WLAN); + + } + + + if ( bif->linklayer != VAL_DEV_LL_LO && (bif->unicast_sock = socket( PF_INET, SOCK_DGRAM, 0 )) < 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, "can't create send socket: %s", strerror(errno) ); + goto error; + } + + // the src address and port used for sending: + bif->ip4_unicast_addr.sin_addr.s_addr = bif->ip4_addr; + bif->ip4_unicast_addr.sin_family = AF_INET; + bif->ip4_unicast_addr.sin_port = htons(base_port); + + // the dst address and port used for sending: + bif->ip4_netwbrc_addr.sin_addr.s_addr = bif->ip4_broad; + bif->ip4_netwbrc_addr.sin_family = AF_INET; + bif->ip4_netwbrc_addr.sin_port = htons(base_port); + + Mtu_min = MIN ( Mtu_min, bif->ip4_mtu ); + + dbgf_all( DBGT_INFO, "searching minimum MTU, so fare: %d, current dev %s, mtu: %d", + Mtu_min, bif->name, bif->ip4_mtu ); + + + if ( bif->linklayer == VAL_DEV_LL_LO ) { + + if ( bif->ip4_prefix_length != 32 /*|| bif->if_addr != bif->if_broad*/ ) + dbg_mute( 30, DBGL_SYS, DBGT_WARN, "netmask of loopback interface is %d but SHOULD BE 32", + bif->ip4_prefix_length ); + + } else { + + int set_on = 1, sock_opts; + + if ( setsockopt( bif->unicast_sock, SOL_SOCKET, SO_BROADCAST, &set_on, sizeof(set_on) ) < 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, "can't enable broadcasts on unicast socket: %s", strerror(errno) ); + goto error; + } + + // bind send socket to interface name + if ( bind_to_iface( bif->unicast_sock, bif->name_phy ) < 0 ) + goto error; + + // bind send socket to address + if ( bind( bif->unicast_sock, (struct sockaddr *)&bif->ip4_unicast_addr, sizeof(struct sockaddr_in) ) < 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, "can't bind unicast socket: %s", strerror(errno) ); + goto error; + } + + // make udp send socket non blocking + sock_opts = fcntl(bif->unicast_sock, F_GETFL, 0); + fcntl(bif->unicast_sock, F_SETFL, sock_opts | O_NONBLOCK); + +#ifdef SO_TIMESTAMP + if (setsockopt(bif->unicast_sock, SOL_SOCKET, SO_TIMESTAMP, &set_on, sizeof(set_on))) + dbg( DBGL_SYS, DBGT_WARN, + "No SO_TIMESTAMP support, despite being defined, falling back to SIOCGSTAMP"); +#else + dbg( DBGL_SYS, DBGT_WARN, "No SO_TIMESTAMP support, falling back to SIOCGSTAMP"); +#endif + + + + + // if the dst address used for sending is the full-broadcast address + // we'll also listen on the network-broadcast address + + struct sockaddr_in if_netwbrc_addr; + memset( &if_netwbrc_addr, 0, sizeof( struct sockaddr_in ) ); + if_netwbrc_addr.sin_family = AF_INET; + if_netwbrc_addr.sin_port = htons(base_port); + if ( bif->ip4_broad == 0xFFFFFFFF ) + if_netwbrc_addr.sin_addr.s_addr = bif->ip4_netaddr | ~(bif->ip4_netmask); + else + if_netwbrc_addr.sin_addr.s_addr = bif->ip4_broad; + + + // get netwbrc recv socket + if ( ( bif->netwbrc_sock = socket( PF_INET, SOCK_DGRAM, 0 ) ) < 0 ) { + + dbg( DBGL_CHANGES, DBGT_ERR, "can't create network-broadcast socket: %s", strerror(errno) ); + goto error; + } + + // bind recv socket to interface name + if ( bind_to_iface( bif->netwbrc_sock, bif->name_phy ) < 0 ) + goto error; + + // bind recv socket to address + if ( bind( bif->netwbrc_sock, (struct sockaddr *)&if_netwbrc_addr, sizeof(struct sockaddr_in) ) < 0 ) { + + dbg( DBGL_CHANGES, DBGT_ERR, "can't bind network-broadcast socket: %s", strerror(errno)); + goto error; + } + + + + // we'll always listen on the full-broadcast address + + struct sockaddr_in if_fullbrc_addr; + memset( &if_netwbrc_addr, 0, sizeof( struct sockaddr_in ) ); + if_fullbrc_addr.sin_addr.s_addr = 0xFFFFFFFF; + if_fullbrc_addr.sin_family = AF_INET; + if_fullbrc_addr.sin_port = htons(base_port); + + // get fullbrc recv socket + if ( ( bif->fullbrc_sock = socket( PF_INET, SOCK_DGRAM, 0 ) ) < 0 ) { + + dbg( DBGL_CHANGES, DBGT_ERR, "can't create full-broadcast socket: %s", strerror(errno) ); + goto error; + } + + // bind recv socket to interface name + if ( bind_to_iface( bif->fullbrc_sock, bif->name_phy ) < 0 ) + goto error; + + // bind recv socket to address + if ( bind( bif->fullbrc_sock, (struct sockaddr *)&if_fullbrc_addr, sizeof(struct sockaddr_in) ) < 0 ) { + + dbg( DBGL_CHANGES, DBGT_ERR, "can't bind full-broadcast socket: %s", strerror(errno)); + goto error; + } + + register_task( rand_num(RAND_INIT_DELAY), schedule_my_hello_message, bif ); + register_task( rand_num(RAND_INIT_DELAY), tx_packet, bif ); + + } + + check_kernel_config( bif ); + + + if ( bif == primary_if ) { + my_orig_node.primary_ip4 = bif->ip4_addr; + addr_to_str(bif->ip4_addr, my_orig_node.primary_ip4_str); + } + + bif->hard_conf_changed = NO; + + bif->soft_conf_changed = YES; + + bif->active = YES; + + if (avl_find(&dev_ip4_tree, &bif->ip4_addr)) { + dbgf(DBGL_SYS, DBGT_ERR, "%s already in dev_tree_ip4!", bif->ip4_str); + } else { + avl_insert(&dev_ip4_tree, bif, -300151); + bif->ip4_tree_addr = bif->ip4_addr; + } + + //reschedule if_reconfigure_soft( bif ) also called from check_interfaces() + // but should also be called here + // - before schedule_if_hello() + // - after if_active=YES + dev_reconfigure_soft( bif ); + + + //activate selector for active interfaces + change_selects(); + + //trigger plugins interested in changed interface configuration + cb_plugin_hooks( NULL, PLUGIN_CB_CONF ); + + return; + +error: + + dev_deactivate( bif ); + +} + +static int8_t track_route_and_proceed( uint32_t dest, int16_t mask, uint32_t metric, uint32_t gw, uint32_t src, int32_t ifi, char *dev, + int16_t rt_table, int16_t rta_type, int8_t del, int8_t track_t ) +{ + + struct list_node *list_pos, *tmp_pos, *prev_pos = (struct list_node*)&routes_list; + struct list_node *first_found_pos=NULL, *first_found_prev=NULL; + struct routes_node *first_found_rn=NULL; + uint32_t found_rns=0; + + paranoia(-500177, (!del && track_t == TRACK_NO)); + + list_for_each_safe( list_pos, tmp_pos, &routes_list ) { + + struct routes_node *tmp_rn = list_entry( list_pos, struct routes_node, list ); + + if ( tmp_rn->dest == dest && + tmp_rn->netmask == mask && + tmp_rn->metric == metric && + tmp_rn->rt_table == rt_table && + tmp_rn->rta_type == rta_type) + { + + // the kernel-ip-stack does not care about my track_t when adding the same route twice + // but found_rns is evaluated for this + if ( !first_found_rn && (tmp_rn->track_t == track_t || track_t == TRACK_NO ) ) { + + first_found_rn = tmp_rn; + first_found_pos = list_pos; + first_found_prev = prev_pos; + } + + found_rns++; + + } + + prev_pos = &tmp_rn->list; + + } + + if ( track_t == TRACK_NO || + ( del && !first_found_rn ) || + ( del && found_rns != 1 ) || + ( !del && found_rns > 0 ) ) + { + dbg( (track_t == TRACK_NO || (del && !first_found_rn)) ? DBGL_SYS : DBGL_ALL, + (track_t == TRACK_NO || (del && !first_found_rn)) ? DBGT_ERR : DBGT_INFO, + " %s route to %-15s via %-15s src %s dev %s table %d %s " + "%s has %d (%d exact) matches", + del?"del":"add", ipStr(dest), ipStr(gw), ipStr(src), dev, rt_table, + rt2str(rta_type), trackt2str(track_t), found_rns, (first_found_rn?1:0) ); + } + + if ( track_t == TRACK_NO ) + return YES; + + + if ( del && !first_found_rn) { + + dbgf_all( DBGT_WARN, "removing orphan route"); + return NO; + + } else if ( del && first_found_rn ) { + + list_del_next(&routes_list, first_found_prev); + debugFree( first_found_rn, -300075 ); + + if ( found_rns > 1 ) + return NO; + + + } else if ( !del ) { + + struct routes_node *tmp_rn = debugMalloc( sizeof( struct routes_node ), -300032 ); + memset( tmp_rn, 0, sizeof( struct routes_node ) ); + + tmp_rn->dest = dest; + tmp_rn->netmask = mask; + tmp_rn->metric = metric; + tmp_rn->rt_table = rt_table; + tmp_rn->rta_type = rta_type; + tmp_rn->track_t = track_t; + + list_add_tail(&routes_list, &tmp_rn->list); + + if ( found_rns > 0 ) + return NO; + + } + + return YES; +} + + + +void configure_route( uint32_t dest, int16_t mask, uint32_t metric, uint32_t gw, uint32_t src, int32_t ifi, char *dev, + int16_t rt_table_macro, int16_t rta_type, int8_t del, int8_t track_t ) +{ + + uint32_t my_router; + char buf[4096]; + struct sockaddr_nl nladdr; + + struct msghdr msg; + struct nlmsghdr *nh; + + struct iovec iov; + + memset(&iov, 0, sizeof (struct iovec)); + iov.iov_base = buf; + iov.iov_len = sizeof (buf); + + uint16_t rt_table = rt_macro_to_table(rt_table_macro); + + dest = dest & htonl( 0xFFFFFFFF<<(32 - mask ) ); + + if ( ( !throw_rules ) && ( rta_type == RTN_THROW ) ) + return; + + + + if ( track_route_and_proceed(dest, mask, metric, gw, src, ifi, dev, rt_table, rta_type, del, track_t) == NO ) + return; + + if ( track_t != TRACK_OTHER_HOST ) + dbg( DBGL_CHANGES, DBGT_INFO, + " %s route to %15s/%-2d table %d via %-15s dev %-10s ifi %2d %s %s", + del?"del":"add", + ipStr(dest), mask, rt_table, ipStr(gw), dev, ifi, rt2str(rta_type), trackt2str(track_t)); + + + if ( gw == dest ) + my_router = 0; + + else + my_router = gw; + + + dbgf_all( DBGT_INFO, "%s %s to %s/%i via %s (table %i - %s src %s )", + del ? "del" : "add", + rt2str(rta_type), ipStr(dest), mask, ipStr(gw), rt_table, dev, ipStr(src)); + + + memset( &nladdr, 0, sizeof(struct sockaddr_nl) ); + memset( &req, 0, sizeof(req) ); + memset( &msg, 0, sizeof(struct msghdr) ); + + nladdr.nl_family = AF_NETLINK; + + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.nlh.nlmsg_pid = My_pid; + + req.rtm.rtm_family = AF_INET; + req.rtm.rtm_table = rt_table; + req.rtm.rtm_dst_len = mask; + + if ( del ) { + + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.nlh.nlmsg_type = RTM_DELROUTE; + req.rtm.rtm_scope = RT_SCOPE_NOWHERE; + + } else { + + req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL/* | NLM_F_REPLACE*/; + req.nlh.nlmsg_type = RTM_NEWROUTE; + + if ( /* source != 0 && */ rta_type == RTN_UNICAST && my_router == 0 ) { + req.rtm.rtm_scope = RT_SCOPE_LINK; + } else { + req.rtm.rtm_scope = RT_SCOPE_UNIVERSE; + } + + req.rtm.rtm_protocol = RTPROT_STATIC; // may be changed to some bmx specific value - see <linux/rtnetlink.h> + req.rtm.rtm_type = ( rta_type == RTN_THROW ? RTN_THROW : RTN_UNICAST ); + + } + + add_rtattr( RTA_DST, (char*)&dest, sizeof(dest) ); + + if ( rta_type == RTN_UNICAST ) { + + if ( my_router ) + add_rtattr( RTA_GATEWAY, (char*)&my_router, sizeof(my_router) ); + + if ( ifi ) + add_rtattr ( RTA_OIF, (char*)&ifi, sizeof(ifi) ); + + if ( src /* && my_router == 0 */) + add_rtattr( RTA_PREFSRC, (char*)&src, sizeof(src) ); + + if ( metric ) + add_rtattr( RTA_PRIORITY, (char*)&metric, sizeof(metric) ); + + } + + errno=0; + + if ( sendto( nl_sk, &req, req.nlh.nlmsg_len, 0, (struct sockaddr *)&nladdr, sizeof(struct sockaddr_nl) ) < 0 ) { + + dbg( DBGL_SYS, DBGT_ERR, + "can't send message to kernel via netlink socket for routing table manipulation: %s", + strerror(errno) ); + + return; + + } + + + while ( 1 ) { + + msg.msg_name = (void *)&nladdr; + msg.msg_namelen = sizeof(nladdr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + errno=0; + int32_t len = recvmsg( nl_sk, &msg, 0 ); + + + if ( len < 0 ) { + + if ( errno == EINTR ) { + + dbgf( DBGL_SYS, DBGT_WARN, "(EINTR) %s", strerror(errno) ); + + continue; + } + + if ( errno == EWOULDBLOCK || errno == EAGAIN ) { + //dbgf( DBGL_CHANGES, DBGT_WARN, "(EWOULDBLOCK || EAGAIN) %s", strerror(errno) ); + break; + } + + dbgf( DBGL_SYS, DBGT_ERR, "%s", strerror(errno) ); + + continue; + + } + + if ( !len ) { + dbgf( DBGL_SYS, DBGT_ERR, "netlink EOF" ); + } + + nh = (struct nlmsghdr *)buf; + + while ( NLMSG_OK(nh, (uint32_t)len) ) { + + if ( nh->nlmsg_type == NLMSG_DONE ) + return; + + if ( ( nh->nlmsg_type == NLMSG_ERROR ) && ( ((struct nlmsgerr*)NLMSG_DATA(nh))->error != 0 ) ) + { + dbg( DBGL_CHANGES, DBGT_WARN, "can't %s %s to %s/%i via %s (table %i): %s", + del ? "delete" : "add", rt2str(rta_type), ipStr(dest), mask, ipStr(gw), rt_table, + strerror(-((struct nlmsgerr*)NLMSG_DATA(nh))->error) ); + } + + nh = NLMSG_NEXT( nh, len ); + + } + } +} + + + + + + +int update_interface_rules( uint8_t cmd ) { + + + static uint8_t setup_tunnel = NO; + static uint8_t setup_networks = NO; + + static uint64_t checksum = 0; + + uint64_t old_checksum = checksum; + checksum = 0; + + uint8_t if_count = 1; + char *buf, *buf_ptr; + + struct ifreq *ifr; + struct ifconf ifc; + + + struct dev_node *dev_node; + + struct list_node *throw_pos; + struct throw_node *throw_node; + uint32_t no_netmask; + + if ( cmd != IF_RULE_CHK_IPS ) { + + flush_tracked_routes( TRACK_MY_NET ); + flush_tracked_rules( TRACK_MY_NET ); + + } + + if ( cmd == IF_RULE_SET_TUNNEL ) { + + setup_tunnel = YES; + return SUCCESS; //will be called again when bat0s' IP is set + + } else if ( cmd == IF_RULE_CLR_TUNNEL ) { + + setup_tunnel = NO; + return SUCCESS; //will be called again when bat0s' IP gets removed + + } else if ( cmd == IF_RULE_SET_NETWORKS ) { + + setup_networks = YES; + + } else if ( cmd == IF_RULE_CLR_NETWORKS ) { + + setup_networks = NO; + + } + + if ( !(buf = get_ip4conf_buffer( &ifc )) ) + return FAILURE; + + for ( buf_ptr = buf; buf_ptr < buf + ifc.ifc_len; ) { + + struct dev_node bif; +// memset( &bif, 0, sizeof(struct dev_node)); + + ifr = (struct ifreq *)buf_ptr; + + buf_ptr += sizeof(struct ifreq); + + /* ignore if not IPv4 interface */ + if ( ifr->ifr_addr.sa_family != AF_INET ) + continue; + + if ( !dev_up( ifr->ifr_name ) ) + continue; + + if ( dev_validate( YES/*set*/, &bif, ifr->ifr_name, YES/*reduced check only*/ ) == FAILURE ) + continue; + + size_t i; + struct config_key { + uint32_t if_prefix_length; + uint32_t if_netaddr; + uint32_t if_addr; + } config_key; + + memset( &config_key, 0, sizeof(struct config_key)); + config_key.if_prefix_length = bif.ip4_prefix_length; + config_key.if_netaddr = bif.ip4_netaddr; + config_key.if_addr = bif.ip4_addr; + + for (i = 0; i < sizeof( struct config_key); i++) { + checksum += ((unsigned char*)(&config_key))[i]; + checksum += (checksum << 10); + checksum ^= (checksum >> 6); + } + + dbgf_ext(DBGT_INFO, "%32ju %32ju %10s %15s %15s %2d ", + old_checksum, checksum, + ifr->ifr_name, ipStr(bif.ip4_addr), ipStr(bif.ip4_netaddr), bif.ip4_prefix_length); + + + if ( cmd == IF_RULE_CHK_IPS ) + continue; + + + uint8_t add_this_rule = YES; + + list_for_each(throw_pos, &throw_list) { + + throw_node = list_entry(throw_pos, struct throw_node, list); + + no_netmask = htonl( 0xFFFFFFFF<<(32 - throw_node->netmask ) ); + + if ( ((throw_node->addr & no_netmask) == (bif.ip4_netaddr & no_netmask)) ) + add_this_rule = NO; + + } + + + if( prio_rules && setup_tunnel == YES ) { + + if ( !Lo_rule && + (bif.ip4_netaddr & htonl( 0xFF000000 ) ) == ( htonl( 0x7F000000 /*127.0.0.0*/ ) ) ) + add_this_rule = NO; + + + if ( add_this_rule ) { + configure_rule( bif.ip4_netaddr, bif.ip4_prefix_length, + RT_TABLE_TUNNEL, RT_PRIO_TUNNEL, 0, RTA_SRC, ADD, TRACK_MY_NET ); + if_count++; + } + + if ( Lo_rule && strncmp( ifr->ifr_name, "lo", strlen("lo") ) == 0 ) { + + configure_rule( 0, 0, RT_TABLE_TUNNEL, RT_PRIO_TUNNEL, "lo", RTA_IIF, ADD, TRACK_MY_NET ); + } + + } + + if( throw_rules && setup_tunnel == YES ) + configure_route( bif.ip4_netaddr, bif.ip4_prefix_length, 0, + 0, 0, 0, ifr->ifr_name, RT_TABLE_TUNNEL, RTN_THROW, ADD, TRACK_MY_NET ); + + + if ( !(dev_node = get_bif( ifr->ifr_name ) ) ) + continue; + + + if( throw_rules && setup_networks == YES ) + configure_route( bif.ip4_netaddr, bif.ip4_prefix_length, 0, + 0, 0, 0, ifr->ifr_name, RT_TABLE_NETWORKS, RTN_THROW, ADD, TRACK_MY_NET ); + + + } + + + debugFree( buf, -300076 ); + + + if ( cmd != IF_RULE_CHK_IPS ) { + + list_for_each(throw_pos, &throw_list) { + + throw_node = list_entry(throw_pos, struct throw_node, list); + + configure_route( throw_node->addr, throw_node->netmask, 0, + 0, 0, 0, "unknown", RT_TABLE_HOSTS, RTN_THROW, ADD, TRACK_MY_NET ); + configure_route( throw_node->addr, throw_node->netmask, 0, + 0, 0, 0, "unknown", RT_TABLE_NETWORKS, RTN_THROW, ADD, TRACK_MY_NET ); + configure_route( throw_node->addr, throw_node->netmask, 0, + 0, 0, 0, "unknown", RT_TABLE_TUNNEL, RTN_THROW, ADD, TRACK_MY_NET ); + + } + + } + + + if ( cmd == IF_RULE_CHK_IPS && (checksum != old_checksum) ) { + dbg( DBGL_CHANGES, DBGT_INFO, + "systems' IP configuration changed! Going to re-init interface rules..."); + update_interface_rules( IF_RULE_UPD_ALL ); + } + + + return SUCCESS; + +} + + + + +// check for further traps: http://lwn.net/Articles/45386/ +void check_kernel_config( struct dev_node *dev_node ) { + + if ( dev_node ) { + + char filename[100]; + + sprintf( filename, "net/ipv4/conf/%s/rp_filter", dev_node->name_phy); + check_proc_sys( filename, 0, &dev_node->rp_filter_orig ); + + sprintf( filename, "net/ipv4/conf/%s/send_redirects", dev_node->name_phy); + check_proc_sys( filename, 0, &dev_node->send_redirects_orig ); + + } else { + + check_proc_sys( "net/ipv4/conf/all/rp_filter", 0, &if_rp_filter_all_orig ); + check_proc_sys( "net/ipv4/conf/default/rp_filter", 0, &if_rp_filter_default_orig ); + check_proc_sys( "net/ipv4/conf/all/send_redirects", 0, &if_send_redirects_all_orig ); + check_proc_sys( "net/ipv4/conf/default/send_redirects", 0, &if_send_redirects_default_orig ); + check_proc_sys( "net/ipv4/ip_forward", 1, &forward_orig ); + + } +} + + + +void dev_deactivate( struct dev_node *bif ) { + + dbg_mute( 30, DBGL_SYS, DBGT_WARN, "deactivating IF %-10s %-15s", bif->name, ipStr(bif->ip4_addr) ); + + + if ( bif->linklayer != VAL_DEV_LL_LO ) { + + remove_task( tx_packet, bif ); + remove_task( schedule_my_hello_message, bif ); + + purge_tx_timestamp_tree( bif, YES); + purge_dev_tx_list ( bif ); + + if (bif->unicast_sock != 0) + close(bif->unicast_sock); + + bif->unicast_sock = 0; + + if (bif->netwbrc_sock != 0) + close(bif->netwbrc_sock); + + bif->netwbrc_sock = 0; + + if (bif->fullbrc_sock != 0) + close(bif->fullbrc_sock); + + bif->fullbrc_sock = 0; + + + } + + if (!bif->ip4_tree_addr) { + dbgf(DBGL_SYS, DBGT_ERR, "no address given to remove in dev_tree_ip4!"); + } else if (!avl_find(&dev_ip4_tree, &bif->ip4_tree_addr)) { + dbgf(DBGL_SYS, DBGT_ERR, "%s not in dev_tree_ip4!", ipStr(bif->ip4_tree_addr)); + } else { + avl_remove(&dev_ip4_tree, &bif->ip4_tree_addr, -300192); + bif->ip4_tree_addr = 0; + } + bif->active = NO; + + + restore_kernel_config ( bif ); + + change_selects(); + + dbgf_all( DBGT_WARN, "Interface %s deactivated", bif->name ); + + if ( bif == primary_if && !terminating() ) { + + purge_orig( NULL, NO); + + dbg_mute( 30, DBGL_SYS, DBGT_WARN, + "You SHOULD always configure a loopback-alias interface for %s/32 to remain reachable under your primary IP!", + ipStr(bif->ip4_addr) ); + + } else { + + purge_orig( bif, NO ); + } + + update_my_description_adv(); +} + + + +void dev_check() { + + uint8_t cb_conf_hooks = NO; + + dbgf_all( DBGT_INFO, " " ); + + remove_task( dev_check, NULL ); + + //Do we need this? There was an interface attribute which change is not catched by ifevent_sk ?? + register_task( 5000, dev_check, NULL ); + + if ( !dev_name_tree.items ) { + dbg( DBGL_SYS, DBGT_ERR, "No interfaces specified"); + cleanup_all( CLEANUP_FAILURE ); + } + + + Mtu_min = MAX_MTU; + + struct avl_node *it=NULL; + struct dev_node *bif; + while ((bif = avl_iterate_item(&dev_name_tree, &it))) { + + if ((bif->active) && (!dev_up(bif->name))) { + + dbg( DBGL_SYS, DBGT_WARN, + "detected inactive but used %sprimary interface: %s ! Deactivating now...", + (bif == primary_if ? "" : "non-" ), bif->name ); + + cb_conf_hooks = YES; + dev_deactivate( bif ); + + + } else if ( bif->active && dev_up(bif->name) ) { + + /* Interface properties might have changed */ + + if ( if_conf_hard_changed || bif->hard_conf_changed || + dev_validate( NO/*set*/, bif, bif->name, NO/*reduced check*/ ) == FAILURE ) { + + cb_conf_hooks = YES; + dev_deactivate( bif ); + + } else { + + Mtu_min = MIN( Mtu_min, bif->ip4_mtu ); + + dbgf_all( DBGT_INFO, + "researching minimum MTU, so fare: %d, current dev %s, mtu: %d", + Mtu_min, bif->name, bif->ip4_mtu); + } + + } + + if (if_conf_soft_changed ) + bif->soft_conf_changed = YES; // to be considered in if_activate()->if_reconfigure_soft() + + if ( (!bif->active) && (dev_up(bif->name)) ) { + + struct dev_node *tmp_bif = avl_find_item(&dev_ip4_tree, &bif->ip4_addr); + + if (tmp_bif && !wordsEqual(tmp_bif->name, bif->name)) { + + dbg_mute(40, DBGL_SYS, DBGT_ERR, "IF %-10s IP %-15s already used for IF %s", + bif->name, bif->ip4_str, tmp_bif->name); + break; + + } else { + + if ( on_the_fly ) + dbg_mute( 50, DBGL_SYS, DBGT_INFO, + "detected valid but disabled dev: %s ! Activating now...", bif->name ); + + dev_activate( bif ); + } + } + + if ( /*bif->if_active &&*/ ( /*if_conf_soft_changed ||*/ bif->soft_conf_changed) ) { + + if ( on_the_fly ) + dbg( DBGL_CHANGES, DBGT_INFO, "%s soft interface configuration changed", bif->name ); + + dev_reconfigure_soft( bif ); + } + + + if ( !on_the_fly && !bif->active ) { + + if ( bif == primary_if ) { + dbg( DBGL_SYS, DBGT_ERR, + "at least primary interface %s MUST be operational at startup! " + "Use loopback (e.g. lo:bmx a.b.c.d/32 ) if nothing else is available!", + bif->name); + + cleanup_all( CLEANUP_FAILURE ); + } + + dbg( DBGL_SYS, DBGT_WARN, + "not using interface %s (retrying later): interface not ready", bif->name); + } + } + + if_conf_soft_changed = NO; + if_conf_hard_changed = NO; + + + if ( cb_conf_hooks ) + cb_plugin_hooks( NULL, PLUGIN_CB_CONF ); + + + if ( on_the_fly ) // opt_policy_rt() is responsible for this during init + update_interface_rules( IF_RULE_CHK_IPS ); + +} + + + + + +static int32_t opt_policy_rt ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + if ( cmd == OPT_APPLY ) { + + check_apply_parent_option( ADD, OPT_APPLY, _save, get_option( 0, 0, ARG_PRIO_RULES ), "0", cn ); + check_apply_parent_option( ADD, OPT_APPLY, _save, get_option( 0, 0, ARG_THROW_RULES ), "0", cn ); + + } else if ( cmd == OPT_SET_POST && !on_the_fly ) { + + // flush orphan routes must be before flushing rules, otherwise orphan routes are not found ! + flush_routes_rules( 0 /* flush routes */); + + /* add rule for hosts and announced interfaces and networks */ + if ( prio_rules ) + flush_routes_rules( 1 /* flush rules */); + + + } else if ( cmd == OPT_POST && !on_the_fly ) { + + // add rule for hosts and announced interfaces and networks + if ( prio_rules ) { + configure_rule( 0, 0, RT_TABLE_HOSTS, RT_PRIO_HOSTS, 0, RTA_DST, ADD, TRACK_STANDARD ); + configure_rule( 0, 0, RT_TABLE_NETWORKS, RT_PRIO_NETWORKS, 0, RTA_DST, ADD, TRACK_STANDARD ); + } + + // add rules and routes for interfaces + if ( update_interface_rules( IF_RULE_SET_NETWORKS ) < 0 ) + cleanup_all( CLEANUP_FAILURE ); + + } + + return SUCCESS; +} + + +static int32_t opt_throw ( uint8_t cmd, uint8_t _save, struct opt_type *opt, struct opt_parent *patch, struct ctrl_node *cn ) { + + uint32_t ip; + int32_t mask; + char tmp[30]; + struct throw_node *throw_node=NULL; + struct list_node *throw_tmp, *throw_pos; + + if ( cmd == OPT_ADJUST || cmd == OPT_CHECK || cmd == OPT_APPLY ) { + + if ( patch->p_val[0] >= '0' && patch->p_val[0] <= '9' ) { + // configure an unnamed throw-rule + + if ( str2netw( patch->p_val, &ip, '/', cn, &mask, 32 ) == FAILURE ) + return FAILURE; + + sprintf( tmp, "%s/%d", ipStr( validate_net_mask( ip, mask, 0 ) ), mask ); + set_opt_parent_val( patch, tmp ); + + } else { + // configure a named throw-rule + + // just for adjust and check + if ( adj_patched_network( opt, patch, tmp, &ip, &mask, cn ) == FAILURE ) + return FAILURE; + + if ( patch->p_diff == ADD ) { + if ( adj_patched_network( opt, patch, tmp, &ip, &mask, cn ) == FAILURE ) + return FAILURE; + } else { + // re-configure network and netmask parameters of an already configured and named throw-rule + if ( get_tracked_network( opt, patch, tmp, &ip, &mask, cn ) == FAILURE ) + return FAILURE; + } + } + + struct list_node *prev_pos = (struct list_node *)&throw_list; + list_for_each_safe( throw_pos, throw_tmp, &throw_list ) { + throw_node = list_entry(throw_pos, struct throw_node, list); + if ( throw_node->addr == ip && throw_node->netmask == mask ) + break; + prev_pos = &throw_node->list; + throw_node = NULL; + } + + if ( cmd == OPT_ADJUST ) + return SUCCESS; + + if ( ( patch->p_diff != ADD && !throw_node ) || ( patch->p_diff == ADD && throw_node ) ) { + dbg_cn( cn, DBGL_SYS, DBGT_ERR, "%s %s does %s exist!", + ARG_THROW, tmp, patch->p_diff == ADD ? "already" : "not" ); + return FAILURE; + } + + if ( cmd == OPT_CHECK ) + return SUCCESS; + + if ( patch->p_diff == DEL || patch->p_diff == NOP ) { + list_del_next( &throw_list, prev_pos ); + debugFree( throw_pos, -300077 ); + } + + if ( patch->p_diff == NOP ) { + // get new network again + if ( adj_patched_network( opt, patch, tmp, &ip, &mask, cn ) == FAILURE ) + return FAILURE; + } + + if ( patch->p_diff == ADD || patch->p_diff == NOP ) { + + throw_node = debugMalloc( sizeof(struct throw_node), -300033 ); + memset( throw_node, 0, sizeof(struct throw_node) ); + list_add_tail( &throw_list, &throw_node->list ); + + throw_node->addr = ip; + throw_node->netmask = mask; + } + + + if ( on_the_fly ) { + /* add rules and routes for interfaces */ + if ( update_interface_rules( IF_RULE_UPD_ALL ) < 0 ) + cleanup_all( CLEANUP_FAILURE ); + } + + return SUCCESS; + + + } else if ( cmd == OPT_UNREGISTER ) { + + while ((throw_node = list_rem_head(&throw_list))) + debugFree(throw_node, -300078); + +/* + list_for_each_safe( throw_pos, throw_tmp, &throw_list ) { + + list_del_next( &throw_list, (struct list_node *)&throw_list ); + + debugFree( throw_pos, -300078 ); + } +*/ + } + + return SUCCESS; +} + + + + +static struct opt_type route_options[]= +{ +// ord parent long_name shrt Attributes *ival min max default *func,*syntax,*help + + + {ODI,0,0, 0, 4,0,0,0,0,0, 0, 0, 0, 0, 0, + 0, "\nSystem and policy-routing options:"}, + + {ODI,0,ARG_BASE_PORT, 0, 4,A_PS1,A_ADM,A_INI,A_CFA,A_ANY, &base_port, MIN_BASE_PORT, MAX_BASE_PORT, DEF_BASE_PORT, 0, + ARG_VALUE_FORM, "set udp ports"}, + + {ODI,0,ARG_RT_PRIO, 0, 4,A_PS1,A_ADM,A_INI,A_CFA,A_ANY, &Rt_prio, MIN_RT_PRIO, MAX_RT_PRIO, DEF_RT_PRIO, 0, + ARG_VALUE_FORM, "set preferences for iproute2-style rules to rt_table (see: man ip)"}, + + {ODI,0,ARG_RT_TABLE, 0, 4,A_PS1,A_ADM,A_INI,A_CFA,A_ANY, &Rt_table, MIN_RT_TABLE, MAX_RT_TABLE-RT_TABLE_MAX_OFFS, DEF_RT_TABLE, 0, + ARG_VALUE_FORM, "set bmx routing-table offset (iproute2 style, see: man ip)"}, + +#ifndef LESS_OPTIONS + {ODI,0,ARG_THROW_RULES, 0, 4,A_PS1,A_ADM,A_INI,A_CFA,A_ANY, &throw_rules, 0, 1, DEF_THROW_RULES,0, + ARG_VALUE_FORM, "disable/enable default throw rules"}, + + {ODI,0,ARG_PRIO_RULES, 0, 4,A_PS1,A_ADM,A_INI,A_CFA,A_ANY, &prio_rules, 0, 1, DEF_PRIO_RULES, 0, + ARG_VALUE_FORM, "disable/enable default priority rules"}, +#endif + {ODI,0,ARG_NO_POLICY_RT, 'n',4,A_PS0,A_ADM,A_INI,A_ARG,A_ANY, 0, 0, 0, 0, opt_policy_rt, + 0, "disable policy routing (throw and priority rules)"}, + +#ifndef LESS_OPTIONS + + {ODI,0,"lo_rule", 0, 4,A_PS1,A_ADM,A_INI,A_CFA,A_ANY, &Lo_rule, 0, 1, DEF_LO_RULE, 0, + ARG_VALUE_FORM, "disable/enable autoconfiguration of lo rule"}, +#endif + {ODI,0,ARG_THROW, 0, 5,A_PMN,A_ADM,A_DYI,A_CFA,A_ANY, 0, 0, 0, 0, opt_throw, + ARG_PREFIX_FORM, "do NOT route packets matching src or dst IP range(s) into gateway tunnel or announced networks"}, + + {ODI,ARG_THROW,ARG_NETW, 'n',5,A_CS1,A_ADM,A_DYI,A_CFA,A_ANY, 0, 0, 0, 0, opt_throw, + ARG_NETW_FORM, "specify network of throw rule"}, + + {ODI,ARG_THROW,ARG_MASK, 'm',5,A_CS1,A_ADM,A_DYI,A_CFA,A_ANY, 0, 0, 0, 0, opt_throw, + ARG_MASK_FORM, "specify network of throw rule"} + +#ifndef LESS_OPTIONS + , + {ODI,0,ARG_PEDANTIC_CLEANUP, 0, 5,A_PS1,A_ADM,A_DYI,A_CFA,A_ANY, &Pedantic_cleanup,0, 1, DEF_PEDANT_CLNUP,0, + ARG_VALUE_FORM, "disable/enable pedantic cleanup of system configuration (like ip_forward,..) \n" + " at program termination. Its generally safer to keep this disabled to not mess up \n" + " with other routing protocols"} +#endif + + +}; + + + +void init_route( void ) { + + if( ( nl_sk = open_netlink_socket()) <= 0 ) + cleanup_all( -500067 ); + + if( ( flush_sk = open_netlink_socket()) <= 0 ) + cleanup_all( -500067 ); + + errno=0; + if ( !rt_sock && (rt_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + dbgf( DBGL_SYS, DBGT_ERR, "can't create routing socket %s:", strerror(errno) ); + cleanup_all( -500021 ); + } + + register_options_array( route_options, sizeof( route_options ) ); + +} + + +void cleanup_route( void ) { + + flush_tracked_routes( TRACK_NO ); + flush_tracked_rules( TRACK_NO ); + + // if ever started succesfully in daemon mode... + if ( on_the_fly ) { + + // flush orphan routes (and do warning in case) + // must be before flushing rules, otherwise orphan routes are not found ! + flush_routes_rules(0 /* flush route */ ); + + // flush orphan rules (and do warning in case) + if ( prio_rules ) + flush_routes_rules(1 /* flush rule */); + + } + + restore_kernel_config( NULL ); + + if( nl_sk > 0 ) + close( nl_sk ); + + if( flush_sk > 0 ) + close( flush_sk ); + + if ( rt_sock ) + close( rt_sock ); + + rt_sock = 0; +} + +
Added: trunk/bmx/route.h =================================================================== --- trunk/bmx/route.h (rev 0) +++ trunk/bmx/route.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2006 BATMAN/BMX contributors: + * Thomas Lopatic, Marek Lindner, Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +#define DEV_LO "lo" +#define DEV_UNKNOWN "unknown" +#define MAX_MTU 1500 + + +extern int32_t base_port; +#define ARG_BASE_PORT "base_port" +#define DEF_BASE_PORT 4305 +#define MIN_BASE_PORT 1025 +#define MAX_BASE_PORT 60000 + + + + +/*** + * + * Things you should leave as is unless your know what you are doing ! + * + * RT_TABLE_INTERFACES routing table for announced (non-primary) interfaces IPs and other unique IP addresses + * RT_TABLE_HOSTS routing table for routes towards originators + * RT_TABLE_NETWORKS routing table for announced networks + * RT_TABLE_TUNNEL routing table for the tunnel towards the internet gateway + * RT_PRIO_DEFAULT standard priority for routing rules + * RT_PRIO_UNREACH standard priority for unreachable rules + * RT_PRIO_TUNNEL standard priority for tunnel routing rules + * + ***/ + +#define RT_TABLE_INTERFACES -1 +#define RT_TABLE_HOSTS -2 +#define RT_TABLE_NETWORKS -3 +#define RT_TABLE_TUNNEL -4 + + + +extern uint8_t if_conf_soft_changed; // temporary enabled to trigger changed interface configuration +extern uint8_t if_conf_hard_changed; // temporary enabled to trigger changed interface configuration + +extern int Mtu_min; + + +struct routes_node { + struct list_node list; + uint32_t dest; + uint16_t netmask; + uint16_t rt_table; + uint32_t metric; + int16_t rta_type; + int8_t track_t; +}; + + +struct rules_node { + struct list_node list; + uint32_t prio; + char *iif; + uint32_t network; + int16_t netmask; + int16_t rt_table; + int16_t rta_type; + int8_t track_t; +}; + + + +//track types: +enum { + TRACK_NO, + TRACK_STANDARD, //basic rules to interfaces, host, and networks routing tables + TRACK_MY_HNA, + TRACK_MY_NET, + TRACK_OTHER_HOST, + TRACK_OTHER_HNA, + TRACK_TUNNEL +}; + +void configure_route( uint32_t dest, int16_t mask, uint32_t metric, uint32_t gw, uint32_t src, int32_t ifi, char *dev, + int16_t rt_table_macro, int16_t rta_type, int8_t del, int8_t track_t ); + +/*** + * + * rule types: 0 = RTA_SRC, 1 = RTA_DST, 2 = RTA_IIF +#define RTA_SRC 0 +#define RTA_DST 1 +#define RTA_IIF 2 + * + ***/ + +// void add_del_rule( uint32_t network, uint8_t netmask, int16_t rt_macro, uint32_t prio, char *iif, int8_t rule_type, int8_t del, int8_t track_t ); + +enum { + IF_RULE_SET_TUNNEL, + IF_RULE_CLR_TUNNEL, + IF_RULE_SET_NETWORKS, + IF_RULE_CLR_NETWORKS, + IF_RULE_UPD_ALL, + IF_RULE_CHK_IPS +}; + +int update_interface_rules( uint8_t cmd ); + + +void check_kernel_config( struct dev_node *dev_node ); + +//int8_t bind_to_iface( int32_t sock, char *dev ); + +//int is_interface_up(char *dev); +void dev_deactivate ( struct dev_node *dev_node ); +void dev_check (); + +void init_route( void ); +void cleanup_route( void ); +
Added: trunk/bmx/schedule.c =================================================================== --- trunk/bmx/schedule.c (rev 0) +++ trunk/bmx/schedule.c 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,661 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <linux/if.h> /* ifr_if, ifr_tun */ +#include <linux/rtnetlink.h> + + +#include "bmx.h" +#include "msg.h" +#include "plugin.h" +#include "route.h" +#include "schedule.h" + +#define OUT_SEQNO_OFFSET 1 + + + +#ifndef NO_PARANOIA +#define DEF_SIM_PARA NO +static int32_t sim_paranoia = DEF_SIM_PARA; +#endif + + +static LIST_SIMPEL( task_list, struct task_node, list ); + +static int32_t receive_max_sock = 0; +static fd_set receive_wait_set; + +static uint16_t changed_readfds = 1; + +static int ifevent_sk = -1; + +#ifdef BMX2_TODO +static struct ext_packet my_pip_extension_packet; +#endif + +void change_selects( void ) { + changed_readfds++; +} + + +static void check_selects( void ) { + + if( changed_readfds == 0 ) + return; + + struct list_node *list_pos; + + dbgf_all( DBGT_INFO, "%d select fds changed... ", changed_readfds ); + + changed_readfds = 0; + + FD_ZERO(&receive_wait_set); + receive_max_sock = 0; + + receive_max_sock = ifevent_sk; + FD_SET(ifevent_sk, &receive_wait_set); + + if ( receive_max_sock < unix_sock ) + receive_max_sock = unix_sock; + + FD_SET(unix_sock, &receive_wait_set); + + list_for_each( list_pos, &ctrl_list ) { + + struct ctrl_node *cn = list_entry( list_pos, struct ctrl_node, list ); + + if ( cn->fd > 0 && cn->fd != STDOUT_FILENO ) { + + receive_max_sock = MAX( receive_max_sock, cn->fd); + + FD_SET( cn->fd, &receive_wait_set ); + } + } + + struct avl_node *it=NULL; + struct dev_node *bif; + while ((bif = avl_iterate_item(&dev_ip4_tree, &it))) { + + if ( bif->active && bif->linklayer != VAL_DEV_LL_LO ) { + + receive_max_sock = MAX( receive_max_sock, bif->unicast_sock ); + + FD_SET(bif->unicast_sock, &receive_wait_set); + + receive_max_sock = MAX( receive_max_sock, bif->netwbrc_sock ); + + FD_SET(bif->netwbrc_sock, &receive_wait_set); + + if (bif->fullbrc_sock > 0) { + + receive_max_sock = MAX( receive_max_sock, bif->fullbrc_sock ); + + FD_SET(bif->fullbrc_sock, &receive_wait_set); + + } + } + } + + list_for_each( list_pos, &cb_fd_list ) { + + struct cb_fd_node *cdn = list_entry( list_pos, struct cb_fd_node, list ); + + receive_max_sock = MAX( receive_max_sock, cdn->fd ); + + FD_SET( cdn->fd, &receive_wait_set ); + + } + +} + + + + + +void register_task( uint32_t timeout, void (* task) (void *), void *data ) +{ + + assertion(-500475, (remove_task(task, data) == FAILURE)); + + struct list_node *list_pos, *prev_pos = (struct list_node *)&task_list; + struct task_node *tmp_tn = NULL; + + + //TODO: allocating and freeing tn and tn->data may be much faster when done by registerig function.. + struct task_node *tn = debugMalloc( sizeof( struct task_node ), -300034 ); + memset( tn, 0, sizeof(struct task_node) ); + + tn->expire = bmx_time + timeout; + tn->task = task; + tn->data = data; + + + list_for_each( list_pos, &task_list ) { + + tmp_tn = list_entry( list_pos, struct task_node, list ); + + if ( GREAT_U32(tmp_tn->expire, tn->expire) ) { + + list_add_after(&task_list, prev_pos, &tn->list); + break; + + } + + prev_pos = &tmp_tn->list; + + } + + if ( ( tmp_tn == NULL ) || ( LSEQ_U32(tmp_tn->expire, tn->expire) )) + list_add_tail(&task_list, &tn->list); + +} + +IDM_T remove_task( void (* task) (void *), void *data ) { + + struct list_node *list_pos, *tmp_pos, *prev_pos = (struct list_node*)&task_list; + IDM_T ret = FAILURE; + + list_for_each_safe( list_pos, tmp_pos, &task_list ) { + + struct task_node *tn = list_entry( list_pos, struct task_node, list ); + + if ( tn->task == task && tn->data == data ) { + + list_del_next(&task_list, prev_pos); + + debugFree( tn, -300080 ); +#ifdef NO_PARANOIA + return SUCCESS; +#else + assertion(-500474, (ret == FAILURE)); + ret = SUCCESS; +#endif + + } else { + + prev_pos = list_pos; + } + } + + return ret; +} + + +uint32_t whats_next( void ) { + + struct list_node *list_pos, *tmp_pos, *prev_pos = (struct list_node*)&task_list; + + paranoia( -500175, sim_paranoia ); + + list_for_each_safe( list_pos, tmp_pos, &task_list ) { + + struct task_node *tn = list_entry( list_pos, struct task_node, list ); + + if ( LSEQ_U32( tn->expire, bmx_time ) ) { + + + list_del_next( &task_list, prev_pos ); + + (*(tn->task)) (tn->data); + + debugFree( tn, -300081 ); + + CHECK_INTEGRITY(); + + return 0; + + } else { + + return tn->expire - bmx_time; + } + } + + return MAX_SELECT_TIMEOUT_MS; +} + + +static int open_ifevent_netlink_sk( void ) { + + struct sockaddr_nl sa; + int32_t unix_opts; + memset (&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + sa.nl_groups |= RTMGRP_IPV4_IFADDR; + sa.nl_groups |= RTMGRP_LINK; // (this can result in select storms with buggy wlan devices + + if ( ( ifevent_sk = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) ) < 0 ) { + dbg( DBGL_SYS, DBGT_ERR, "can't create af_netlink socket for reacting on if up/down events: %s", + strerror(errno) ); + ifevent_sk = 0; + return -1; + } + + + unix_opts = fcntl( ifevent_sk, F_GETFL, 0 ); + fcntl( ifevent_sk, F_SETFL, unix_opts | O_NONBLOCK ); + + if ( ( bind( ifevent_sk, (struct sockaddr*)&sa, sizeof(sa) ) ) < 0 ) { + dbg( DBGL_SYS, DBGT_ERR, "can't bind af_netlink socket for reacting on if up/down events: %s", + strerror(errno) ); + ifevent_sk = 0; + return -1; + } + + change_selects(); + + return ifevent_sk; +} + +static void close_ifevent_netlink_sk( void ) { + + if ( ifevent_sk > 0 ) + close( ifevent_sk ); + + ifevent_sk = 0; +} + +static void recv_ifevent_netlink_sk( void ) { + char buf[4096]; //test this with a very small value !! + + struct sockaddr_nl sa; + + struct iovec iov; + + memset(&iov, 0, sizeof (struct iovec)); + + iov.iov_base = buf; + iov.iov_len = sizeof (buf); + + struct msghdr msg; // = {(void *) & sa, sizeof (sa), &iov, 1, NULL, 0, 0}; + memset( &msg, 0, sizeof( struct msghdr)); + msg.msg_name = (void *)&sa; + msg.msg_namelen = sizeof(sa); /* Length of address data. */ + msg.msg_iov = &iov; /* Vector of data to send/receive into. */ + msg.msg_iovlen = 1; /* Number of elements in the vector. */ + + while( recvmsg (ifevent_sk, &msg, 0) > 0 ); + + //so fare I just want to consume the pending message... +} + + + + + + + + + +void wait4Event( uint32_t timeout ) { + + static struct packet_buff pb; + + uint32_t last_get_time_result = 0; + + static uint32_t addr_len = sizeof(struct sockaddr_in); + + uint32_t return_time = bmx_time + timeout; + struct timeval tv; + struct list_node *list_pos; + int selected; + fd_set tmp_wait_set; + + +loop4Event: + + while ( GREAT_U32(return_time, bmx_time) ) { + + check_selects(); + + memcpy( &tmp_wait_set, &receive_wait_set, sizeof(fd_set) ); + + tv.tv_sec = (return_time - bmx_time) / 1000; + tv.tv_usec = ( (return_time - bmx_time) % 1000 ) * 1000; + + selected = select( receive_max_sock + 1, &tmp_wait_set, NULL, NULL, &tv ); + + upd_time( &(pb.tv_stamp) ); + + //omit debugging here since event could be a closed -d4 ctrl socket + //which should be removed before debugging + //dbgf_all( DBGT_INFO, "timeout %d", timeout ); + + if ( bmx_time < last_get_time_result ) { + + last_get_time_result = bmx_time; + dbg( DBGL_SYS, DBGT_WARN, "detected Timeoverlap..." ); + + goto wait4Event_end; + } + + last_get_time_result = bmx_time; + + if ( selected < 0 ) { + + dbg( DBGL_SYS, DBGT_WARN, //happens when receiving SIGHUP + "can't select! Waiting a moment! errno: %s", strerror(errno) ); + + wait_sec_msec( 0, 1 ); + upd_time( NULL ); + + goto wait4Event_end; + + } + + if ( selected == 0 ) { + + //Often select returns just a few milliseconds before being scheduled + if ( LESS_U32( return_time, (bmx_time + 10) ) ) { + + //cheating time :-) + bmx_time = return_time; + + goto wait4Event_end; + } + + //if ( LESS_U32( return_time, bmx_time ) ) + dbg_mute( 50, DBGL_CHANGES, DBGT_WARN, + "select() returned %d without reason!! return_time %d, curr_time %d", + selected, return_time, bmx_time ); + + goto loop4Event; + } + + // check for changed interface status... + if ( FD_ISSET( ifevent_sk, &tmp_wait_set ) ) { + + dbg_mute( 40, DBGL_CHANGES, DBGT_INFO, + "select() indicated changed interface status! Going to check interfaces!" ); + + recv_ifevent_netlink_sk( ); + + //do NOT delay checking of interfaces to not miss ifdown/up of interfaces !! + dev_check(); + + goto wait4Event_end; + } + + + // check for received packets... + struct avl_node *it = NULL; + while ((pb.iif = avl_iterate_item(&dev_ip4_tree, &it))) { + + if ( pb.iif->linklayer == VAL_DEV_LL_LO ) + continue; + + if ( FD_ISSET( pb.iif->netwbrc_sock, &tmp_wait_set ) ) { + + pb.unicast = NO; + + errno=0; + pb.total_length = recvfrom( pb.iif->netwbrc_sock, pb.packet_in, + sizeof(pb.packet_in) - 1, 0, + (struct sockaddr *)&pb.addr, &addr_len ); + + if ( pb.total_length < 0 && ( errno == EWOULDBLOCK || errno == EAGAIN ) ) { + + dbgf(DBGL_SYS, DBGT_WARN, + "sock returned %d errno %d: %s", + pb.total_length, errno, strerror(errno) ); + + continue; + } + + ioctl(pb.iif->netwbrc_sock, SIOCGSTAMP, &(pb.tv_stamp)) ; + + rx_packet( &pb ); + + if ( --selected == 0 ) + goto loop4Event; + + } + + if ( FD_ISSET( pb.iif->fullbrc_sock, &tmp_wait_set ) ) { + + pb.unicast = NO; + + errno=0; + pb.total_length = recvfrom( pb.iif->fullbrc_sock, pb.packet_in, + sizeof(pb.packet_in) - 1, 0, + (struct sockaddr *)&pb.addr, &addr_len ); + + if ( pb.total_length < 0 && ( errno == EWOULDBLOCK || errno == EAGAIN ) ) { + + dbgf(DBGL_SYS, DBGT_WARN, + "sock returned %d errno %d: %s", + pb.total_length, errno, strerror(errno) ); + + continue; + } + + ioctl(pb.iif->fullbrc_sock, SIOCGSTAMP, &(pb.tv_stamp)) ; + + rx_packet( &pb ); + + if ( --selected == 0 ) + goto loop4Event; + + } + + + if ( FD_ISSET( pb.iif->unicast_sock, &tmp_wait_set ) ) { + + pb.unicast = YES; + + struct msghdr msghdr; + struct iovec iovec; + char buf[4096]; + struct cmsghdr *cp; + struct timeval *tv_stamp = NULL; + + iovec.iov_base = pb.packet_in; + iovec.iov_len = sizeof(pb.packet_in) - 1; + + msghdr.msg_name = (struct sockaddr *)&pb.addr; + msghdr.msg_namelen = addr_len; + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + msghdr.msg_control = buf; + msghdr.msg_controllen = sizeof( buf ); + msghdr.msg_flags = 0; + + errno=0; + + pb.total_length = recvmsg( pb.iif->unicast_sock, &msghdr, MSG_DONTWAIT ); + + if ( pb.total_length < 0 && ( errno == EWOULDBLOCK || errno == EAGAIN ) ) { + dbgf(DBGL_SYS, DBGT_WARN, + "sock returned %d errno %d: %s", + pb.total_length, errno, strerror(errno) ); + continue; + } + +#ifdef SO_TIMESTAMP + for (cp = CMSG_FIRSTHDR(&msghdr); cp; cp = CMSG_NXTHDR(&msghdr, cp)) { + + if ( cp->cmsg_type == SO_TIMESTAMP && + cp->cmsg_level == SOL_SOCKET && + cp->cmsg_len >= CMSG_LEN(sizeof(struct timeval)) ) { + + tv_stamp = (struct timeval*)CMSG_DATA(cp); + break; + } + } +#endif + if ( tv_stamp == NULL ) + ioctl( pb.iif->unicast_sock, SIOCGSTAMP, &(pb.tv_stamp) ); + else + timercpy( tv_stamp, &(pb.tv_stamp) ); + + rx_packet( &pb ); + + if ( --selected == 0) + goto loop4Event; + //goto wait4Event_end; + + } + + } + +loop4ActivePlugins: + + // check active plugins... + list_for_each( list_pos, &cb_fd_list ) { + + struct cb_fd_node *cdn = list_entry( list_pos, struct cb_fd_node, list ); + + if ( FD_ISSET( cdn->fd, &tmp_wait_set ) ) { + + FD_CLR( cdn->fd, &tmp_wait_set ); + + (*(cdn->cb_fd_handler)) (cdn->fd); + + // list might have changed, due to unregistered handlers, reiterate NOW + //TBD: check if fd was really consumed ! + if ( --selected == 0 ) + goto loop4Event; + //goto wait4Event_end; + + goto loop4ActivePlugins; + } + + } + + + // check for new control clients... + if ( FD_ISSET( unix_sock, &tmp_wait_set ) ) { + + dbgf_all( DBGT_INFO, "new control client..." ); + + accept_ctrl_node(); + + if ( --selected == 0 ) + goto loop4Event; + + //goto wait4Event_end; + } + + +loop4ActiveClients: + // check for all connected control clients... + list_for_each( list_pos, &ctrl_list ) { + + struct ctrl_node *client = list_entry( list_pos, struct ctrl_node, list ); + + if ( FD_ISSET( client->fd, &tmp_wait_set ) ) { + + FD_CLR( client->fd, &tmp_wait_set ); + + //omit debugging here since event could be a closed -d4 ctrl socket + //which should be removed before debugging + //dbgf_all( DBGT_INFO, "got msg from control client"); + + handle_ctrl_node( client ); + + --selected; + + // return straight because client might be removed and list might have changed. + goto loop4ActiveClients; + } + + } + + + + if ( selected ) + dbg( DBGL_CHANGES, DBGT_WARN, + "select() returned with %d unhandled event(s)!! return_time %d, curr_time %d", + selected, return_time, bmx_time ); + + break; + } + +wait4Event_end: + + dbgf_all( DBGT_INFO, "end of function"); + + return; +} + + + + + +static struct opt_type schedule_options[]= +{ +// ord parent long_name shrt Attributes *ival min max default *func,*syntax,*help + + {ODI,0,0, 0, 5,0,0,0,0,0, 0, 0, 0, 0, 0, + 0, "\nScheduling options:"} + +#ifndef LESS_OPTIONS +#ifndef NO_PARANOIA + ,{ODI,0,"simulate_cleanup", 0, 5,A_PS1,A_ADM,A_DYI,A_ARG,A_ANY, &sim_paranoia, NO, YES, DEF_SIM_PARA, 0, + ARG_VALUE_FORM, "simulate paranoia and cleanup_all for testing"} +#endif +#endif + +}; + + + +void init_schedule( void ) { + +#ifdef BMX2_TODO + memset( &my_pip_extension_packet, 0, sizeof(struct ext_packet) ); + my_pip_extension_packet.EXT_FIELD_MSG = YES; + my_pip_extension_packet.EXT_FIELD_TYPE = EXT_TYPE_64B_PIP; +#endif + if ( open_ifevent_netlink_sk() < 0 ) + cleanup_all( -500150 ); + + register_options_array( schedule_options, sizeof( schedule_options ) ); +} + + + +void cleanup_schedule( void ) { + + + struct task_node *tn; + + while ( (tn = list_rem_head( &task_list ))) + debugFree(tn, -300082); + +/* + struct list_node *list_pos_tmp, *list_pos; + list_for_each_safe( list_pos, list_pos_tmp, &task_list ) { + + struct task_node *tn = list_entry( list_pos, struct task_node, list ); + + list_del_next( &task_list, (struct list_node *)&task_list ); + + debugFree(tn, -300082); + } +*/ + + close_ifevent_netlink_sk(); +}
Added: trunk/bmx/schedule.h =================================================================== --- trunk/bmx/schedule.h (rev 0) +++ trunk/bmx/schedule.h 2010-06-03 20:07:47 UTC (rev 1683) @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2010 BMX contributors: + * Axel Neumann + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + + + +void init_schedule( void ); +void change_selects( void ); +void cleanup_schedule( void ); +void register_task( uint32_t timeout, void (* task) (void *), void *data ); +IDM_T remove_task(void (* task) (void *), void *data); +uint32_t whats_next( void ); +void wait4Event( uint32_t timeout ); +