WIP: DIAMETER performance testing

Change-Id: I7e6e4fdf3252b0a05f13eb85c8a84cc3c32b550e
This commit is contained in:
Harald Welte 2020-03-01 21:21:59 +01:00
parent c3c6ee6c63
commit 36fbac125b
6 changed files with 483 additions and 0 deletions

View File

@ -0,0 +1,19 @@
[ORDERED_INCLUDE]
# Common configuration, shared between test suites
"../Common.cfg"
# testsuite specific configuration, not expected to change
"./DIAMETER_Tests.default"
# Local configuration below
[LOGGING]
[TESTPORT_PARAMETERS]
[MODULE_PARAMETERS]
#DIAMETER_Tests.mp_client := { "192.168.100.1", 62324, "", -1}
[MAIN_CONTROLLER]
[EXECUTE]
DIAMETER_Tests.control

View File

@ -0,0 +1,12 @@
[LOGGING]
FileMask := ERROR | WARNING | PARALLEL | TESTCASE | USER;
[TESTPORT_PARAMETERS]
DIA_SERVER.DIAMETER.noDelay := "YES"
DIA_CLIENT.DIAMETER.noDelay := "YES"
[MODULE_PARAMETERS]
[MAIN_CONTROLLER]
[EXECUTE]

View File

@ -0,0 +1,180 @@
module DIAMETER_Tests {
import from General_Types all;
import from Osmocom_Types all;
import from IPL4asp_Types all;
import from DIAMETER_Types all;
import from DIAMETER_Templates all;
import from DIAMETER_CodecPort all;
import from DIAMETER_CodecPort_CtrlFunct all;
import from DIAMETER_Emulation all;
modulepar {
DIAMETER_conn_parameters mp_server := {
remote_ip := "",
remote_sctp_port := -1,
local_ip := "127.0.0.1",
local_sctp_port := 8888
};
DIAMETER_conn_parameters mp_client := {
remote_ip := "127.0.0.1",
remote_sctp_port := 8888,
local_ip := "",
local_sctp_port := -1
};
}
type component DIAMETER_RAW_CT {
port DIAMETER_CODEC_PT DIAMETER;
var integer g_diameter_conn_id;
var integer msg_count_intended := 100000;
var integer msg_count_actual := 0;
}
private template (value) SctpTuple ts_SCTP(template (omit) integer ppid := omit) := {
sinfo_stream := omit,
sinfo_ppid := ppid,
remSocks := omit,
assocId := omit
};
private template PortEvent tr_SctpAssocChange := {
sctpEvent := {
sctpAssocChange := ?
}
}
private template PortEvent tr_SctpPeerAddrChange := {
sctpEvent := {
sctpPeerAddrChange := ?
}
}
function f_init(DIAMETER_conn_parameters p) runs on DIAMETER_RAW_CT {
var Result res;
map(self:DIAMETER, system:DIAMETER_CODEC_PT);
if (p.remote_sctp_port == -1) {
res := DIAMETER_CodecPort_CtrlFunct.f_IPL4_listen(DIAMETER, p.local_ip, p.local_sctp_port, { sctp := valueof(ts_SCTP) });
} else {
res := DIAMETER_CodecPort_CtrlFunct.f_IPL4_connect(DIAMETER, p.remote_ip, p.remote_sctp_port,
p.local_ip, p.local_sctp_port, -1, { sctp := valueof(ts_SCTP) });
}
if (not ispresent(res.connId)) {
setverdict(fail, "Could not connect DIAMETER socket, check your configuration");
mtc.stop;
}
g_diameter_conn_id := res.connId;
}
function tr_DIAMETER_RecvFrom_R(template PDU_DIAMETER msg)
runs on DIAMETER_RAW_CT return template DIAMETER_RecvFrom {
var template DIAMETER_RecvFrom mrf := {
connId := g_diameter_conn_id,
remName := ?,
remPort := ?,
locName := ?,
locPort := ?,
msg := msg
}
return mrf;
}
function f_rcv_main() runs on DIAMETER_RAW_CT {
var template IMSI imsi_t;
var hexstring imsi;
var DIAMETER_RecvFrom mrf;
var PortEvent port_evt;
alt {
[] DIAMETER.receive(PortEvent:{connOpened := ?}) -> value port_evt {
g_diameter_conn_id := port_evt.connOpened.connId;
}
[] DIAMETER.receive(PortEvent:?) { }
/* handle CER/CEA handshake */
[] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(tr_DIAMETER_R(cmd_code := Capabilities_Exchange))) -> value mrf {
var template (value) PDU_DIAMETER resp;
resp := ts_DIA_CEA(mrf.msg.hop_by_hop_id, mrf.msg.end_to_end_id);
DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, resp));
}
/* DIAMETER from remote peer */
[] DIAMETER.receive(tr_DIAMETER_RecvFrom_R(?)) -> value mrf {
msg_count_actual := msg_count_actual + 1;
//imsi_t := f_DIAMETER_get_imsi(mrf.msg);
}
[] DIAMETER.receive(tr_SctpAssocChange) { }
[] DIAMETER.receive(tr_SctpPeerAddrChange) { }
}
}
function f_server_main(DIAMETER_conn_parameters p) runs on DIAMETER_RAW_CT {
f_init(p);
while (true) {
f_rcv_main();
}
}
function f_client_main(DIAMETER_conn_parameters p) runs on DIAMETER_RAW_CT {
var integer i;
var hexstring imsi := f_rnd_hexstring(16);
f_init(p);
f_sleep(1.0);
log("START_XMIT");
for (i := 0; i < msg_count_intended; i := i+1) {
var template (value) PDU_DIAMETER msg;
msg := ts_DIA_AIR(int2oct(i, 4), int2oct(100000+i, 4), char2oct("session_id"),
"dest_realm", imsi);
DIAMETER.send(t_DIAMETER_Send(g_diameter_conn_id, msg));
msg_count_actual := msg_count_actual + 1;
}
log("Transmitted ", msg_count_actual, " messages");
}
const integer NR_PAIRS := 1
type component Test_CT {
var DIAMETER_RAW_CT vc_server[NR_PAIRS];
var DIAMETER_RAW_CT vc_client[NR_PAIRS];
};
testcase TC_flood() runs on Test_CT {
var integer i;
for (i := 0; i < sizeof(vc_server); i:=i+1) {
var DIAMETER_conn_parameters p_server := mp_server;
p_server.local_sctp_port := p_server.local_sctp_port + i;
log("Starting server ", i);
vc_server[i] := DIAMETER_RAW_CT.create("DIA_SERVER");
vc_server[i].start(f_server_main(p_server));
}
for (i := 0; i < sizeof(vc_client); i:=i+1) {
var DIAMETER_conn_parameters p_client := mp_client;
p_client.remote_sctp_port := p_client.remote_sctp_port + i;
log("Starting client ", i);
vc_client[i] := DIAMETER_RAW_CT.create("DIA_CLIENT");
vc_client[i].start(f_client_main(p_client));
}
for (i := 0; i < lengthof(vc_client); i:=i+1) {
vc_client[i].done;
}
f_sleep(1.0);
}
}

43
diameter/gen_links.sh Executable file
View File

@ -0,0 +1,43 @@
#!/bin/bash
BASEDIR=../deps
. ../gen_links.sh.inc
DIR=$BASEDIR/titan.Libraries.TCCUsefulFunctions/src
FILES="TCCInterface_Functions.ttcn TCCConversion_Functions.ttcn TCCConversion.cc TCCInterface.cc TCCInterface_ip.h"
FILES+=" TCCEncoding_Functions.ttcn TCCEncoding.cc " # GSM 7-bit coding
gen_links $DIR $FILES
DIR=$BASEDIR/titan.TestPorts.Common_Components.Socket-API/src
FILES="Socket_API_Definitions.ttcn"
gen_links $DIR $FILES
# Required by MGCP and IPA
DIR=$BASEDIR/titan.TestPorts.IPL4asp/src
FILES="IPL4asp_Functions.ttcn IPL4asp_PT.cc IPL4asp_PT.hh IPL4asp_PortType.ttcn IPL4asp_Types.ttcn IPL4asp_discovery.cc IPL4asp_protocol_L234.hh"
gen_links $DIR $FILES
DIR=$BASEDIR/titan.TestPorts.TELNETasp/src
FILES="TELNETasp_PT.cc TELNETasp_PT.hh TELNETasp_PortType.ttcn"
gen_links $DIR $FILES
#DIR=$BASEDIR/titan.ProtocolModules.GTPv2_v13.7.0/src
#FILES="GTPv2_Types.ttcn"
#gen_links $DIR $FILES
#DIR=$BASEDIR/titan.ProtocolModules.GTP_v13.5.0/src
#FILES="GTPC_EncDec.cc GTPC_Types.ttcn GTPU_EncDec.cc GTPU_Types.ttcn"
#gen_links $DIR $FILES
DIR=$BASEDIR/titan.ProtocolModules.DIAMETER_ProtocolModule_Generator/src
FILES="DIAMETER_EncDec.cc"
gen_links $DIR $FILES
DIR=../library
FILES="Misc_Helpers.ttcn General_Types.ttcn GSM_Types.ttcn Osmocom_Types.ttcn Native_Functions.ttcn Native_FunctionDefs.cc "
FILES+="DNS_Helpers.ttcn "
FILES+="DIAMETER_Types.ttcn DIAMETER_CodecPort.ttcn DIAMETER_CodecPort_CtrlFunct.ttcn DIAMETER_CodecPort_CtrlFunctDef.cc DIAMETER_Emulation.ttcn DIAMETER_Templates.ttcn "
gen_links $DIR $FILES
ignore_pp_results

9
diameter/regen_makefile.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
FILES="*.ttcn IPL4asp_PT.cc IPL4asp_discovery.cc Native_FunctionDefs.cc TCCConversion.cc TCCEncoding.cc TCCInterface.cc TELNETasp_PT.cc DIAMETER_EncDec.cc DIAMETER_CodecPort_CtrlFunctDef.cc "
export CPPFLAGS_TTCN3=""
../regen-makefile.sh DIAMETER_Tests.ttcn $FILES
sed -i -e 's/^LINUX_LIBS = -lxml2/LINUX_LIBS = -lxml2 -lfftranscode -lgnutls/' Makefile

220
diameter/sctptest.c Normal file
View File

@ -0,0 +1,220 @@
/*
* Simple SCTP test program, original version by Daniel Mack
* at https://gist.github.com/zonque/7d03568eab14a2bb57cb
*
* Modified in 2020 by Harald Welte <laforge@gnumonks.org> for
* - DATA chunk rate testing.
* - initial support for userspace SCTP stack testing
*
* Compile:
*
* gcc sctptest.c -o server -lsctp -Wall
* ln -s server client
*
* Invoke:
*
* ./client
* ./server
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define _GNU_SOURCE
#include <getopt.h>
#define HAVE_KERNEL_SCTP
#ifdef HAVE_KERNEL_SCTP
#include <netinet/sctp.h>
#define ext_socket socket
#define ext_bind bind
#define ext_setsockopt setsockopt
#define ext_listen listen
#define ext_accept accept
#define ext_close close
#define ext_connect connect
#else
/* sctplib + socketapi */
#include <ext_socket.h>
#include <sctp.h>
#endif
#define MY_PORT_NUM 62324
/* compute differece between two timespec */
static void timespec_diff(const struct timespec *start, const struct timespec *stop,
struct timespec *result)
{
if ((stop->tv_nsec - start->tv_nsec) < 0) {
result->tv_sec = stop->tv_sec - start->tv_sec - 1;
result->tv_nsec = stop->tv_nsec - start->tv_nsec + 1000000000;
} else {
result->tv_sec = stop->tv_sec - start->tv_sec;
result->tv_nsec = stop->tv_nsec - start->tv_nsec;
}
}
static void die(const char *s) {
perror(s);
exit(1);
}
static void server(int argc, char **argv)
{
struct sockaddr_in servaddr = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_ANY),
.sin_port = htons(MY_PORT_NUM),
};
struct sctp_initmsg initmsg = {
.sinit_num_ostreams = 5,
.sinit_max_instreams = 5,
.sinit_max_attempts = 4,
};
struct sctp_sndrcvinfo sndrcvinfo;
int listen_fd, conn_fd, flags, ret, in;
listen_fd = ext_socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
if (listen_fd < 0)
die("socket");
ret = ext_bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
if (ret < 0)
die("bind");
ret = ext_setsockopt(listen_fd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg));
if (ret < 0)
die("setsockopt");
ret = ext_listen(listen_fd, initmsg.sinit_max_instreams);
if (ret < 0)
die("listen");
for (;;) {
char buffer[1024];
unsigned int num_chunks_rcvd;
printf("Waiting for connection\n");
fflush(stdout);
conn_fd = ext_accept(listen_fd, (struct sockaddr *) NULL, NULL);
if(conn_fd < 0)
die("accept()");
printf("New client connected\n");
fflush(stdout);
num_chunks_rcvd = 0;
while (1) {
in = sctp_recvmsg(conn_fd, buffer, sizeof(buffer), NULL, 0, &sndrcvinfo, &flags);
if (in <= 0)
break;
num_chunks_rcvd++;
}
printf("Server: Received %u chunks, closing\n", num_chunks_rcvd);
fflush(stdout);
ext_close(conn_fd);
}
}
static void client(int argc, char **argv) {
struct sockaddr_in servaddr = {
.sin_family = AF_INET,
.sin_port = htons(MY_PORT_NUM),
.sin_addr.s_addr = inet_addr("127.0.0.1"),
};
struct timespec ts_start, ts_stop, ts_diff;
uint8_t *payload;
unsigned int num_chunks = 10000;
unsigned int chunksize = 150;
int nodelay = 0;
int conn_fd, ret;
while (1) {
int option_index = 0, c;
const struct option long_options[] = {
{ "num-chunks", 1, 0, 'n' },
{ "chunk-size", 1, 0, 's' },
{ "sctp-nodelay", 0, 0, 'd' },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "n:s:d", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'n':
num_chunks = atoi(optarg);
break;
case 's':
chunksize = atoi(optarg);
break;
case 'd':
nodelay = 1;
break;
default:
break;
}
}
printf("About to send %u chunks of each %u bytes\n", num_chunks, chunksize);
payload = malloc(chunksize);
if (!payload)
die("malloc()");
conn_fd = ext_socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
if (conn_fd < 0)
die("socket()");
ret = ext_setsockopt(conn_fd, IPPROTO_SCTP, SCTP_NODELAY, &nodelay, sizeof(nodelay));
if (ret < 0)
die("setsockopt(SCTP_NODELAY)");
ret = ext_connect(conn_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
if (ret < 0)
die("connect()");
ret = clock_gettime(CLOCK_MONOTONIC_RAW, &ts_start);
if (ret < 0)
die("clock_gettime()");
for (int i = 0; i < num_chunks; i++) {
ret = sctp_sendmsg(conn_fd, payload, chunksize, NULL, 0, 0, 0, 0, 0, 0 );
if (ret < 0)
die("sctp_sendmsg");
}
ret = clock_gettime(CLOCK_MONOTONIC_RAW, &ts_stop);
if (ret < 0)
die("clock_gettime()");
timespec_diff(&ts_start, &ts_stop, &ts_diff);
float diff_f = (float)ts_diff.tv_sec + (float)ts_diff.tv_nsec/1000000000.0;
printf("%u DATA chunks of %u bytes each in %5.2f seconds: %5.2f DATA chunks per second\n",
num_chunks, chunksize, diff_f, (float)num_chunks/diff_f);
close(conn_fd);
}
int main(int argc, char **argv) {
if (strstr(basename(argv[0]), "server"))
server(argc, argv);
else
client(argc, argv);
return 0;
}