Skip to content

Commit

Permalink
Enable a mixture of server-mode and client-mode operation simultaneously
Browse files Browse the repository at this point in the history
Source: https://sourceforge.net/p/sipp/mailman/message/28557628/

I'm posting the following modifications in case others have been
battling the same sipp limitations as I have.

Background
==========

I've had weeks of battle with SIPP server-mode scripts in an environment
where maintaining registration with an SBC is required.  Server mode means
that you cannot start a script with a 'send' to carry out the registration,
so a separate script had to be executed first to do this. Then all sorts of
scripting acrobatics were required to maintain a registration (which had a
60 second expiry) and respond to options pings (as used by dynamic HNT to
calculate the NAT timeout) during execution.

Additionally running SIP over TCP proved completely impossible for
server-mode (terminating party) scenarios - as discussed in a previous
mail, our SBC drops the registration as soon as it received a FIN on the
TCP session used to register the endpoint, meaning that it was impossible
to run a separate client-mode script to register, then a server-mode script
to terminate calls without losing our registration when the first script
exits.

The obvious solution was to enable SIPP to run in a mixture of client and
server mode concurrently.

Solution
========

The following diff log shows to updates required to do the above.
The modifications add two new command line parameters:

-sfrx
-snrx

Including either of these at the command line puts SIPP into a mixed mode
where it loads two scenarios, the first (specified in -sn or -sf) in client
mode, and the second (specified in -snrx or -sfrx) in server mode.

The client mode scenario runs as normal, starting as many calls as
specified in the command line parameters -m and -l.

Any unsolicited messages are handled in server mode by the second
scenario - spawning a new 'call' to handle each new incoming call-id
received as done in normal server mode.  The -m and -l call limits are
ignored by the second server-mode scenario.

The result is that a client mode scenario file can be specified in -sf to
handle registration or make outgoing calls, and a server mode scenario file
can simultaneously be specified in -sfrx to terminate multiple incoming
calls and handle incoming options pings.

This differs from the OOC scenario allowed previously where all incoming
unsolicited messages were handled by the same 'call' irrespective of their
call-id, making this option unusable for handling multiple simultaneous
incoming calls.

Fixes SIPp#745.
  • Loading branch information
Matthew Briggs authored and orgads committed Sep 7, 2024
1 parent 11b5174 commit c61d627
Show file tree
Hide file tree
Showing 13 changed files with 144 additions and 64 deletions.
4 changes: 2 additions & 2 deletions include/call.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ class call : virtual public task, virtual public listener, public virtual socket
public:
/* These are wrappers for various circumstances, (private) init does the real work. */
//call(char * p_id, int userId, bool ipv6, bool isAutomatic);
call(const char *p_id, bool use_ipv6, int userId, struct sockaddr_storage *dest);
call(const char *p_id, SIPpSocket *socket, struct sockaddr_storage *dest);
call(scenario *call_scenario, const char *p_id, bool use_ipv6, int userId, struct sockaddr_storage *dest);
call(scenario *call_scenario, const char *p_id, SIPpSocket *socket, struct sockaddr_storage *dest);
static call *add_call(int userId, bool ipv6, struct sockaddr_storage *dest);
call(scenario * call_scenario, SIPpSocket *socket, struct sockaddr_storage *dest, const char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitCall);

Expand Down
2 changes: 1 addition & 1 deletion include/fileutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ extern "C"
{
#endif

char* find_file(const char* filename);
char* find_file(const char* filename, const char *basepath);

#ifdef __cplusplus
}
Expand Down
7 changes: 7 additions & 0 deletions include/scenario.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@

#define MODE_CLIENT 0
#define MODE_SERVER 1
#define MODE_MIXED 2

#define MODE_3PCC_NONE 0
#define MODE_3PCC_CONTROLLER_A 2
Expand Down Expand Up @@ -190,6 +191,9 @@ class scenario
int get_counter(const char *varName, const char *what);
int get_rtd(const char *ptr, bool start);
int find_var(const char *varName);
void setFileName(const char *fileName);
const std::string &getFileName() const { return fileName; }
const std::string &getPath() const { return path; }

void addRtpTaskThreadID(pthread_t id);
void removeRtpTaskThreadID(pthread_t id);
Expand All @@ -205,6 +209,8 @@ class scenario
str_int_map initLabelMap;

str_int_map txnMap;
std::string fileName;
std::string path;

bool found_timewait;

Expand Down Expand Up @@ -232,6 +238,7 @@ class scenario

/* There are external variable containing the current scenario */
extern scenario *main_scenario;
extern scenario *rx_scenario;
extern scenario *ooc_scenario;
extern scenario *aa_scenario;
extern scenario *display_scenario;
Expand Down
2 changes: 1 addition & 1 deletion include/send_packets.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ typedef struct {
extern "C"
{
#endif
int parse_play_args(const char*, pcap_pkts*);
int parse_play_args(const char*, const char*, pcap_pkts*);
int parse_dtmf_play_args(const char*, pcap_pkts*, uint16_t start_seq_no);
void free_pcaps(pcap_pkts* pkts);
void send_packets(play_args_t*);
Expand Down
5 changes: 3 additions & 2 deletions include/sipp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,13 +326,14 @@ MAYBE_EXTERN double tls_version DEFVAL(0.0);
MAYBE_EXTERN const char * bind_to_device_name DEFVAL(nullptr);
#endif

MAYBE_EXTERN char* scenario_file DEFVAL(nullptr);
MAYBE_EXTERN char* scenario_path DEFVAL(nullptr);
MAYBE_EXTERN const char * scenario_file DEFVAL(nullptr);

// extern field file management
typedef std::map<std::string, FileContents *> file_map;
MAYBE_EXTERN file_map inFiles;
typedef std::map<std::string, str_int_map *> file_index;
MAYBE_EXTERN char *rx_ip_file DEFVAL(NULL);
MAYBE_EXTERN char *rx_default_file DEFVAL(NULL);
MAYBE_EXTERN char *ip_file DEFVAL(nullptr);
MAYBE_EXTERN char *default_file DEFVAL(nullptr);

Expand Down
4 changes: 2 additions & 2 deletions src/actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ void CAction::setPcapArgs(const char* P_value)

if (P_value != nullptr) {
M_pcapArgs = (pcap_pkts *) malloc(sizeof(*M_pcapArgs));
if (parse_play_args(P_value, M_pcapArgs) == -1) {
if (parse_play_args(P_value, M_scenario->getPath().c_str(), M_pcapArgs) == -1) {
ERROR("Play pcap error");
}
if (access(M_pcapArgs->file, F_OK)) {
Expand Down Expand Up @@ -712,7 +712,7 @@ void CAction::setRTPStreamActInfo(const char *P_value)

// Lookup best file match
if (pattern_mode == 0) {
found_file = find_file(argument_buf);
found_file = find_file(argument_buf, M_scenario->getPath().c_str());
if (found_file) {
if (strlen(found_file) >= sizeof(M_rtpstream_actinfo.filename)) {
ERROR("Filename/Pattern keyword %s is too long -- maximum supported length %zu",
Expand Down
10 changes: 5 additions & 5 deletions src/call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -785,14 +785,14 @@ unsigned long call::hash(const char * msg)
}

/******************* Call class implementation ****************/
call::call(const char *p_id, bool use_ipv6, int userId, struct sockaddr_storage *dest) : listener(p_id, true)
call::call(scenario *call_scenario, const char *p_id, bool use_ipv6, int userId, struct sockaddr_storage *dest) : listener(p_id, true)
{
init(main_scenario, nullptr, dest, p_id, userId, use_ipv6, false, false);
init(call_scenario, nullptr, dest, p_id, userId, use_ipv6, false, false);
}

call::call(const char *p_id, SIPpSocket *socket, struct sockaddr_storage *dest) : listener(p_id, true)
call::call(scenario *call_scenario, const char *p_id, SIPpSocket *socket, struct sockaddr_storage *dest) : listener(p_id, true)
{
init(main_scenario, socket, dest, p_id, 0 /* No User. */, socket->ss_ipv6, false /* Not Auto. */, false);
init(call_scenario, socket, dest, p_id, 0 /* No User. */, socket->ss_ipv6, false /* Not Auto. */, false);
}

call::call(scenario * call_scenario, SIPpSocket *socket, struct sockaddr_storage *dest, const char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitialization) : listener(p_id, true)
Expand Down Expand Up @@ -6832,7 +6832,7 @@ void *send_wrapper(void *arg)

class mockcall : public call {
public:
mockcall(bool is_ipv6) : listener("//testing", true), call("///testing", is_ipv6, 0, nullptr) {}
mockcall(bool is_ipv6) : listener("//testing", true), call(main_scenario, "///testing", is_ipv6, 0, nullptr) {}

/* Helpers to poke at protected internals */
void parse_media_addr(std::string const& msg) { get_remote_media_addr(msg); }
Expand Down
10 changes: 4 additions & 6 deletions src/fileutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
#include <string.h>
#include <unistd.h>

extern char* scenario_path;

int expand_user_path(const char* path, char* expanded_home_path /*The buffer*/, size_t buflen)
{
if (path[0] != '~') { /* We have nothing to expand here */
Expand Down Expand Up @@ -79,7 +77,7 @@ int expand_user_path(const char* path, char* expanded_home_path /*The buffer*/,
return 1;
}

char* find_file(const char* filename)
char* find_file(const char* filename, const char *basepath)
{
char tmppath[MAX_PATH];
tmppath[0] = '\0';
Expand All @@ -88,13 +86,13 @@ char* find_file(const char* filename)
filepathptr = filename;
}

if (filepathptr[0] == '/' || !*scenario_path) {
if (filepathptr[0] == '/' || !*basepath) {
return strdup(filepathptr);
}

size_t len = strlen(scenario_path) + strlen(filepathptr) + 1;
size_t len = strlen(basepath) + strlen(filepathptr) + 1;
char* fullpath = malloc(len);
snprintf(fullpath, len, "%s%s", scenario_path, filepathptr);
snprintf(fullpath, len, "%s%s", basepath, filepathptr);

if (access(fullpath, R_OK) < 0) {
free(fullpath);
Expand Down
25 changes: 24 additions & 1 deletion src/scenario.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ message::~message()

/******** Global variables which compose the scenario file **********/

scenario *rx_scenario;
scenario *main_scenario;
scenario *ooc_scenario;
scenario *aa_scenario;
Expand Down Expand Up @@ -409,6 +410,25 @@ void scenario::addRtpTaskThreadID(pthread_t id)
threadIDs[id] = "threadID";
}

void scenario::setFileName(const char *name)
{
const char* sep = strrchr(name, '/');
if (sep) {
++sep; // include slash
path = std::string(name, sep - name);
} else {
path.clear();
sep = name;
}
const char* ext = strrchr(sep, '.');
if (ext && strcmp(ext, ".xml") == 0) {
fileName = std::string(sep, ext - sep);
} else {
fileName = sep;
}
stats->setFileName(fileName.c_str(), ".csv");
}

void scenario::removeRtpTaskThreadID(pthread_t id)
{
threadIDs.erase(id);
Expand Down Expand Up @@ -684,6 +704,7 @@ scenario::scenario(char * filename, int deflt)
last_recv_optional = false;

if(filename) {
setFileName(filename);
if(!xp_set_xml_buffer_from_file(filename)) {
ERROR("Unable to load or parse '%s' xml scenario file", filename);
}
Expand Down Expand Up @@ -1247,7 +1268,9 @@ void scenario::computeSippMode()
bool isRecvCmdFound = false;
bool isSendCmdFound = false;

creationMode = -1;
if (creationMode != MODE_MIXED) {
creationMode = -1;
}
sendMode = -1;
thirdPartyMode = MODE_3PCC_NONE;

Expand Down
31 changes: 23 additions & 8 deletions src/screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ void print_statistics(int last)
}
}

static bool display_client()
{
return (creationMode == MODE_CLIENT) || ((creationMode == MODE_MIXED) && (display_scenario == main_scenario));
}

static bool display_server()
{
return (creationMode == MODE_SERVER) || ((creationMode == MODE_MIXED) && (display_scenario == rx_scenario));
}


void ScreenPrinter::print_closing_stats() {
M_last = true;
get_lines();
Expand Down Expand Up @@ -220,6 +231,10 @@ void ScreenPrinter::get_lines()
ERROR("Internal error: creationMode=%d, thirdPartyMode=%d",
creationMode, thirdPartyMode);
}
} else if((creationMode == MODE_MIXED) && (display_scenario == main_scenario)) {
lines.push_back("----------------Sipp Mixed Mode - main - call originating scenario------------");
} else if((creationMode == MODE_MIXED) && (display_scenario == rx_scenario)) {
lines.push_back("-----------------Sipp Mixed mode - rx - call terminating screnario-------------");
} else {
assert(creationMode == MODE_SERVER);
switch (thirdPartyMode) {
Expand Down Expand Up @@ -270,14 +285,14 @@ void ScreenPrinter::draw_scenario_screen()
unsigned long long total_calls =
display_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) +
display_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated);
if (creationMode == MODE_SERVER) {
if (display_server()) {
lines.push_back(" Port Total-time Total-calls Transport");
snprintf(buf, bufsiz, " %-5d %6lu.%02lu s %8llu %s", local_port,
clock_tick / 1000, (clock_tick % 1000) / 10, total_calls,
TRANSPORT_TO_STRING(transport));
lines.push_back(buf);
} else {
assert(creationMode == MODE_CLIENT);
assert(display_client());
if (users >= 0) {
lines.push_back(" Users (length) Port Total-time "
"Total-calls Remote-host");
Expand Down Expand Up @@ -320,7 +335,7 @@ void ScreenPrinter::draw_scenario_screen()
lines.push_back(buf);

/* 2nd line */
if (creationMode == MODE_SERVER) {
if (display_server()) {
snprintf(left_buf, 40, "%llu calls",
display_scenario->stats->GetStat(CStat::CPT_C_CurrentCall));
} else {
Expand All @@ -342,7 +357,7 @@ void ScreenPrinter::draw_scenario_screen()
/* 3rd line dead call msgs, and optional out-of-call msg */
snprintf(left_buf, 40, "%llu dead call msg (discarded)",
display_scenario->stats->GetStat(CStat::CPT_G_C_DeadCallMsgs));
if (creationMode == MODE_CLIENT) {
if (display_client()) {
snprintf(
buf, bufsiz, " %-38s %llu out-of-call msg (discarded)", left_buf,
display_scenario->stats->GetStat(CStat::CPT_G_C_OutOfCallMsgs));
Expand Down Expand Up @@ -479,7 +494,7 @@ void ScreenPrinter::draw_scenario_screen()
int buf_len = 0;

if (SendingMessage* src = curmsg->send_scheme) {
if (creationMode == MODE_SERVER) {
if (display_server()) {
if (src->isResponse()) {
buf_len += snprintf(buf + buf_len, bufsiz - buf_len,
" <---------- %-10d ", src->getCode());
Expand Down Expand Up @@ -527,7 +542,7 @@ void ScreenPrinter::draw_scenario_screen()
: "");
}
} else if (curmsg->recv_response) {
if (creationMode == MODE_SERVER) {
if (display_server()) {
buf_len += snprintf(buf + buf_len, bufsiz - buf_len,
" ----------> %-10d ", curmsg->recv_response);
} else {
Expand Down Expand Up @@ -570,7 +585,7 @@ void ScreenPrinter::draw_scenario_screen()
}
int len = strlen(desc) < 9 ? 9 : strlen(desc);

if (creationMode == MODE_SERVER) {
if (display_server()) {
snprintf(left_buf, 40, " [%9s] Pause%*s", desc,
23 - len > 0 ? 23 - len : 0, "");
} else {
Expand All @@ -583,7 +598,7 @@ void ScreenPrinter::draw_scenario_screen()
curmsg->sessions,
curmsg->nb_unexp);
} else if (curmsg->recv_request) {
if (creationMode == MODE_SERVER) {
if (display_server()) {
buf_len +=
snprintf(buf + buf_len, bufsiz - buf_len,
" ----------> %-10s ", curmsg->recv_request);
Expand Down
4 changes: 2 additions & 2 deletions src/send_packets.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ float2timer(float time, struct timeval *tvp)
tvp->tv_usec = n * 100000;
}

int parse_play_args(const char* filename, pcap_pkts* pkts)
int parse_play_args(const char* filename, const char *basepath, pcap_pkts* pkts)
{
pkts->file = find_file(filename);
pkts->file = find_file(filename, basepath);
prepare_pkts(pkts->file, pkts);
return 1;
}
Expand Down
Loading

0 comments on commit c61d627

Please sign in to comment.