From fcca284db3b8392b16cc2e8d053f57ac7eab50a5 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Wed, 24 Dec 2014 06:22:49 +0200 Subject: [PATCH 1/2] Added complex recursion detection to tee filter --- server/modules/filter/tee.c | 94 ++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/server/modules/filter/tee.c b/server/modules/filter/tee.c index c15cc9420..f4ee04685 100644 --- a/server/modules/filter/tee.c +++ b/server/modules/filter/tee.c @@ -107,7 +107,6 @@ static void setDownstream(FILTER *instance, void *fsession, DOWNSTREAM *downstre static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue); static void diagnostic(FILTER *instance, void *fsession, DCB *dcb); - static FILTER_OBJECT MyObject = { createInstance, newSession, @@ -153,6 +152,33 @@ typedef struct { } TEE_SESSION; static int packet_is_required(GWBUF *queue); +static int detect_loops(TEE_INSTANCE *instance, HASHTABLE* ht, SERVICE* session); + +static int hkfn( + void* key) +{ + if(key == NULL){ + return 0; + } + unsigned int hash = 0,c = 0; + char* ptr = (char*)key; + while((c = *ptr++)){ + hash = c + (hash << 6) + (hash << 16) - hash; + } + return *(int *)key; +} + +static int hcfn( + void* v1, + void* v2) +{ + char* i1 = (char*) v1; + char* i2 = (char*) v2; + + return strcmp(i1,i2); +} + + /** * Implementation of the mandatory version entry point * @@ -313,7 +339,20 @@ char *remote, *userName; my_session = NULL; goto retblock; } - + + HASHTABLE* ht = hashtable_alloc(100,hkfn,hcfn); + bool is_loop = detect_loops(my_instance,ht,session->service); + hashtable_free(ht); + + if(is_loop) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "Error : %s: Recursive use of tee filter in service.", + session->service->name))); + my_session = NULL; + goto retblock; + } + if ((my_session = calloc(1, sizeof(TEE_SESSION))) != NULL) { my_session->active = 1; @@ -373,6 +412,7 @@ char *remote, *userName; goto retblock; } + ses->ses_is_child = true; my_session->branch_session = ses; my_session->branch_dcb = dcb; @@ -641,3 +681,53 @@ int i; return 1; return 0; } + +/** + * Detects possible loops in the query cloning chain. + */ +int detect_loops(TEE_INSTANCE *instance,HASHTABLE* ht, SERVICE* service) +{ + SERVICE* svc = service; + bool recurse = true; + int i; + + if(ht == NULL) + { + return -1; + } + + if(hashtable_add(ht,(void*)service->name,(void*)true) == 0) + { + return true; + } + + for(i = 0;in_filters;i++) + { + if(strcmp(svc->filters[i]->module,"tee") == 0) + { + /* + * Found a Tee filter, recurse down its path + * if the service name isn't already in the hashtable. + */ + + TEE_INSTANCE* ninst = (TEE_INSTANCE*)svc->filters[i]->filter; + if(ninst == NULL) + { + /** + * This tee instance hasn't been initialized yet and full + * resolution of recursion cannot be done now. + */ + continue; + } + SERVICE* tgt = ninst->service; + + if(detect_loops((TEE_INSTANCE*)svc->filters[i]->filter,ht,tgt)) + { + return true; + } + + } + } + + return false; +} From 69237b4f4f56e185158f8272170e4ea9fcc4f8b6 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 29 Dec 2014 14:58:08 +0200 Subject: [PATCH 2/2] Added tests for tee filter recursion. --- server/modules/filter/test/CMakeLists.txt | 10 +- server/modules/filter/test/tee_recursion.sh | 80 ++++++++++++ server/modules/filter/test/tee_recursion1.cnf | 114 ++++++++++++++++++ server/modules/filter/test/tee_recursion2.cnf | 112 +++++++++++++++++ 4 files changed, 315 insertions(+), 1 deletion(-) create mode 100755 server/modules/filter/test/tee_recursion.sh create mode 100644 server/modules/filter/test/tee_recursion1.cnf create mode 100644 server/modules/filter/test/tee_recursion2.cnf diff --git a/server/modules/filter/test/CMakeLists.txt b/server/modules/filter/test/CMakeLists.txt index 9c87ee926..d703bc7be 100644 --- a/server/modules/filter/test/CMakeLists.txt +++ b/server/modules/filter/test/CMakeLists.txt @@ -13,4 +13,12 @@ target_link_libraries(harness fullcore) execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${ERRMSG} ${CMAKE_CURRENT_BINARY_DIR}) add_test(TestHintfilter /bin/sh -c "MAXSCALE_HOME=\"${CMAKE_BINARY_DIR}\" ${CMAKE_CURRENT_BINARY_DIR}/harness -i ${CMAKE_CURRENT_SOURCE_DIR}/hint_testing.input -o ${CMAKE_CURRENT_BINARY_DIR}/hint_testing.output -c ${CMAKE_CURRENT_SOURCE_DIR}/hint_testing.cnf -t 1 -s 1 -e ${CMAKE_CURRENT_SOURCE_DIR}/hint_testing.expected") -add_test(TestRegexfilter /bin/sh -c "MAXSCALE_HOME=\"${CMAKE_BINARY_DIR}\" ${CMAKE_CURRENT_BINARY_DIR}/harness -i ${CMAKE_CURRENT_SOURCE_DIR}/regextest.input -o ${CMAKE_CURRENT_BINARY_DIR}/regextest.output -c ${CMAKE_CURRENT_SOURCE_DIR}/regextest.cnf -t 1 -s 1 -e ${CMAKE_CURRENT_SOURCE_DIR}/regextest.expected") \ No newline at end of file +add_test(TestRegexfilter /bin/sh -c "MAXSCALE_HOME=\"${CMAKE_BINARY_DIR}\" ${CMAKE_CURRENT_BINARY_DIR}/harness -i ${CMAKE_CURRENT_SOURCE_DIR}/regextest.input -o ${CMAKE_CURRENT_BINARY_DIR}/regextest.output -c ${CMAKE_CURRENT_SOURCE_DIR}/regextest.cnf -t 1 -s 1 -e ${CMAKE_CURRENT_SOURCE_DIR}/regextest.expected") + +add_test(TestTeeRecursion ${CMAKE_CURRENT_SOURCE_DIR}/tee_recursion.sh + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${TEST_USER} + ${TEST_PASSWORD} + ${TEST_HOST} + ${TEST_PORT}) diff --git a/server/modules/filter/test/tee_recursion.sh b/server/modules/filter/test/tee_recursion.sh new file mode 100755 index 000000000..7f2d307d6 --- /dev/null +++ b/server/modules/filter/test/tee_recursion.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +function execute_test() +{ + mysql -u $USER -p$PWD -h $HOST -P $PORT -e"select 1;" + + if [[ $? -eq 0 ]] + then + echo "Test failed: query to backend was successful." + return 1 + fi + + LAST_LOG=$(ls $BINDIR/log -1|grep err|sort|uniq|tail -n 1) + TEST_RESULT=$(cat $BINDIR/log/$LAST_LOG | grep -i recursive) + if [[ "$TEST_RESULT" != "" ]] + then + + return 0 + fi + + return 1 +} + +function reload_conf() +{ + $BINDIR/bin/maxadmin --user=admin --password=skysql reload config + if [[ $? -ne 0 ]] + then + echo "Test failed: maxadmin returned a non-zero value." + return 1 + fi + return 0 +} + +if [[ $# -lt 6 ]] +then + echo "usage: $0 " + exit 1 +fi +BINDIR=$1 +SRCDIR=$2 +USER=$3 +PWD=$4 +HOST=$5 +PORT=$6 +CONF=$BINDIR/etc/MaxScale.cnf +OLDCONF=$BINDIR/etc/MaxScale.cnf.old +TEST1=$SRCDIR/server/modules/filter/test/tee_recursion1.cnf +TEST2=$SRCDIR/server/modules/filter/test/tee_recursion2.cnf + +$BINDIR/bin/maxadmin --user=admin --password=skysql flush logs +execute_test +mv $CONF $OLDCONF + +cp $TEST1 $CONF +reload_conf +execute_test +T1RVAL=$? +mv $CONF $CONF.test1 +cp $TEST2 $CONF +reload_conf +execute_test +T2RVAL=$? +mv $CONF $CONF.test2 +mv $OLDCONF $CONF +reload_conf + +if [[ $T1RVAL -ne 0 ]] +then + echo "Test 1 failed." + exit 1 +elif [[ $T2RVAL -ne 0 ]] +then + echo "Test 2 failed" + exit 1 +else + echo "Test successful: log mentions recursive tee usage." +fi + +exit 0 diff --git a/server/modules/filter/test/tee_recursion1.cnf b/server/modules/filter/test/tee_recursion1.cnf new file mode 100644 index 000000000..61acb9574 --- /dev/null +++ b/server/modules/filter/test/tee_recursion1.cnf @@ -0,0 +1,114 @@ +[maxscale] +threads=4 + +[MySQL Monitor] +type=monitor +module=mysqlmon +servers=server1,server2,server3,server4 +user=maxuser +passwd=maxpwd +monitor_interval=10000 + +[RW Split Router] +type=service +router=readwritesplit +servers=server1,server2,server3,server4 +user=maxuser +passwd=maxpwd +filters=recurse1 + +[RW Split Hint Router] +type=service +router=readwritesplit +servers=server1,server2,server3,server4 +user=maxuser +passwd=maxpwd +filters=recurse2 + + +[Read Connection Router] +type=service +router=readconnroute +router_options=master +servers=server1 +user=maxuser +passwd=maxpwd +filters=recurse3 + +[recurse3] +type=filter +module=tee +service=RW Split Router + +[recurse2] +type=filter +module=tee +service=Read Connection Router + +[recurse1] +type=filter +module=tee +service=RW Split Hint Router + + +[Debug Interface] +type=service +router=debugcli + +[CLI] +type=service +router=cli + +[Read Connection Listener] +type=listener +service=Read Connection Router +protocol=MySQLClient +port=4008 + +[RW Split Listener] +type=listener +service=RW Split Router +protocol=MySQLClient +port=4006 + +[RW Split Hint Listener] +type=listener +service=RW Split Hint Router +protocol=MySQLClient +port=4009 + +[Debug Listener] +type=listener +service=Debug Interface +protocol=telnetd +port=4442 + +[CLI Listener] +type=listener +service=CLI +protocol=maxscaled +port=6603 + +[server1] +type=server +address=127.0.0.1 +port=3000 +protocol=MySQLBackend + +[server2] +type=server +address=127.0.0.1 +port=3001 +protocol=MySQLBackend + +[server3] +type=server +address=127.0.0.1 +port=3002 +protocol=MySQLBackend + +[server4] +type=server +address=127.0.0.1 +port=3003 +protocol=MySQLBackend diff --git a/server/modules/filter/test/tee_recursion2.cnf b/server/modules/filter/test/tee_recursion2.cnf new file mode 100644 index 000000000..55eafd728 --- /dev/null +++ b/server/modules/filter/test/tee_recursion2.cnf @@ -0,0 +1,112 @@ +[maxscale] +threads=4 + +[MySQL Monitor] +type=monitor +module=mysqlmon +servers=server1,server2,server3,server4 +user=maxuser +passwd=maxpwd +monitor_interval=10000 + +[RW Split Router] +type=service +router=readwritesplit +servers=server1,server2,server3,server4 +user=maxuser +passwd=maxpwd +filters=recurse1|recurse2 + +[RW Split Hint Router] +type=service +router=readwritesplit +servers=server1,server2,server3,server4 +user=maxuser +passwd=maxpwd + +[Read Connection Router] +type=service +router=readconnroute +router_options=master +servers=server1 +user=maxuser +passwd=maxpwd +filters=recurse3 + +[recurse3] +type=filter +module=tee +service=RW Split Router + +[recurse2] +type=filter +module=tee +service=Read Connection Router + +[recurse1] +type=filter +module=tee +service=RW Split Hint Router + + +[Debug Interface] +type=service +router=debugcli + +[CLI] +type=service +router=cli + +[Read Connection Listener] +type=listener +service=Read Connection Router +protocol=MySQLClient +port=4008 + +[RW Split Listener] +type=listener +service=RW Split Router +protocol=MySQLClient +port=4006 + +[RW Split Hint Listener] +type=listener +service=RW Split Hint Router +protocol=MySQLClient +port=4009 + +[Debug Listener] +type=listener +service=Debug Interface +protocol=telnetd +port=4442 + +[CLI Listener] +type=listener +service=CLI +protocol=maxscaled +port=6603 + +[server1] +type=server +address=127.0.0.1 +port=3000 +protocol=MySQLBackend + +[server2] +type=server +address=127.0.0.1 +port=3001 +protocol=MySQLBackend + +[server3] +type=server +address=127.0.0.1 +port=3002 +protocol=MySQLBackend + +[server4] +type=server +address=127.0.0.1 +port=3003 +protocol=MySQLBackend