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;
+}