Initial commit

Change-Id: I6a4444e3c193dae437cd7929f4c39aba7b749efa
diff --git a/extensions/test_netemul/CMakeLists.txt b/extensions/test_netemul/CMakeLists.txt
new file mode 100644
index 0000000..507e96a
--- /dev/null
+++ b/extensions/test_netemul/CMakeLists.txt
@@ -0,0 +1,43 @@
+# The test_netemul extension
+PROJECT("Simple Diameter network emulator proxy extension (latency, PDV, duplicates)" C)
+
+# Parser files
+BISON_FILE(test_netemul.y)
+FLEX_FILE(test_netemul.l)
+SET_SOURCE_FILES_PROPERTIES(lex.test_netemul.c test_netemul.tab.c PROPERTIES COMPILE_FLAGS "-I ${CMAKE_CURRENT_SOURCE_DIR}")
+
+# List of source files
+SET( TNEMUL_SRC
+	test_netemul.c
+	test_netemul.h
+	lex.test_netemul.c
+	test_netemul.tab.c
+	test_netemul.tab.h
+	tne_process.c
+)
+
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
+
+# Compile these files as a freeDiameter extension
+FD_ADD_EXTENSION(test_netemul ${TNEMUL_SRC})
+
+
+# math functions
+CHECK_FUNCTION_EXISTS (sqrt HAVE_SQRT)
+IF (HAVE_SQRT)
+   SET(MATH_LIBS "")
+ELSE (HAVE_SQRT)
+   CHECK_LIBRARY_EXISTS (m sqrt "" HAVE_LIBM)
+   IF (HAVE_LIBM)
+     SET(MATH_LIBS "-lm")
+   ENDIF (HAVE_LIBM)
+ENDIF (HAVE_SQRT)
+
+TARGET_LINK_LIBRARIES(test_netemul ${MATH_LIBS})
+
+####
+## INSTALL section ##
+
+INSTALL(TARGETS test_netemul
+	LIBRARY DESTINATION ${INSTALL_EXTENSIONS_SUFFIX}
+	COMPONENT freeDiameter-debug-tools)
diff --git a/extensions/test_netemul/test_netemul.c b/extensions/test_netemul/test_netemul.c
new file mode 100644
index 0000000..66fe610
--- /dev/null
+++ b/extensions/test_netemul/test_netemul.c
@@ -0,0 +1,106 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2011, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* 
+ * This extension provides a simple Diameter network emulator mechanism.
+ * It allows the introduction of delays and duplicates of Diameter messages.
+ * See test_netemul.conf.sample file for the format of the configuration file.
+ */
+
+#include "test_netemul.h"
+
+/* The configuration structure */
+struct tne_conf tne_conf;
+
+
+
+/* Proxying callback */
+static int tne_fwd_cb(void * cbdata, struct msg ** msg)
+{
+	struct msg * m;
+	
+	TRACE_ENTRY("%p %p", cbdata, msg);
+	
+	m = *msg;
+	*msg = NULL;
+	
+	TRACE_DEBUG(FULL, "[tne] Processing message %p", m);
+	CHECK_FCT( tne_process_message(m) );
+	
+	return 0;
+}
+
+
+static struct fd_rt_fwd_hdl * fwd_hdl = NULL;
+
+/* Register the callbacks to the daemon */
+static int tne_main(char * conffile)
+{
+	TRACE_ENTRY("%p", conffile);
+	
+	/* Initialize the configuration */
+	memset(&tne_conf, 0, sizeof(tne_conf));
+	tne_conf.lat_avg = 500;
+	tne_conf.lat_dev = 20;
+	tne_conf.dupl_proba = 1E-2;
+	
+	/* Parse the configuration file */
+	CHECK_FCT( tne_conf_handle(conffile) );
+	
+	/* Initialize the process module */
+	CHECK_FCT( tne_process_init() );
+	
+	/* Now, register the proxying callback */
+	CHECK_FCT( fd_rt_fwd_register ( tne_fwd_cb, NULL, RT_FWD_REQ, &fwd_hdl ) );
+
+	return 0;
+}
+
+/* Cleanup the callbacks */
+void fd_ext_fini(void)
+{
+	TRACE_ENTRY();
+	
+	/* Unregister the module */
+	CHECK_FCT_DO( fd_rt_fwd_unregister ( fwd_hdl, NULL ), /* continue */ );
+	
+	/* Destroy the process thread */
+	CHECK_FCT_DO( tne_process_fini (  ), /* continue */ );
+	
+	return ;
+}
+
+/* Define the entry point function */
+EXTENSION_ENTRY("test_netemul", tne_main);
diff --git a/extensions/test_netemul/test_netemul.h b/extensions/test_netemul/test_netemul.h
new file mode 100644
index 0000000..0d9d40b
--- /dev/null
+++ b/extensions/test_netemul/test_netemul.h
@@ -0,0 +1,56 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2011, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/*
+ *  See the test_netemul.conf.sample file for the format of the configuration file.
+ */
+ 
+/* FreeDiameter's common include file */
+#include <freeDiameter/extension.h>
+
+/* Parse the configuration file */
+int tne_conf_handle(char * conffile);
+
+/* The configuration structure */
+extern struct tne_conf {
+	unsigned long  lat_avg; /* in milliseconds */
+	unsigned int   lat_dev; /* between 0 and 100 */
+	float          dupl_proba; 
+} tne_conf;
+
+/* Apply the configured process to the message, then send it. */
+int tne_process_message(struct msg * msg);
+int tne_process_init();
+int tne_process_fini();
diff --git a/extensions/test_netemul/test_netemul.l b/extensions/test_netemul/test_netemul.l
new file mode 100644
index 0000000..430e785
--- /dev/null
+++ b/extensions/test_netemul/test_netemul.l
@@ -0,0 +1,119 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Tokenizer */
+
+%{
+#include "test_netemul.h"
+/* Include yacc tokens definitions */
+#include "test_netemul.tab.h"
+
+/* Update the column information */
+#define YY_USER_ACTION { 						\
+	yylloc->first_column = yylloc->last_column + 1; 		\
+	yylloc->last_column = yylloc->first_column + yyleng - 1;	\
+}
+
+/* Avoid warning with newer flex */
+#define YY_NO_INPUT
+
+%}
+
+%option bison-bridge bison-locations
+%option noyywrap
+%option nounput
+
+%%
+
+	/* Update the line count */
+\n			{
+				yylloc->first_line++; 
+				yylloc->last_line++; 
+				yylloc->last_column=0; 
+			}
+	 
+	/* Eat all spaces but not new lines */
+([[:space:]]{-}[\n])+	;
+	/* Eat all comments */
+#.*$			;
+
+
+	/* Recognize floats */
+[[:digit:]]+"."[[:digit:]]*	{
+				/* Convert this to an integer value */
+				int ret=0;
+				ret = sscanf(yytext, "%f", &yylval->decimal);
+				if (ret != 1) {
+					/* No matching: an error occurred */
+					fd_log_debug("Unable to convert the value '%s' to a valid float: %s", yytext, strerror(errno));
+					return LEX_ERROR; /* trig an error in yacc parser */
+					/* Maybe we could REJECT instead of failing here? */
+				}
+				return FLOAT;
+			}
+			
+	/* Recognize simple integers */
+[[:digit:]]+		{
+				/* Convert this to an integer value */
+				int ret=0;
+				ret = sscanf(yytext, "%lu", &yylval->ulong);
+				if (ret != 1) {
+					/* No matching: an error occurred */
+					fd_log_debug("Unable to convert the value '%s' to a valid long: %s", yytext, strerror(errno));
+					return LEX_ERROR; /* trig an error in yacc parser */
+					/* Maybe we could REJECT instead of failing here? */
+				}
+				return ULONG;
+			}
+			
+	
+	
+	/* The key words */	
+(?i:"latency_average")	 	{	return LATENCY_AVERAGE;		}
+(?i:"latency_deviation")	{	return LATENCY_DEVIATION;	}
+(?i:"dupl_proba")	 	{	return DUPL_PROBA;		}
+(?i:"ms")	 		{	return UNIT_MSEC;		}
+(?i:"s")	 		{	return UNIT_SEC;		}
+			
+	/* Valid single characters for yyparse */
+[=;%/]			{ return yytext[0]; }
+
+	/* Unrecognized sequence, if it did not match any previous pattern */
+[^[:space:][:digit:]=;%/\n]+	{ 
+				fd_log_debug("Unrecognized text on line %d col %d: '%s'.", yylloc->first_line, yylloc->first_column, yytext);
+			 	return LEX_ERROR; 
+			}
+
+%%
diff --git a/extensions/test_netemul/test_netemul.y b/extensions/test_netemul/test_netemul.y
new file mode 100644
index 0000000..222cf2e
--- /dev/null
+++ b/extensions/test_netemul/test_netemul.y
@@ -0,0 +1,171 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+/* Yacc extension's configuration parser.
+ */
+
+/* For development only : */
+%debug 
+%error-verbose
+
+/* The parser receives the configuration file filename as parameter */
+%parse-param {char * conffile}
+
+/* Keep track of location */
+%locations 
+%pure-parser
+
+%{
+#include "test_netemul.h"
+#include "test_netemul.tab.h"	/* YYLTYPE */
+
+/* Forward declaration */
+int yyparse(char * conffile);
+
+/* Parse the configuration file */
+int tne_conf_handle(char * conffile)
+{
+	extern FILE * test_netemulin;
+	int ret;
+	
+	TRACE_ENTRY("%p", conffile);
+	
+	TRACE_DEBUG (FULL, "Parsing configuration file: %s...", conffile);
+	
+	test_netemulin = fopen(conffile, "r");
+	if (test_netemulin == NULL) {
+		ret = errno;
+		fd_log_debug("Unable to open extension configuration file %s for reading: %s", conffile, strerror(ret));
+		TRACE_DEBUG (INFO, "Error occurred, message logged -- configuration file.");
+		return ret;
+	}
+
+	ret = yyparse(conffile);
+
+	fclose(test_netemulin);
+
+	if (ret != 0) {
+		TRACE_DEBUG (INFO, "Unable to parse the configuration file.");
+		return EINVAL;
+	} else {
+		TRACE_DEBUG(FULL, "[test_netemul]  latency: %lu ms (var:%u%%)  duplicates: %G probability.", tne_conf.lat_avg, tne_conf.lat_dev, tne_conf.dupl_proba);
+	}
+	
+	return 0;
+}
+
+/* The Lex parser prototype */
+int test_netemullex(YYSTYPE *lvalp, YYLTYPE *llocp);
+
+/* Function to report the errors */
+void yyerror (YYLTYPE *ploc, char * conffile, char const *s)
+{
+	TRACE_DEBUG(INFO, "Error in configuration parsing");
+	
+	if (ploc->first_line != ploc->last_line)
+		fd_log_debug("%s:%d.%d-%d.%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_line, ploc->last_column, s);
+	else if (ploc->first_column != ploc->last_column)
+		fd_log_debug("%s:%d.%d-%d : %s", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s);
+	else
+		fd_log_debug("%s:%d.%d : %s", conffile, ploc->first_line, ploc->first_column, s);
+}
+
+%}
+
+/* Values returned by lex for token */
+%union {
+	unsigned long	ulong;
+	float	decimal;
+}
+
+/* In case of error in the lexical analysis */
+%token 		LEX_ERROR
+
+%token <ulong>   ULONG
+%token <decimal> FLOAT
+
+/* Tokens */
+%token 		LATENCY_AVERAGE
+%token 		LATENCY_DEVIATION
+%token 		DUPL_PROBA
+%token 		UNIT_SEC
+%token 		UNIT_MSEC
+
+
+/* -------------------------------------- */
+%%
+
+	/* The grammar definition */
+conffile:		/* empty */
+			| conffile latency_average
+			| conffile latency_deviation
+			| conffile dupl_proba
+			;
+			
+	/* a server entry */
+latency_average:	LATENCY_AVERAGE '=' ULONG UNIT_SEC ';'
+			{
+				tne_conf.lat_avg = $3 * 1000;
+			}
+			|
+			LATENCY_AVERAGE '=' ULONG UNIT_MSEC ';'
+			{
+				tne_conf.lat_avg = $3;
+			}
+			;
+
+latency_deviation:	LATENCY_DEVIATION '=' ULONG '%' ';'
+			{
+				tne_conf.lat_dev = (int)$3;
+				if ((tne_conf.lat_dev < 0) || (tne_conf.lat_dev > 100)) {
+					yyerror (&yylloc, conffile, "Latency_Deviation must be comprised between 0 and 100.");
+					YYERROR;
+				}
+			}
+			;
+
+dupl_proba:		DUPL_PROBA '=' ULONG ';'
+			{
+				tne_conf.dupl_proba = (float) $3;
+			}
+			| DUPL_PROBA '=' FLOAT ';'
+			{
+				tne_conf.dupl_proba = $3;
+			}
+			| DUPL_PROBA '=' ULONG '/' ULONG ';'
+			{
+				tne_conf.dupl_proba = ((float)$3) / ((float)$5) ;
+			}
+			;
diff --git a/extensions/test_netemul/tne_process.c b/extensions/test_netemul/tne_process.c
new file mode 100644
index 0000000..1bff64c
--- /dev/null
+++ b/extensions/test_netemul/tne_process.c
@@ -0,0 +1,333 @@
+/*********************************************************************************************************
+* Software License Agreement (BSD License)                                                               *
+* Author: Sebastien Decugis <sdecugis@freediameter.net>							 *
+*													 *
+* Copyright (c) 2013, WIDE Project and NICT								 *
+* All rights reserved.											 *
+* 													 *
+* Redistribution and use of this software in source and binary forms, with or without modification, are  *
+* permitted provided that the following conditions are met:						 *
+* 													 *
+* * Redistributions of source code must retain the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer.										 *
+*    													 *
+* * Redistributions in binary form must reproduce the above 						 *
+*   copyright notice, this list of conditions and the 							 *
+*   following disclaimer in the documentation and/or other						 *
+*   materials provided with the distribution.								 *
+* 													 *
+* * Neither the name of the WIDE Project or NICT nor the 						 *
+*   names of its contributors may be used to endorse or 						 *
+*   promote products derived from this software without 						 *
+*   specific prior written permission of WIDE Project and 						 *
+*   NICT.												 *
+* 													 *
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
+* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
+* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
+* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
+* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
+*********************************************************************************************************/
+
+#include "test_netemul.h"
+#include <math.h>
+
+/* This file implements the real processing of the message.
+ The entry point is tne_process_message().
+ 
+ First, the duplicate filter is applied: with the configured
+ probability, a copy of the message is created. Then, with 
+ the tenth probability, a second copy is created, and so on,
+ until the random value tells not to create a copy.
+ 
+ The original message + all copies are stored in a list, for next step.
+ 
+ Second step is the latency filter. For each message in the list, a
+ latency value is randomly generated (with a lognormal shape of the 
+ random distribution) and stored in the list.
+ 
+ Finally, when the latency time is over, the message is sent.
+ */
+
+static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  cnd = PTHREAD_COND_INITIALIZER;
+static pthread_t       thr = (pthread_t)NULL;
+
+/* The lists below are all protected by the same mutex mtx */
+static struct fd_list input = FD_LIST_INITIALIZER(input); /* messages received from network */
+static struct fd_list forlat = FD_LIST_INITIALIZER(forlat); /* messages after duplicate filter */
+static struct fd_list waitlist = FD_LIST_INITIALIZER(waitlist); /* messages waiting for sending */
+
+struct process_item {
+	struct fd_list chain; /* link into one of the lists. "o" points to the message. */
+	struct timespec ts; /* when the message must be sent */
+};
+
+/******************************************************************/
+/* helper functions */
+
+/* Process all pi in input list and queue in forlat, duplicating when needed */
+static int do_duplicates()
+{
+	TRACE_ENTRY("");
+	
+	while (!FD_IS_LIST_EMPTY(&input)) {
+		struct msg * m;
+		struct process_item * pi = (struct process_item *)(input.next);
+		
+		/* Take out this pi from input */
+		fd_list_unlink(&pi->chain);
+		
+		/* store it in forlat */
+		fd_list_insert_before(&forlat, &pi->chain);
+		
+		/* Duplicate eventually, unless deactivated */
+		if (tne_conf.dupl_proba != 0.0) {
+			DiamId_t src;
+			size_t srclen;
+			/* Pick a random value in [0, 1] */
+			double my_rand = drand48();
+			m = pi->chain.o;
+			CHECK_FCT( fd_msg_source_get(m, &src, &srclen) );
+			
+			while (my_rand < (double) tne_conf.dupl_proba) {
+				/* create the duplicate */
+				struct process_item * npi;
+				struct msg * nm;
+				struct msg_hdr * nh;
+				unsigned char * buf;
+				size_t len;
+				
+				/* Duplicate the message */
+				CHECK_FCT( fd_msg_bufferize(m, &buf, &len) );
+				CHECK_FCT( fd_msg_parse_buffer(&buf, len, &nm) );
+				CHECK_FCT( fd_msg_source_set(nm, src, srclen) );
+				CHECK_FCT( fd_msg_hdr(nm, &nh) );
+				nh->msg_flags |= CMD_FLAG_RETRANSMIT; /* Add the 'T' flag */
+				TRACE_DEBUG(FULL, "[tne] Duplicated message %p as %p", m, nm);
+				
+				/* Duplicate the pi */
+				CHECK_MALLOC( npi = malloc(sizeof(struct process_item)) );
+				memset(npi, 0, sizeof(struct process_item));
+				fd_list_init(&npi->chain, nm);
+				memcpy(&npi->ts, &pi->ts, sizeof(struct timespec));
+				
+				/* Enqueue the candidate in forlat */
+				fd_list_insert_before(&forlat, &npi->chain);
+				
+				/* loop for another duplicate */
+				if (!my_rand)
+					break; /* otherwise, infinite loop */
+				my_rand *= 10.0;
+			}
+		}
+	}
+	
+	/* Done */
+	return 0;
+}
+
+/* Generate a random value with a normal distribution, mean 0, variance 1 */
+/* Using Box-Muller algo from Numerical Recipes in C++, 2nd Ed. */
+static double get_rand_norm()
+{
+	double ru1, ru2; /* two random uniform values in -1..1 */
+	double rsq;      /* ru1^2 + ru2^2, to ensure we are in the circle */
+	
+	/* Get our appropriate 2 random uniform values */
+	do {
+		ru1 = 2.0 * drand48() - 1.0;
+		ru2 = 2.0 * drand48() - 1.0;
+		rsq = ru1 * ru1 + ru2 * ru2;
+	} while ((rsq >= 1.0) || (rsq == 0.0));
+	
+	/* Do the Box-Muller transform -- we don't use the 2nd value generated */
+	return ru1 * sqrt( -2.0 * log(rsq) / rsq );
+}
+
+/* Return the latency to add, in ms. */
+static __inline__ unsigned long get_latency()
+{
+	unsigned long lat = tne_conf.lat_avg;
+
+	if (tne_conf.lat_dev) {
+		/* We randomize the value to add */
+		double rn;
+
+		rn = get_rand_norm(); /* this is normal random value with mean = 0 and variance = 1 */
+		rn = rn * ((double)tne_conf.lat_dev) / 100.0; /* now the variance is lat_dev */
+		rn = exp(rn); /* and now, we have a lognormal random value, with geometric mean = 1 */
+
+		lat = (unsigned long)(rn * (double)lat); /* Apply to our mean latency */
+	}
+	
+	return lat;
+}
+			
+/* Process all pi in forlat and add a random latency, then requeue in order into waitlist */
+static int do_latency()
+{
+	TRACE_ENTRY("");
+	
+	while (!FD_IS_LIST_EMPTY(&forlat)) {
+		struct process_item * pi = (struct process_item *)(forlat.next);
+		struct fd_list * li;
+		
+		/* Take out this pi from forlat */
+		fd_list_unlink(&pi->chain);
+		
+		/* If there is a latency to add */
+		if (tne_conf.lat_avg) {
+			unsigned long l = get_latency();
+			TRACE_DEBUG(FULL, "[tne] Set %lu ms latency for %p", l, pi->chain.o);
+			pi->ts.tv_sec += l / 1000;
+			l %= 1000;
+			pi->ts.tv_nsec += l * 1000000;
+			if (pi->ts.tv_nsec >= 1000000000) {
+				pi->ts.tv_sec += 1;
+				pi->ts.tv_nsec -= 1000000000;
+			}
+		}
+
+		for (li = waitlist.prev; li != &waitlist; li=li->prev) {
+			struct process_item * p = (struct process_item *)li;
+			if (TS_IS_INFERIOR( &p->ts, &pi->ts ))
+				break; /* we must insert after this one */
+		}
+		
+		/* store it */
+		fd_list_insert_after(li, &pi->chain);
+	}
+	
+	/* Done */
+	return 0;
+}
+
+/* Send all messages in waitlist that have passed their latency period */
+static int send_all_ready()
+{
+	struct timespec now;
+	
+	TRACE_ENTRY("");
+	
+	CHECK_SYS( clock_gettime(CLOCK_REALTIME, &now) );
+	
+	while (!FD_IS_LIST_EMPTY(&waitlist)) {
+		struct msg * m;
+		struct process_item * pi = (struct process_item *)(waitlist.next);
+		
+		if (!TS_IS_INFERIOR( &pi->ts, &now))
+			break; /* We sent already all we could */
+		
+		/* Take out this pi and send the message */
+		fd_list_unlink(&pi->chain);
+		m = pi->chain.o;
+		free(pi);
+		
+		TRACE_DEBUG(FULL, "[tne] Sending now %p", m);
+		CHECK_FCT( fd_msg_send(&m, NULL, NULL) );
+	}
+	
+	return 0;
+
+}
+
+
+/******************************************************************/
+/* the processing thread */
+static void * tne_process_th(void * arg) 
+{
+	TRACE_ENTRY("%p", arg);
+	
+	/* Name the thread */
+	fd_log_threadname ( "test_netemul/process" );
+	
+	CHECK_POSIX_DO( pthread_mutex_lock(&mtx), goto error );
+	pthread_cleanup_push( fd_cleanup_mutex, &mtx );
+	
+	/* The loop */
+	while (1) {
+		/* First, test if we are canceled */
+		pthread_testcancel();
+		
+		/* Send all messages that are ready (free resources before using new ones) */
+		CHECK_FCT_DO( send_all_ready(), break );
+		
+		/* Now process the new messages in input list for duplicate filter */
+		CHECK_FCT_DO( do_duplicates(), break );
+		
+		/* Now compute the latency for each new item */
+		CHECK_FCT_DO( do_latency(), break );
+		
+		/* Now, wait then loop */
+		if (FD_IS_LIST_EMPTY(&waitlist)) {
+			CHECK_POSIX_DO( pthread_cond_wait(&cnd, &mtx), break );
+		} else {
+			CHECK_POSIX_DO2( pthread_cond_timedwait(&cnd, &mtx, &((struct process_item *)(waitlist.next))->ts), 
+				ETIMEDOUT, /* ETIMEDOUT is a normal return value, continue */,
+					/* on other error, */ break );
+		}
+		
+		/* loop */
+	}
+
+	pthread_cleanup_pop( 0 );
+	CHECK_POSIX_DO( pthread_mutex_unlock(&mtx),  );
+error:
+	TRACE_DEBUG(INFO, "A fatal error occurred in test_netemul/process thread!");
+	ASSERT(0);
+	CHECK_FCT_DO(fd_core_shutdown(), );
+	return NULL;
+}
+
+/******************************************************************/
+/* functions visible from outside this file */
+int tne_process_init() 
+{
+	CHECK_POSIX( pthread_create(&thr, NULL, tne_process_th, NULL) );
+	
+	#if 0 /* debug */
+	int i;
+	for (i=0; i< 20; i++) {
+		printf("LAT: %lu\n", get_latency()); 
+	}
+	#endif /* 0 */
+	
+	return 0;
+}
+
+
+int tne_process_fini() 
+{
+	CHECK_FCT( fd_thr_term(&thr) );
+	return 0;
+}
+
+
+int tne_process_message(struct msg * msg) 
+{
+	struct process_item * pi;
+	
+	TRACE_ENTRY("%p", msg);
+	
+	/* Create a new pi for this message */
+	CHECK_MALLOC( pi = malloc(sizeof(struct process_item)) );
+	memset(pi, 0, sizeof(struct process_item));
+	fd_list_init(&pi->chain, msg);
+	CHECK_SYS(clock_gettime(CLOCK_REALTIME, &pi->ts));
+	
+	/* Store it in the input list */
+	CHECK_POSIX( pthread_mutex_lock(&mtx) );
+	fd_list_insert_before(&input, &pi->chain);
+	CHECK_POSIX( pthread_mutex_unlock(&mtx) );
+	
+	/* Wake up the process thread so that it processes the message */
+	CHECK_POSIX( pthread_cond_signal(&cnd) );
+	
+	/* done */
+	return 0;
+}