1. Store Subscriber (MSISDN, IMSI) as testcase witness, and not only the Session-Id
2. In tinyTestcase.sh, in case of SLR waitfc programming, we need the subscriber condition, not the Session-Id, because that session is created in the client (PCRF) and can't know it.
2.1 By the way, change tsharkDecoder.sh to get MSISDN and IMSI
2.2 Answers must copy the Session-Id from the corresponding request in case of difference (SLR/A, for example; in case of RAR/A, no problem because is the Gx session created in
ADML system too)
3. Allow test|<id>|sendxml2e/2c|step number (specially 2c), to be used as step reference not only for answers build, but for further requests (SNR for example) which should transport
the same Session-Id.
result += "\n the difficulty of predicting these information). Be sure to";
result += "\n refer to a 'wait for request' step. Conditions like 'regexp'";
result += "\n (as we will see later) are not verified.";
+ result += "\n In the case of requests, the step number is used to force the";
+ result += "\n copy of Session-Id value from the referred step.";
result += "\n";
result += "\n sendxml2c|<source_file>[|<step number>]";
result += "\n Sends xml source file (pathfile) to client (it would be a";
SCR_DIR=`readlink -f $0 | xargs dirname`
TESTCASE_BN=testcase.txt
WHAT=?????????
+REQUEST_STEP=
#############
# FUNCTIONS #
local s_wait="test|1|$wait_command|$code|$((1-isrequest))"
[ -n "$sessionid" ] && s_wait="${s_wait}|||${sessionid}"
+ local s_send="test|1|$send_command|$xml"
+
if [ $isrequest -eq 1 ]
then
- echo "test|1|$send_command|$xml" >> $TESTCASE_BN
+ # Send the request
+ # Special case for SNR/SNA (code=8388636): the Session-Id is created on client and received on SLR previously
+ [ "$code" = "8388636" -a "$adml_type" = "server" ] && s_send="test|1|$send_command|$xml|$REQUEST_STEP"
+ echo "$s_send" >> $TESTCASE_BN
+
if [ -n "$resultcode" ]
then
local hbh="$(grep -o "hop-by-hop-id=\"[0-9]*\"" $xml)"
_rc=$(getResultCode $ans_xml)
[ -n "$_rc" ] && rc=$_rc
fi
+
+ # Wait the answer:
s_wait="${s_wait}|${rc}"
fi
echo "$s_wait" >> $TESTCASE_BN
else
+ local next_step_number=$((lines+1))
+
+ # Wait the request
+ # Special case for SLR/SLA (code=8388635): the Session-Id is created on client
+ [ "$code" = "8388635" -a "$adml_type" = "server" ] && { s_wait="test|1|$wait_command|$code|1||||$subscriber" ; REQUEST_STEP=$next_step_number ; }
echo "$s_wait" >> $TESTCASE_BN
- echo "test|1|$send_command|$xml|$((lines+1))" >> $TESTCASE_BN
+
+ # Send the answer
+ echo "test|1|$send_command|$xml|$next_step_number" >> $TESTCASE_BN
fi
}
void TestCase::assertMessage(const anna::DataBlock &db, bool toEntity) throw(anna::RuntimeException) {
bool isRequest = anna::diameter::codec::functions::isRequest(db);
- bool registerSessionId = ((isRequest && toEntity) || (!isRequest && !toEntity) /* (*) */);
- // (*) we register answers Session-Id assuming that we will know the Session-Id values created by the client (OCS)
- // This is another solution for TODO(***) regarding diameter server testing. No sure about the final implementation.
+ bool registerKeys = ((isRequest && toEntity) || (!isRequest && !toEntity) /* (*) */);
+ // (*) we register answers Session-Id "assuming" that we will know the Session-Id values created by the client.
+ // This is another solution regarding diameter server testing. No sure about the final implementation.
+ // We will help registering also subscriber data, because certain messages (i.e. SLR) coming from clients could
+ // have specific Session-Id value (unknown at test programming), and normally are identified by subscriber.
// Check hop-by-hop:
if (isRequest) {
a_hopByHops[hbh] = NULL; // may be assigned to a wait condition
}
- if (registerSessionId)
- TestManager::instantiate().registerSessionId(anna::diameter::helpers::base::functions::getSessionId(db), this);
+ if (registerKeys) {
+ TestManager &testManager = TestManager::instantiate();
+ testManager.registerSessionId(anna::diameter::helpers::base::functions::getSessionId(db), this);
+
+
+ std::string subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(db, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164);
+ if (subscriberId == "") // try with IMSI
+ subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(db, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI);
+
+ if (subscriberId != "")
+ testManager.registerSubscriberId(subscriberId, this);
+ }
}
void TestCase::addTimeout(const anna::Millisecond &timeout) throw(anna::RuntimeException) {
#include <anna/app/functions.hpp>
#include <anna/timex/Engine.hpp>
#include <anna/diameter/helpers/base/functions.hpp>
+#include <anna/diameter/helpers/dcca/functions.hpp>
#include <anna/diameter.comm/ClientSession.hpp>
#include <anna/diameter.comm/ServerSession.hpp>
}
}
+void TestManager::registerSubscriberId(const std::string &subscriberId, const TestCase *testCase) throw(anna::RuntimeException) {
+
+ std::map<std::string /* subscriber id's */, TestCase*>::const_iterator it = a_subscriberIdTestCaseMap.find(subscriberId);
+ if (it != a_subscriberIdTestCaseMap.end()) { // found
+ unsigned int id = it->second->getId();
+ if (id != testCase->getId()) {
+ throw anna::RuntimeException(anna::functions::asString("There is another test case (id = %llu) which registered such subscriberId: %s", id, subscriberId.c_str()), ANNA_FILE_LOCATION);
+ }
+ }
+ else {
+ a_subscriberIdTestCaseMap[subscriberId] = const_cast<TestCase*>(testCase);
+ LOGDEBUG(anna::Logger::debug(anna::functions::asString("TestManager::registerSubscriberId for test case (id = %llu): %s)", testCase->getId(), subscriberId.c_str()), ANNA_FILE_LOCATION));
+ }
+}
+
TestTimer* TestManager::createTimer(TestCaseStep* testCaseStep, const anna::Millisecond &timeout, const TestTimer::Type::_v type)
throw(anna::RuntimeException) {
TestTimer* result(NULL);
a_testPool.clear();
a_sessionIdTestCaseMap.clear();
+ a_subscriberIdTestCaseMap.clear();
a_currentTestIt = a_testPool.end();
a_poolCycle = 1;
configureTTPS(0); // stop
result = true;
}
//a_sessionIdTestCaseMap.clear();
+ //a_subscriberIdTestCaseMap.clear();
return result;
}
}
catch (anna::RuntimeException &ex) {
//ex.trace();
- LOGWARNING(anna::Logger::warning("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
+ LOGDEBUG(anna::Logger::debug("Cannot get the Session-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
return NULL;
}
std::map<std::string /* session id's */, TestCase*>::const_iterator sessionIdIt = a_sessionIdTestCaseMap.find(sessionId);
if (sessionIdIt != a_sessionIdTestCaseMap.end())
return sessionIdIt->second;
- LOGWARNING(anna::Logger::warning(anna::functions::asString("Cannot identify the Test Case for received Session-Id: %s", sessionId.c_str()), ANNA_FILE_LOCATION));
+ LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Session-Id: %s", sessionId.c_str()), ANNA_FILE_LOCATION));
+ return NULL;
+}
+
+TestCase *TestManager::getTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw() {
+ try {
+ subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_E164);
+ if (subscriberId == "") // try with IMSI
+ subscriberId = anna::diameter::helpers::dcca::functions::getSubscriptionIdData(message, anna::diameter::helpers::dcca::AVPVALUES__Subscription_Id_Type::END_USER_IMSI);
+ }
+ catch (anna::RuntimeException &ex) {
+ //ex.trace();
+ LOGDEBUG(anna::Logger::debug("Cannot get the Subscriber-Id from received DataBlock in order to identify the Test Case", ANNA_FILE_LOCATION));
+ return NULL;
+ }
+ std::map<std::string /* subscriber id's */, TestCase*>::const_iterator subscriberIdIt = a_subscriberIdTestCaseMap.find(subscriberId);
+ if (subscriberIdIt != a_subscriberIdTestCaseMap.end())
+ return subscriberIdIt->second;
+
+ LOGDEBUG(anna::Logger::debug(anna::functions::asString("Cannot identify the Test Case for received Subscriber-Id: %s", subscriberId.c_str()), ANNA_FILE_LOCATION));
return NULL;
}
if (!tests()) return;
// Identify the test case:
- std::string sessionId;
- TestCase *tc = getTestCaseFromSessionId(message, sessionId);
- if (!tc) return;
+ std::string sessionId, subscriberId;
+ TestCase *tc;
+ tc = getTestCaseFromSessionId(message, sessionId);
+ if (!tc) getTestCaseFromSubscriberId(message, subscriberId);
+ if (!tc) {
+ LOGWARNING(anna::Logger::warning(anna::comm::functions::asText("Cannot identify the Test Case for the message received from server: ", message), ANNA_FILE_LOCATION)); // this should not appear
+ return;
+ }
// Work with Test case:
TestStepWait *tsw = tc->searchNextWaitConditionFulfilled(message, true /* comes from entity */);
if (!tests()) return;
// Identify the test case:
- std::string sessionId;
- TestCase *tc = getTestCaseFromSessionId(message, sessionId);
- if (!tc) return;
+ std::string sessionId, subscriberId;
+ TestCase *tc;
+ tc = getTestCaseFromSessionId(message, sessionId);
+ if (!tc) getTestCaseFromSubscriberId(message, subscriberId);
+ if (!tc) {
+ LOGWARNING(anna::Logger::warning(anna::comm::functions::asText("Cannot identify the Test Case for the message received from client: ", message), ANNA_FILE_LOCATION)); // this should not appear
+ return;
+ }
// Work with Test case:
TestStepWait *tsw = tc->searchNextWaitConditionFulfilled(message, false /* comes from client */);
// Test timers
timer_container a_timers;
+ // Test case identifiers:
// Session-Id's
std::map<std::string /* session id's */, TestCase*> a_sessionIdTestCaseMap; // stores used Session-Id values within a test case.
// No other can use them, but a test case could use more than one.
+ // Subscriber's
+ std::map<std::string /* subscriber id's */, TestCase*> a_subscriberIdTestCaseMap; // stores used Subscriber-Id values within a test case.
+
+
StatSummary a_statSummary; // general statistics
public:
void registerSessionId(const std::string &sessionId, const TestCase *testCase) throw(anna::RuntimeException);
+ void registerSubscriberId(const std::string &subscriberId, const TestCase *testCase) throw(anna::RuntimeException);
int tests() const throw() { return a_testPool.size(); }
void setTimerController(anna::timex::Engine *engine) throw() { a_timeController = engine; }
// Main logic
TestCase *getTestCaseFromSessionId(const anna::DataBlock &message, std::string &sessionId) throw();
+ TestCase *getTestCaseFromSubscriberId(const anna::DataBlock &message, std::string &subscriberId) throw();
void receiveMessage(const anna::DataBlock &message, OriginHost *host, const anna::diameter::comm::ClientSession *clientSession) throw(anna::RuntimeException);
void receiveMessage(const anna::DataBlock &message, OriginHost *host, const anna::diameter::comm::ServerSession *serverSession) throw(anna::RuntimeException);
bool TestStepSendxml::do_execute() throw() {
bool success = false;
- std::string failReason, s_warn;
+ std::string failReason;
MyDiameterEntity *entity = a_originHost->getEntity(); // by default
MyLocalServer *localServer = a_originHost->getDiameterServer(); // by default
const TestStepWait *tsw = NULL;
-
- // Create comm message:
anna::diameter::comm::Message *msg = a_originHost->createCommMessage();
- //msg->clearBody();
- msg->setBody(a_message);
try {
- // Update sequence for answers:
- if (a_waitForRequestStepNumber != -1) { // is an answer: try to copy sequence information; alert about Session-Id discrepance
- // Request which was received:
- tsw = static_cast<const TestStepWait*>(a_testCase->getStep(a_waitForRequestStepNumber));
- const anna::DataBlock &request = tsw->getMsgDataBlock();
- anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(request);
- anna::diameter::EndToEnd ete = anna::diameter::codec::functions::getEndToEnd(request);
+ if (a_waitForRequestStepNumber != -1) {
- // Update sequence:
- anna::diameter::codec::functions::setHopByHop(a_message, hbh);
- anna::diameter::codec::functions::setEndToEnd(a_message, ete);
+ // Referenced request in the 'wait for request step':
+ tsw = static_cast<const TestStepWait*>(a_testCase->getStep(a_waitForRequestStepNumber));
+ const anna::DataBlock &referenceRequest = tsw->getMsgDataBlock();
+ std::string sessionIdReferenceRequest = anna::diameter::helpers::base::functions::getSessionId(referenceRequest);
+ bool thisIsAnswer = anna::diameter::codec::functions::isRequest(getMsgDataBlock());
- // Check Session-Id for warning ...
- std::string sessionIdAnswer = anna::diameter::helpers::base::functions::getSessionId(a_message);
- std::string sessionIdRequest = anna::diameter::helpers::base::functions::getSessionId(request);
+ if (thisIsAnswer) { // is an answer: try to copy sequence information; alert about Session-Id discrepance
+ anna::diameter::HopByHop hbh = anna::diameter::codec::functions::getHopByHop(referenceRequest);
+ anna::diameter::EndToEnd ete = anna::diameter::codec::functions::getEndToEnd(referenceRequest);
- if (sessionIdRequest != sessionIdAnswer) {
- s_warn = anna::functions::asString("Sending an answer which Session-Id (%s) is different than supposed corresponding request (%s)", sessionIdAnswer.c_str(), sessionIdRequest.c_str());
- LOGWARNING(anna::Logger::warning(s_warn, ANNA_FILE_LOCATION));
- a_testCase->addDebugSummaryHint(s_warn);
+ // Update sequence:
+ anna::diameter::codec::functions::setHopByHop(a_message, hbh);
+ anna::diameter::codec::functions::setEndToEnd(a_message, ete);
+ }
+
+ // Session-Id substitution:
+ std::string thisSessionId = anna::diameter::helpers::base::functions::getSessionId(getMsgDataBlock());
+ if (thisSessionId != sessionIdReferenceRequest) {
+ static anna::diameter::codec::Message codecMsg;
+ codecMsg.decode(getMsgDataBlock());
+ codecMsg.getAvp("Session-Id")->getUTF8String()->setValue(sessionIdReferenceRequest);
+ a_message = codecMsg.code();
+ std::string trace = anna::functions::asString("Replacing %s Session-Id (%s) to set the corresponding request one (%s)", (thisIsAnswer ? "answer":"request"), thisSessionId.c_str(), sessionIdReferenceRequest.c_str());
+ LOGDEBUG(anna::Logger::debug(trace, ANNA_FILE_LOCATION));
+ a_testCase->addDebugSummaryHint(trace);
}
}
+ // Create comm message:
+ //msg->clearBody();
+ msg->setBody(a_message);
+
+
if (getType() == Type::Sendxml2e) {
anna::diameter::comm::ClientSession *usedClientSession = NULL;
# Get the frames with diameter content (take care about '-2' two-pass option and don't add it, because we need to get reassembled parts in their corresponding frames):
# Fields needed (we won't need diameter.hopbyhopid & diameter.endtoendid to verify diameter message as hint patterns; length management will be enough): see https://www.wireshark.org/docs/dfref/d/diameter.html
-FIELDS_DIAMETER="-e diameter.cmd.code -e diameter.flags.request -e diameter.applicationId -e diameter.hopbyhopid -e diameter.endtoendid -e diameter.Session-Id -e diameter.Origin-Host -e diameter.length"
+FIELDS_DIAMETER="-e diameter.cmd.code -e diameter.flags.request -e diameter.applicationId -e diameter.hopbyhopid -e diameter.endtoendid -e diameter.Session-Id -e diameter.Origin-Host -e diameter.Subscription-Id-Data -e diameter.Subscription-Id-Type -e diameter.length"
FIELDS="-e frame.number -e frame.time_epoch -e ip.src_host -e ip.dst_host $FIELDS_DIAMETER -e tcp.len -e frame.protocols -e tcp.segment"
# Disect selectors for non-standard diameter ports:
for port in $OTHER_PORTS
$TSHARK -E separator="|" -r $PCAP_FILE -N mntC -Tfields $FIELDS $DISECT_SELECTORS 2>/dev/null | grep -i diameter > $tmpdir/diameter_frames
# Example output:
-# /length\
-# frame timestamp src dst code R App-ID HopByHop EndToEnd Session-Id Origin-Host DIAM TCP protocol segments
-# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
-# 1|1427215933.697904000|gt_traf|vcbavipt|272|1|16777238|0x0004e6e6|0x000bd986|xxxxxxx|xxxxxxxxxxxxx|432|432|eth:ip:tcp:diameter:diameter:diameter3gpp|
-# 3|1427215934.449523000|vcbavipt|gt_traf|272|0|16777238|0x0004e6e6|0x000bd986|xxxxxxx|xxxxxxxxxxxxx|292|292|eth:ip:tcp:diameter:diameter:diameter3gpp|
+# /length\
+# frame timestamp src dst code R App-ID HopByHop EndToEnd Session-Id Origin-Host Subs-Data Subs-Type DIAM TCP protocol segments
+# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
+# 1|1427215933.697904000|gt_traf|vcbavipt|272|1|16777238|0x0004e6e6|0x000bd986|xxxxxxx|xxxxxxxxxxxxx|xxxxxxxxxx|xxxxxxxxxxx|432|432|eth:ip:tcp:diameter:diameter:diameter3gpp|
+# 3|1427215934.449523000|vcbavipt|gt_traf|272|0|16777238|0x0004e6e6|0x000bd986|xxxxxxx|xxxxxxxxxxxxx|xxxxxxxxxx|xxxxxxxxxxx|292|292|eth:ip:tcp:diameter:diameter:diameter3gpp|
# 5|1427215934.456160000|gt_traf|vcbavipt||||||||1400|eth:ip:tcp:diameter|
-# 6|1427215934.456204000|gt_traf|vcbavipt|265|1|16777236|0x000c73c3|0x0004cee4|xxxxxxx|xxxxxxxxxxxxx|1972|572|eth:ip:tcp:diameter:diameter:diameter3gpp|5,6
-# 8|1427215935.123559000|vcbavipt|gt_traf|265|0|16777236|0x000c73c3|0x0004cee4|xxxxxxx|xxxxxxxxxxxxx|248|248|eth:ip:tcp:diameter:diameter:diameter3gpp|
+# 6|1427215934.456204000|gt_traf|vcbavipt|265|1|16777236|0x000c73c3|0x0004cee4|xxxxxxx|xxxxxxxxxxxxx|xxxxxxxxxx|xxxxxxxxxxx|1972|572|eth:ip:tcp:diameter:diameter:diameter3gpp|5,6
+# 8|1427215935.123559000|vcbavipt|gt_traf|265|0|16777236|0x000c73c3|0x0004cee4|xxxxxxx|xxxxxxxxxxxxx|xxxxxxxxxx|xxxxxxxxxxx|248|248|eth:ip:tcp:diameter:diameter:diameter3gpp|
all_frames=( $(cat $tmpdir/diameter_frames | cut -d\| -f1) )
-needs_join=( $(cat $tmpdir/diameter_frames | cut -d\| -f15) )
-main_frames=( $(cat $tmpdir/diameter_frames | awk -F\| '{ if ($13 != "") print $1 }') )
+needs_join=( $(cat $tmpdir/diameter_frames | cut -d\| -f17) )
+main_frames=( $(cat $tmpdir/diameter_frames | awk -F\| '{ if ($15 != "") print $1 }') )
# Reassemble procedure (using frame 1 as example):
# (for non segmented frames, it is enough with tcp or diameter length within the frame content itself)
frame_info=$(grep "^${frame}|" $tmpdir/diameter_frames)
# Get the diameter part:
- tcp_len=$(echo $frame_info | cut -d\| -f13)
+ tcp_len=$(echo $frame_info | cut -d\| -f15)
frm_len=$(wc -c $tmpdir/block.$frame | awk '{ print $1 }')
cut_len=$((frm_len-2*tcp_len))
cat $tmpdir/block.$frame | cut -c${cut_len}- > $RESULTS_DIR/$frame.hex
appid=$(echo $frame_info | cut -d\| -f7)
sid=$(echo $frame_info | cut -d\| -f10)
oh=$(echo $frame_info | cut -d\| -f11)
+ subscriber=$(echo $frame_info | cut -d\| -f12)
+ subscribertype=$(echo $frame_info | cut -d\| -f13)
+ [ "$subscribertype" = "0" ] && subscribertype=msisdn
+ [ "$subscribertype" = "1" ] && subscribertype=imsi
+
#hbh=$(echo $frame_info | cut -d\| -f8)
#e2e=$(echo $frame_info | cut -d\| -f9)
# HBH and ETE To decimal:
echo "applicationid=$appid" >> $RESULTS_DIR/$frame.metadata
[ -n "$sid" ] && echo "sessionid=$sid" >> $RESULTS_DIR/$frame.metadata
echo "originhost=$oh" >> $RESULTS_DIR/$frame.metadata
+ if [ -n "$subscriber" ]
+ then
+ echo "subscriber=$subscriber" >> $RESULTS_DIR/$frame.metadata
+ echo "subscribertype=$subscribertype" >> $RESULTS_DIR/$frame.metadata
+ fi
#echo "hopbyhop=$hbh" >> $RESULTS_DIR/$frame.metadata
#echo "endtoend=$e2e" >> $RESULTS_DIR/$frame.metadata
# Delete superfluous metadata:
echo "Deleting superfluous buffers & metadata ..."
-segments=( $(cat $tmpdir/diameter_frames | awk -F\| '{ if ($12 == "") print $1 }') )
+segments=( $(cat $tmpdir/diameter_frames | awk -F\| '{ if ($14 == "") print $1 }') )
for s in ${segments[@]}; do rm $RESULTS_DIR/$s.*; done
# Detecting Session-Id values: