From d2f2e9b5431c826f4a7a688525a44ab266d6ee20 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 1 Mar 2020 21:21:59 +0100 Subject: WIP: DIAMETER performance testing Change-Id: I7e6e4fdf3252b0a05f13eb85c8a84cc3c32b550e --- diameter/DIAMETER_Tests.cfg | 19 ++++ diameter/DIAMETER_Tests.default | 12 +++ diameter/DIAMETER_Tests.ttcn | 180 ++++++++++++++++++++++++++++++++ diameter/gen_links.sh | 43 ++++++++ diameter/regen_makefile.sh | 9 ++ diameter/sctptest.c | 220 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 483 insertions(+) create mode 100644 diameter/DIAMETER_Tests.cfg create mode 100644 diameter/DIAMETER_Tests.default create mode 100644 diameter/DIAMETER_Tests.ttcn create mode 100755 diameter/gen_links.sh create mode 100755 diameter/regen_makefile.sh create mode 100644 diameter/sctptest.c diff --git a/diameter/DIAMETER_Tests.cfg b/diameter/DIAMETER_Tests.cfg new file mode 100644 index 00000000..a55c10e1 --- /dev/null +++ b/diameter/DIAMETER_Tests.cfg @@ -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 diff --git a/diameter/DIAMETER_Tests.default b/diameter/DIAMETER_Tests.default new file mode 100644 index 00000000..0b0bd1ce --- /dev/null +++ b/diameter/DIAMETER_Tests.default @@ -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] diff --git a/diameter/DIAMETER_Tests.ttcn b/diameter/DIAMETER_Tests.ttcn new file mode 100644 index 00000000..0633ab71 --- /dev/null +++ b/diameter/DIAMETER_Tests.ttcn @@ -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); +} + + + + + + +} diff --git a/diameter/gen_links.sh b/diameter/gen_links.sh new file mode 100755 index 00000000..cc5304fd --- /dev/null +++ b/diameter/gen_links.sh @@ -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 diff --git a/diameter/regen_makefile.sh b/diameter/regen_makefile.sh new file mode 100755 index 00000000..77b25254 --- /dev/null +++ b/diameter/regen_makefile.sh @@ -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 diff --git a/diameter/sctptest.c b/diameter/sctptest.c new file mode 100644 index 00000000..2e56c1ae --- /dev/null +++ b/diameter/sctptest.c @@ -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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _GNU_SOURCE +#include + +#define HAVE_KERNEL_SCTP + +#ifdef HAVE_KERNEL_SCTP +#include +#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 +#include +#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; +} -- cgit v1.2.3