# HG changeset patch # User Sebastien Decugis # Date 1294392817 -32400 # Node ID 083317a4e947760836fdcff547aa7833866b367e # Parent 7e9a5e9aad64ec60e0cc080f64e0439a44aeed10 Added new test_netemul extension (need testing) diff -r 7e9a5e9aad64 -r 083317a4e947 contrib/debian/changelog --- a/contrib/debian/changelog Thu Jan 06 10:39:21 2011 +0900 +++ b/contrib/debian/changelog Fri Jan 07 18:33:37 2011 +0900 @@ -2,6 +2,7 @@ * Added new API to specify timeout on receiving answer (#10) * Bumped API version number accordingly. + * New test_netemul extension (simple network emulator proxy) -- Sebastien Decugis Tue, 04 Jan 2011 16:18:53 +0900 diff -r 7e9a5e9aad64 -r 083317a4e947 contrib/debian/freediameter-debug-tools.examples --- a/contrib/debian/freediameter-debug-tools.examples Thu Jan 06 10:39:21 2011 +0900 +++ b/contrib/debian/freediameter-debug-tools.examples Fri Jan 07 18:33:37 2011 +0900 @@ -1,2 +1,3 @@ doc/test_app.conf.sample +doc/test_netemul.conf.sample doc/dbg_interactive.py.sample diff -r 7e9a5e9aad64 -r 083317a4e947 contrib/debian/freediameter-debug-tools.install --- a/contrib/debian/freediameter-debug-tools.install Thu Jan 06 10:39:21 2011 +0900 +++ b/contrib/debian/freediameter-debug-tools.install Fri Jan 07 18:33:37 2011 +0900 @@ -1,4 +1,5 @@ usr/lib/freeDiameter/dbg_rt.fdx usr/lib/freeDiameter/test_app.fdx usr/lib/freeDiameter/test_sip.fdx +usr/lib/freeDiameter/test_netemul.fdx usr/lib/freeDiameter/dbg_interactive.fdx diff -r 7e9a5e9aad64 -r 083317a4e947 doc/test_netemul.conf.sample --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/test_netemul.conf.sample Fri Jan 07 18:33:37 2011 +0900 @@ -0,0 +1,59 @@ +# This file contains information for configuring the test_netemul extension. +# To find how to have freeDiameter load this extension, please refer to the freeDiameter documentation. +# +# The test_netemul extension implements a Diameter proxy that behaves like simple forwarding agent, +# with the exception that it can introduce delay in the forwarding of the messages and generate duplicates +# of messages, as can be expected from a real Diameter network. It can also generate routing errors when +# connected to more than 2 peers. + + +# LATENCY: +# Two parameters are used to control the delay introduced in the messages. +# - latency_average: +# This is the average delay introduced in the packets. +# Set to 0 to not add any latency (beyond the normal processing time). +# The value is expressed as an integer followed by a unit which can +# be 's' (seconds) or 'ms' (milliseconds). Example: +# latency_average = 700 ms; +# +# - latency_deviation: +# This parameter controls the variance in the latency. It is expressed +# as a value between 0 % and 100 %. When set to 0 %, all messages will be delayed +# by exactly latency_average. Otherwise, it represents the width of the interval +# "around" the average where "most" of the latency will be chosen (the distribution +# has a Gaussian shape). Example: +# latency_deviation = 25 %; +# +# The default values give an added latency "mostly" between 0.4 and 0.6 seconds: +# latency_average = 500 ms; +# latency_deviation = 20 % ; + + +# REORDERING: +# There is no special control over the reordering of messages. It may simply happen +# as a result of the latency. If you want to get a lot of reordering, set the +# latency_variance to a high value. + + +# DUPLICATES: +# Duplicate messages are expected in the Diameter protocol by design, as a consequence +# of the failover mechanism that provides the protocol's reliability. +# - dupl_proba: +# This value gives the probability of producing a duplicate of a forwarded message. +# The value is comprized between 0 (no duplicates) and 1 (duplicate all messages). +# Duplicates are created for requests, but may result in duplicate answers +# received by your Diameter client(s), depending on your server(s)'s behavior. +# In the case of freeDiameter client, the duplicate answer is automatically filtered out +# because the hop-by-hop id has already been used. +# Note that each duplicate copy is an independent message, +# which receives a different latency, and might be routed to a different server if you +# use for example load-balancing. +# The parameter can take several forms: +# dupl_proba = 0 ; +# Disables the generation of duplicate messages completely. +# dupl_proba = 1 / 10000 ; +# dupl_proba = 0.0001 ; +# Around 1 messages over ten thousands will be duplicated. +# +# Default value: +# dupl_proba = 1 / 100 ; diff -r 7e9a5e9aad64 -r 083317a4e947 extensions/CMakeLists.txt --- a/extensions/CMakeLists.txt Thu Jan 06 10:39:21 2011 +0900 +++ b/extensions/CMakeLists.txt Fri Jan 07 18:33:37 2011 +0900 @@ -86,6 +86,8 @@ FD_EXTENSION_SUBDIR(test_app "Testing application to send dummy message to another peer, like a Diameter 'ping'" OFF) FD_EXTENSION_SUBDIR(test_sip "Testing application to simulate Diameter-SIP client (RFC4740)" OFF) FD_EXTENSION_SUBDIR(dbg_interactive "Python-interpreter based module" OFF) +FD_EXTENSION_SUBDIR(test_netemul "Simple Diameter network emulator proxy extension (latency, PDV, duplicates)" OFF) + # The following extension have very little use except for specific tests, so we disable them except in Debug configurations. diff -r 7e9a5e9aad64 -r 083317a4e947 extensions/test_netemul/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/test_netemul/CMakeLists.txt Fri Jan 07 18:33:37 2011 +0900 @@ -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 -r 7e9a5e9aad64 -r 083317a4e947 extensions/test_netemul/test_netemul.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/test_netemul/test_netemul.c Fri Jan 07 18:33:37 2011 +0900 @@ -0,0 +1,105 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* 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; + + 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 -r 7e9a5e9aad64 -r 083317a4e947 extensions/test_netemul/test_netemul.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/test_netemul/test_netemul.h Fri Jan 07 18:33:37 2011 +0900 @@ -0,0 +1,56 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* 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 + +/* 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 -r 7e9a5e9aad64 -r 083317a4e947 extensions/test_netemul/test_netemul.l --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/test_netemul/test_netemul.l Fri Jan 07 18:33:37 2011 +0900 @@ -0,0 +1,119 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* 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. * +*********************************************************************************************************/ + +/* 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\n", 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\n", 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'.\n", yylloc->first_line, yylloc->first_column, yytext); + return LEX_ERROR; + } + +%% diff -r 7e9a5e9aad64 -r 083317a4e947 extensions/test_netemul/test_netemul.y --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/test_netemul/test_netemul.y Fri Jan 07 18:33:37 2011 +0900 @@ -0,0 +1,171 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* 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. * +*********************************************************************************************************/ + +/* 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\n", 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]\n latency: %lu ms (var:%u%%)\n 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\n", 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\n", conffile, ploc->first_line, ploc->first_column, ploc->last_column, s); + else + fd_log_debug("%s:%d.%d : %s\n", 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 +%token 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 -r 7e9a5e9aad64 -r 083317a4e947 extensions/test_netemul/tne_process.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extensions/test_netemul/tne_process.c Fri Jan 07 18:33:37 2011 +0900 @@ -0,0 +1,330 @@ +/********************************************************************************************************* +* Software License Agreement (BSD License) * +* Author: Sebastien Decugis * +* * +* 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. * +*********************************************************************************************************/ + +#include "test_netemul.h" +#include + +/* 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 bellow 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) { + char * src; + /* Pick a random value in [0, 1] */ + double my_rand = drand48(); + m = pi->chain.o; + CHECK_FCT( fd_msg_source_get(m, &src) ); + + while (my_rand < (double) tne_conf.dupl_proba) { + /* create the duplicate */ + struct process_item * npi; + struct msg * nm; + 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, 0, NULL) ); + + /* 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(); + 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); + + 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 ); + + /* The loop */ + while (1) { + /* First, test if we are canceled */ + CHECK_POSIX_DO( pthread_mutex_unlock(&mtx), goto error ); + pthread_testcancel(); + CHECK_POSIX_DO( pthread_mutex_lock(&mtx), goto error ); + + pthread_cleanup_push( fd_cleanup_mutex, &mtx ); + + /* Send all messages that are ready (free resources before using new ones) */ + CHECK_FCT_DO( send_all_ready(), goto error_ul ); + + /* Now process the new messages in input list for duplicate filter */ + CHECK_FCT_DO( do_duplicates(), goto error_ul ); + + /* Now compute the latency for each new item */ + CHECK_FCT_DO( do_latency(), goto error_ul ); + + /* Now, wait then loop */ + if (FD_IS_LIST_EMPTY(&waitlist)) { + CHECK_POSIX_DO( pthread_cond_wait(&cnd, &mtx), goto error_ul ); + } 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, */ goto error_ul ); + } + + pthread_cleanup_pop( 0 ); + /* loop */ + } + +error_ul: + 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_event_send(fd_g_config->cnf_main_ev, FDEV_TERMINATE, 0, NULL), ); + 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 = 30; + 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; +}