Merge remote-tracking branch 'sipwise/master' into add-requests-ps-stats

pull/446/head
Claudiu Boriga 7 years ago
commit f7ece0452b

@ -169,6 +169,7 @@ option and which are reproduced below:
-G, --graphite-interval=INT Graphite data statistics send interval
--graphite-prefix=STRING Graphite prefix for every line
-T, --tos=INT TOS value to set on streams
--control-tos=INT TOS value to set on control-ng interface
-o, --timeout=SECS RTP timeout
-s, --silent-timeout=SECS RTP timeout for muted
-a, --final-timeout=SECS Call timeout
@ -183,11 +184,16 @@ option and which are reproduced below:
--redis-expires=INT Expire time in seconds for redis keys
--redis-multikey Use multiple redis keys for storing the call (old behaviour) DEPRECATED
-q, --no-redis-required Start even if can't connect to redis databases
--redis-allowed-errors Number of allowed errors before redis is temporarily disabled
--redis-disable-time Number of seconds redis communication is disabled because of errors
--redis-cmd-timeout Sets a timeout in milliseconds for redis commands
--redis-connect-timeout Sets a timeout in milliseconds for redis connections
-b, --b2b-url=STRING XMLRPC URL of B2B UA
-L, --log-level=INT Mask log priorities above this level
--log-facility=daemon|local0|... Syslog facility to use for logging
--log-facility-cdr=local0|... Syslog facility to use for logging CDRs
--log-facility-rtcp=local0|... Syslog facility to use for logging RTCP data (take care of traffic amount)
--log-format=default|parsable Log prefix format
-E, --log-stderr Log on stderr instead of syslog
-x, --xmlrpc-format=INT XMLRPC timeout request format to use. 0: SEMS DI, 1: call-id only
--num-threads=INT Number of worker threads to create
@ -213,6 +219,24 @@ The options are described in more detail below.
If called with this option, the *rtpengine* daemon will simply print its version number
and exit.
* --config-file
Specifies the location of a config file to be used. The config file is an *.ini* style config file,
with all command-line options listed here also being valid options in the config file. For all
command-line options, the long name version instead of the single-character version
(e.g. `table` instead of just `t`) must be used in the config file. For boolean options that are
either present or not (e.g. `no-fallback`), a boolean value (either `true` or `false`) must be
used in the config file. If an option is given in both the config file and at the command line,
the command-line value overrides the value from the config file. Options that can be specified
multiple times on the command line must be given only once in the config file, with the multiple
values separated by semicolons (see section *Interfaces configuration* below for an example).
* --config-section
Specifies the *.ini* style section to be used in the config file. Multiple sections can be
present in the config file, but only one can be used at a time. The default value is `rtpengine`.
A config file section is started in the config file using square brackets (e.g. `[rtpengine]`).
* -t, --table
Takes an integer argument and specifies which kernel table to use for in-kernel packet forwarding. See
@ -227,46 +251,7 @@ The options are described in more detail below.
* -i, --interface
Specifies a local network interface for RTP. At least one must be given, but multiple can be specified.
The format of the value is `[NAME/]IP[!IP]` with `IP` being either an IPv4 address or an IPv6 address.
The second IP address after the exclamation point is optional and can be used if the address to advertise
in outgoing SDP bodies should be different from the actual local address. This can be useful in certain
cases, such as your SIP proxy being behind NAT. For example, `--interface=10.65.76.2!192.0.2.4` means
that 10.65.76.2 is the actual local address on the server, but outgoing SDP bodies should advertise
192.0.2.4 as the address that endpoints should talk to. Note that you may have to escape the exlamation
point from your shell, e.g. using `\!`.
Giving an interface a name (separated from the address by a slash) is optional; if omitted, the name
`default` is used. Names are useful to create logical interfaces which consist of one or more local
addresses. It is then possible to instruct *rtpengine* to use particular interfaces when processing
an SDP message, to use different local addresses when talking to different endpoints. The most common use
case for this is to bridge between one or more private IP networks and the public internet.
For example, if clients coming from a private IP network must communicate their RTP with the local
address 10.35.2.75, while clients coming from the public internet must communicate with your other
local address 192.0.2.67, you could create one logical interface `pub` and a second one `priv` by
using `--interface=pub/192.0.2.67 --interface=priv/10.35.2.75`. You can then use the `direction`
option to tell *rtpengine* which local address to use for which endpoints (either `pub` or `priv`).
If multiple logical interfaces are configured, but the `direction` option isn't given in a
particular call, then the first interface given on the command line will be used.
It is possible to specify multiple addresses for the same logical interface (the same name). Most
commonly this would be one IPv4 addrsess and one IPv6 address, for example:
`--interface=192.168.63.1 --interface=fe80::800:27ff:fe00:0`. In this example, no interface name
is given, therefore both addresses will be added to a logical interface named `default`. You would use
the `address family` option to tell *rtpengine* which address to use in a particular case.
It is also possible to have multiple addresses of the same family in a logical network interface. In
this case, the first address (of a particular family) given for an interface will be the primary address
used by *rtpengine* for most purposes. Any additional addresses will be advertised as additional ICE
candidates with increasingly lower priority. This is useful on multi-homed systems and allows endpoints
to choose the best possible path to reach the RTP proxy. If ICE is not being used, then additional
addresses will go unused.
If you're not using the NG protocol but rather the legacy UDP protocol used by the *rtpproxy* module,
the interfaces must be named `internal` and `external` corresponding to the `i` and `e` flags if you
wish to use network bridging in this mode.
See the section *Interfaces configuration* just below for details.
* -l, --listen-tcp, -u, --listen-udp, -n, --listen-ng
@ -308,6 +293,11 @@ The options are described in more detail below.
Takes an integer as argument and if given, specifies the TOS value that should be set in outgoing
packets. The default is to leave the TOS field untouched. A typical value is 184 (*Expedited Forwarding*).
* --control-tos
Takes an integer as argument and if given, specifies the TOS value that should be set in the control-ng
interface packets. The default is to leave the TOS field untouched.
* -o, --timeout
Takes the number of seconds as argument after which a media stream should be considered dead if no media
@ -360,6 +350,13 @@ The options are described in more detail below.
Same as --log-facility with the difference that only RTCP data is written to this log facility.
Be careful with this parameter since there may be a lot of information written to it.
* --log-format=default|parsable
Selects between multiple log output styles. The default is to prefix log lines with a description
of the relevant entity, such as `[CALLID]` or `[CALLID port 12345]`. The `parsable` output style
is similar, but makes the ID easier to parse by enclosing it in quotes, such as `[ID="CALLID"]`
or `[ID="CALLID" port="12345"]`.
* -E, --log-stderr
Log to stderr instead of syslog. Only useful in combination with `--foreground`.
@ -444,6 +441,28 @@ The options are described in more detail below.
Be aware that if the -r redis can't be initially connected, sessions are not reloaded upon rtpengine startup,
even though rtpengine still starts.
* --redis-allowed-errors
If this parameter is present and has a value >= 0, it will configure how many consecutive errors are allowed
when communicating with a redis server before the redis communication will be temporarily disabled for that
server. While the communcation is disabled there will be no attempts to reconnect to redis or send commands
to that server. Default value is -1, meaning that this feature is disabled.
* --redis-disable-time
This parameter configures the number of seconds redis communication is disabled because of errors.
This works together with redis-allowed-errors parameter. The default value is 10.
* --redis-cmd-timeout
If this parameter is set to a non-zero value it will set the timeout, in milliseconds, for each command to the redis server.
If redis does not reply within the specified timeout the command will fail. The default value is 0, meaning that the commands
will have no timeout
* --redis-connect-timeout
This parameter sets the timeout value, in milliseconds, when connecting to a redis server. If the connection cannot be made
within the specified timeout the connection will fail. Note that in case of failure, when reconnecting to redis, a PING command
is issued before attempting to connect so the `--redis-cmd-timeout` value will also be added to the total waiting time.
This is useful if using `--redis-allowed-errors`, when attempting to estimate the total lost time in case of redis failures.
The default value for the connection timeout is 1000ms.
* -b, --b2b-url
Enables and sets the URI for an XMLRPC callback to be made when a call is torn down due to packet
@ -585,6 +604,80 @@ A typical command line (enabling both UDP and NG protocols) thus may look like:
--listen-udp=127.0.0.1:22222 --listen-ng=127.0.0.1:2223 --tos=184 \
--pidfile=/var/run/rtpengine.pid
Interfaces configuration
------------------------
The command-line options `-i` or `--interface=`, or equivalently the `interface=` config file option,
specify local network interfaces for RTP. At least one must be given, but multiple can be specified.
The format of the value is `[NAME/]IP[!IP]` with `IP` being either an IPv4 address or an IPv6 address.
To configure multiple interfaces using the command-line options, simply present multiple `-i` or
`--interface=` options. When using the config file, only use a single `interface=` line, but specify
multiple values separated by semicolons (e.g. `interface = internal/12.23.34.45;external/23.34.45.54`).
The second IP address after the exclamation point is optional and can be used if the address to advertise
in outgoing SDP bodies should be different from the actual local address. This can be useful in certain
cases, such as your SIP proxy being behind NAT. For example, `--interface=10.65.76.2!192.0.2.4` means
that 10.65.76.2 is the actual local address on the server, but outgoing SDP bodies should advertise
192.0.2.4 as the address that endpoints should talk to. Note that you may have to escape the exlamation
point from your shell when using command-line options, e.g. using `\!`.
Giving an interface a name (separated from the address by a slash) is optional; if omitted, the name
`default` is used. Names are useful to create logical interfaces which consist of one or more local
addresses. It is then possible to instruct *rtpengine* to use particular interfaces when processing
an SDP message, to use different local addresses when talking to different endpoints. The most common use
case for this is to bridge between one or more private IP networks and the public internet.
For example, if clients coming from a private IP network must communicate their RTP with the local
address 10.35.2.75, while clients coming from the public internet must communicate with your other
local address 192.0.2.67, you could create one logical interface `pub` and a second one `priv` by
using `--interface=pub/192.0.2.67 --interface=priv/10.35.2.75`. You can then use the `direction`
option to tell *rtpengine* which local address to use for which endpoints (either `pub` or `priv`).
If multiple logical interfaces are configured, but the `direction` option isn't given in a
particular call, then the first interface given on the command line will be used.
It is possible to specify multiple addresses for the same logical interface (the same name). Most
commonly this would be one IPv4 addrsess and one IPv6 address, for example:
`--interface=192.168.63.1 --interface=fe80::800:27ff:fe00:0`. In this example, no interface name
is given, therefore both addresses will be added to a logical interface named `default`. You would use
the `address family` option to tell *rtpengine* which address to use in a particular case.
It is also possible to have multiple addresses of the same family in a logical network interface. In
this case, the first address (of a particular family) given for an interface will be the primary address
used by *rtpengine* for most purposes. Any additional addresses will be advertised as additional ICE
candidates with increasingly lower priority. This is useful on multi-homed systems and allows endpoints
to choose the best possible path to reach the RTP proxy. If ICE is not being used, then additional
addresses will go unused, even though ports would still get allocated on those interfaces.
Another option is to give interface names in the format `BASE:SUFFIX`. This allows interfaces to be
used in a round-robin fashion, useful for load-balancing the port ranges of multiple interfaces.
For example, consider the following configuration:
`--interface=pub:1/192.0.2.67 --interface=pub:2/10.35.2.75`. These two interfaces can still be
referenced directly by name (e.g. `direction=pub:1`), but it is now also possible to reference only
the base name (i.e. `direction=pub`). If the base name is used, one of the two interfaces is selected
in a round-robin fashion, and only if the interface actually has enough open ports available. This
makes it possible to effectively increase the number of available media ports across multiple IP
addresses. There is no limit on how many interfaces can share the same base name.
It is possible to combine the `BASE:SUFFIX` notation with specifying multiple addresses for the same
interface name. An advanced example could be (using config file notation, and omitting actual
network addresses):
interface = pub:1/IPv4 pub:1/IPv4 pub:1/IPv6 pub:2/IPv4 pub:2/IPv6 pub:3/IPv6 pub:4/IPv4
In this example, when `direction=pub` is IPv4 is needed as a primary address, either `pub:1`, `pub:2`,
or `pub:4` might be selected. When `pub:1` is selected, one IPv4 and one IPv6 address will be used
as additional ICE alternatives. For `pub:2`, only one IPv6 is used as ICE alternative, and for `pub:4`
no alternatives would be used. When IPv6 is needed as a primary address, either `pub:1`, `pub:2`, or
`pub:3` might be selected. If at any given time not enough ports are available on any interface,
it will not be selected by the round-robin algorithm.
If you're not using the NG protocol but rather the legacy UDP protocol used by the *rtpproxy* module,
the interfaces must be named `internal` and `external` corresponding to the `i` and `e` flags if you
wish to use network bridging in this mode.
In-kernel Packet Forwarding
---------------------------
@ -615,7 +708,8 @@ actually add the required rule to the tables.
In short, the prerequisites for in-kernel packet forwarding are:
1. The `xt_RTPENGINE` kernel module must be loaded.
2. An `iptables` and/or `ip6tables` rule must be present in the `INPUT` chain to send packets
2. An `iptables` and/or `ip6tables` rule must be present in the `INPUT` chain (or in a custom user-defined
chain which is then called by the `INPUT` chain) to send packets
to the `RTPENGINE` target. This rule should be limited to UDP packets, but otherwise there
are no restrictions.
3. The `rtpengine` daemon must be running.
@ -704,6 +798,15 @@ like `--dport 30000:40000`. If the kernel module receives a packet that it doesn
to an active media stream, it will simply ignore it and hand it back to the network stack for normal
processing.
The `RTPENGINE` rule need not necessarily be present directly in the `INPUT` chain. It can also be in a
user-defined chain which is then referenced by the `INPUT` chain, like so:
iptables -N rtpengine
iptables -I INPUT -p udp -j rtpengine
iptables -I rtpengine -j RTPENGINE --id 42
This can be a useful setup if certain firewall scripts are being used.
Summary
-------
@ -719,7 +822,7 @@ A typical start-up sequence including in-kernel forwarding might look like this:
echo 'del 0' > /proc/rtpengine/control
# start daemon
/usr/sbin/rtpengine --table=0 --ip=10.64.73.31 --ip6=2001:db8::4f3:3d \
/usr/sbin/rtpengine --table=0 --interface=10.64.73.31 --interface=2001:db8::4f3:3d \
--listen-ng=127.0.0.1:2223 --tos=184 --pidfile=/var/run/rtpengine.pid --no-fallback
Running Multiple Instances
@ -741,9 +844,9 @@ then the start-up sequence might look like this:
echo 'del 0' > /proc/rtpengine/control
echo 'del 1' > /proc/rtpengine/control
/usr/sbin/rtpengine --table=0 --ip=10.64.73.31 \
/usr/sbin/rtpengine --table=0 --interface=10.64.73.31 \
--listen-ng=127.0.0.1:2223 --tos=184 --pidfile=/var/run/rtpengine-10.pid --no-fallback
/usr/sbin/rtpengine --table=1 --ip=192.168.65.73 \
/usr/sbin/rtpengine --table=1 --interface=192.168.65.73 \
--listen-ng=127.0.0.1:2224 --tos=184 --pidfile=/var/run/rtpengine-192.pid --no-fallback
With this setup, the SIP proxy can choose which instance of *rtpengine* to talk to and thus which local
@ -908,6 +1011,10 @@ Optionally included keys are:
Identical to setting `record call` to `on` (see below).
- `no rtcp attribute`
Omit the `a=rtcp` line from the outgoing SDP.
* `replace`
@ -944,24 +1051,12 @@ Optionally included keys are:
However, this mechanism for selecting the address family is now obsolete
and the `address family` dictionary key should be used instead.
A direction keyword is *round-robin-calls*. If this is received, a round robin algorithm runs for
choosing the logical interface for the current stream(e.g. audio, video).
The algorithm checks that all local interfaces of the tried logical interface have free ports for
call streams. If a logical interface fails the check, the next one is tried. If there is no logical
interface found with this property, it fallbacks to the default behaviour (e.g. return first logical
interface in --interface list even if no free ports are available). The attribute is ignored for
answers() because the logical interface was already selected at offers().
Naming an interface "round-robin-calls" and trying to select it using direction will
__run the above algorithm__!
Round robin for both legs of the stream:
{ ..., "direction": [ "round-robin-calls", "round-robin-calls" ], ... }
Round robin for first leg and and select "pub" for the second leg of the stream:
{ ..., "direction": [ "round-robin-calls", "pub" ], ... }
Round robin for first leg and and default behaviour for the second leg of the stream:
{ ..., "direction": [ "round-robin-calls" ], ... }
For legacy support, the special direction keyword `round-robin-calls` can be used to invoke the
round-robin interface selection algorithm described in the section *Interfaces configuration*.
If this special keyword is used, the round-robin selection will run over all configured
interfaces, whether or not they are configured using the `BASE:SUFFIX` interface name notation.
This special keyword is provided only for legacy support and should be considered obsolete.
It will be removed in future versions.
* `received from`
@ -1111,6 +1206,32 @@ Optionally included keys are:
See the `--recording-dir` option above.
* `codec`
Contains a dictionary controlling various aspects of codecs (or RTP payload types).
The following keys are understood:
* `strip`
Contains a list of strings. Each string is the name of a codec or RTP payload
type that should be removed from the SDP. Codec names are case sensitive, and
can be either from the list of codecs explicitly defined by the SDP through
an `a=rtpmap` attribute, or can be from the list of RFC-defined codecs. Examples
are `PCMU`, `opus`, or `telephone-event`.
As a special keyword, `all` can be used to remove all codecs, except the ones
that should explicitly offered (see below). Note that it is an error to strip
all codecs and leave none that could be offered. In this case, the original
list of codecs will be left unchanged.
* `offer`
Contains a list of strings. Each string is the name of a codec that should be
included in the list of codecs offered. This is primarily useful to block all
codecs (`strip -> all`) except the ones given in the `offer` whitelist.
Currently codecs that were not present in the original list of codecs
offered by the client will be ignored.
An example of a complete `offer` request dictionary could be (SDP body abbreviated):

@ -55,7 +55,7 @@ include ../lib/lib.Makefile
SRCS= main.c kernel.c poller.c aux.c control_tcp.c streambuf.c call.c control_udp.c redis.c \
bencode.c cookie_cache.c udp_listener.c control_ng.c sdp.c str.c stun.c rtcp.c \
crypto.c rtp.c call_interfaces.c dtls.c log.c cli.c graphite.c ice.c socket.c \
media_socket.c homer.c recording.c statistics.c cdr.c ssrc.c iptables.c
media_socket.c homer.c recording.c statistics.c cdr.c ssrc.c iptables.c tcp_listener.c
LIBSRCS= loglib.c auxlib.c rtplib.c
OBJS= $(SRCS:.c=.o) $(LIBSRCS:.c=.o)

@ -34,8 +34,8 @@ static cond_t threads_cond = COND_STATIC_INIT;
static struct thread_buf __thread t_bufs[NUM_THREAD_BUFS];
static int __thread t_buf_idx;
__thread struct timeval g_now;
volatile int g_shutdown;
__thread struct timeval rtpe_now;
volatile int rtpe_shutdown;
#ifdef NEED_ATOMIC64_MUTEX
mutex_t __atomic64_mutex = MUTEX_STATIC_INIT;

@ -60,13 +60,10 @@ G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint)); \
#define NUM_THREAD_BUFS 8
#define ALGORITHM_DEFAULT ""
#define ALGORITHM_ROUND_ROBIN_CALLS "round-robin-calls"
/*** GLOBALS ***/
extern __thread struct timeval g_now;
extern volatile int g_shutdown;
extern __thread struct timeval rtpe_now;
extern volatile int rtpe_shutdown;
@ -186,7 +183,8 @@ INLINE int strmemcmp(const void *mem, int len, const char *str) {
}
INLINE void random_string(unsigned char *buf, int len) {
assert(RAND_bytes(buf, len) == 1);
int ret = RAND_bytes(buf, len);
assert(ret == 1);
}
INLINE long unsigned int ssl_random() {
long unsigned int ret;

@ -41,6 +41,8 @@
#include "cdr.h"
#include "statistics.h"
#include "ssrc.h"
#include "main.h"
#include "graphite.h"
/* also serves as array index for callstream->peers[] */
@ -115,12 +117,19 @@ const struct transport_protocol transport_protocols[] = {
};
const int num_transport_protocols = G_N_ELEMENTS(transport_protocols);
/* XXX rework these */
struct stats rtpe_statsps;
struct stats rtpe_stats;
rwlock_t rtpe_callhash_lock;
GHashTable *rtpe_callhash;
/* ********** */
static void __monologue_destroy(struct call_monologue *monologue);
static int monologue_destroy(struct call_monologue *ml);
static struct timeval add_ongoing_calls_dur_in_interval(struct callmaster *m,
struct timeval *interval_start, struct timeval *interval_duration);
static struct timeval add_ongoing_calls_dur_in_interval(struct timeval *interval_start,
struct timeval *interval_duration);
/* called with call->master_lock held in R */
static int call_timer_delete_monologues(struct call *c) {
@ -138,7 +147,7 @@ static int call_timer_delete_monologues(struct call *c) {
if (!ml->deleted)
continue;
if (ml->deleted > poller_now) {
if (ml->deleted > rtpe_now.tv_sec) {
if (!min_deleted || ml->deleted < min_deleted)
min_deleted = ml->deleted;
continue;
@ -162,12 +171,11 @@ out:
/* called with callmaster->hashlock held */
/* called with hashlock held */
static void call_timer_iterator(gpointer data, gpointer user_data) {
struct call *c = data;
struct iterator_helper *hlp = user_data;
GList *it;
struct callmaster *cm;
unsigned int check;
int good = 0;
struct packet_stream *ps;
@ -180,11 +188,10 @@ static void call_timer_iterator(gpointer data, gpointer user_data) {
rwlock_lock_r(&c->master_lock);
log_info_call(c);
cm = c->callmaster;
rwlock_lock_r(&cm->conf.config_lock);
rwlock_lock_r(&rtpe_config.config_lock);
// final timeout applicable to all calls (own and foreign)
if (cm->conf.final_timeout && poller_now >= (c->created.tv_sec + cm->conf.final_timeout)) {
if (rtpe_config.final_timeout && rtpe_now.tv_sec >= (c->created.tv_sec + rtpe_config.final_timeout)) {
ilog(LOG_INFO, "Closing call due to final timeout");
tmp_t_reason = FINAL_TIMEOUT;
for (it = c->monologues.head; it; it = it->next) {
@ -201,11 +208,11 @@ static void call_timer_iterator(gpointer data, gpointer user_data) {
goto out;
}
if (c->deleted && poller_now >= c->deleted
if (c->deleted && rtpe_now.tv_sec >= c->deleted
&& c->last_signal <= c->deleted)
goto delete;
if (c->ml_deleted && poller_now >= c->ml_deleted) {
if (c->ml_deleted && rtpe_now.tv_sec >= c->ml_deleted) {
if (call_timer_delete_monologues(c))
goto delete;
}
@ -239,14 +246,14 @@ no_sfd:
if (good)
goto next;
check = cm->conf.timeout;
check = rtpe_config.timeout;
tmp_t_reason = TIMEOUT;
if (!MEDIA_ISSET(ps->media, RECV) || !sfd || !PS_ISSET(ps, FILLED)) {
check = cm->conf.silent_timeout;
check = rtpe_config.silent_timeout;
tmp_t_reason = SILENT_TIMEOUT;
}
if (poller_now - atomic64_get(timestamp) < check)
if (rtpe_now.tv_sec - atomic64_get(timestamp) < check)
good = 1;
next:
@ -277,7 +284,7 @@ delete:
goto out;
out:
rwlock_unlock_r(&cm->conf.config_lock);
rwlock_unlock_r(&rtpe_config.config_lock);
rwlock_unlock_r(&c->master_lock);
log_info_clear();
}
@ -330,7 +337,7 @@ retry:
for (i = 0; i < 100; i++)
close(i);
if (!ilog_stderr) {
if (!rtpe_config.common.log_stderr) {
openlog("rtpengine/child", LOG_PID | LOG_NDELAY, LOG_DAEMON);
}
ilog(LOG_INFO, "Initiating XMLRPC call for tag "STR_FORMAT"", STR_FMT(tag));
@ -376,19 +383,18 @@ fault:
g_slice_free1(sizeof(*xh), xh);
}
void kill_calls_timer(GSList *list, struct callmaster *m) {
void kill_calls_timer(GSList *list, const char *url) {
struct call *ca;
GList *csl;
struct call_monologue *cm;
const char *url, *url_prefix, *url_suffix;
const char *url_prefix, *url_suffix;
struct xmlrpc_helper *xh = NULL;
char url_buf[128];
if (!list)
return;
/* if m is NULL, it's the scheduled deletions, otherwise it's the timeouts */
url = m ? m->conf.b2b_url : NULL;
/* if url is NULL, it's the scheduled deletions, otherwise it's the timeouts */
if (url) {
xh = g_slice_alloc(sizeof(*xh));
xh->c = g_string_chunk_new(64);
@ -401,7 +407,7 @@ void kill_calls_timer(GSList *list, struct callmaster *m) {
else
url_suffix = g_string_chunk_insert(xh->c, url);
xh->tags_urls = NULL;
xh->fmt = m->conf.fmt;
xh->fmt = rtpe_config.fmt;
}
while (list) {
@ -420,7 +426,7 @@ void kill_calls_timer(GSList *list, struct callmaster *m) {
else
snprintf(url_buf, sizeof(url_buf), "%s", url_suffix);
switch (m->conf.fmt) {
switch (rtpe_config.fmt) {
case XF_SEMS:
for (csl = ca->monologues.head; csl; csl = csl->next) {
cm = csl->data;
@ -458,7 +464,7 @@ destroy:
else \
d = ke->stats.x - ks_val; \
atomic64_add(&ps->stats.x, d); \
atomic64_add(&m->statsps.x, d); \
atomic64_add(&rtpe_statsps.x, d); \
} while (0)
static void update_requests_per_second_stats(struct requests_ps *request, u_int64_t new_val) {
@ -478,8 +484,7 @@ static void update_requests_per_second_stats(struct requests_ps *request, u_int6
mutex_unlock(&request->lock);
}
static void callmaster_timer(void *ptr) {
struct callmaster *m = ptr;
static void call_timer(void *ptr) {
struct iterator_helper hlp;
GList *i, *l, *calls = NULL;
struct rtpengine_list_entry *ke;
@ -496,39 +501,39 @@ static void callmaster_timer(void *ptr) {
hlp.addr_sfd = g_hash_table_new(g_endpoint_hash, g_endpoint_eq);
/* obtain the call list and make a copy from it so not to hold the lock */
rwlock_lock_r(&m->hashlock);
l = g_hash_table_get_values(m->callhash);
rwlock_lock_r(&rtpe_callhash_lock);
l = g_hash_table_get_values(rtpe_callhash);
if (l) {
calls = g_list_copy(l);
g_list_free(l);
g_list_foreach(calls, call_obj_get, NULL);
}
rwlock_unlock_r(&m->hashlock);
rwlock_unlock_r(&rtpe_callhash_lock);
if (calls) {
g_list_foreach(calls, call_timer_iterator, &hlp);
g_list_free_full(calls, call_obj_put);
}
atomic64_local_copy_zero_struct(&tmpstats, &m->statsps, bytes);
atomic64_local_copy_zero_struct(&tmpstats, &m->statsps, packets);
atomic64_local_copy_zero_struct(&tmpstats, &m->statsps, errors);
atomic64_local_copy_zero_struct(&tmpstats, &rtpe_statsps, bytes);
atomic64_local_copy_zero_struct(&tmpstats, &rtpe_statsps, packets);
atomic64_local_copy_zero_struct(&tmpstats, &rtpe_statsps, errors);
atomic64_set(&m->stats.bytes, atomic64_get_na(&tmpstats.bytes));
atomic64_set(&m->stats.packets, atomic64_get_na(&tmpstats.packets));
atomic64_set(&m->stats.errors, atomic64_get_na(&tmpstats.errors));
atomic64_set(&rtpe_stats.bytes, atomic64_get_na(&tmpstats.bytes));
atomic64_set(&rtpe_stats.packets, atomic64_get_na(&tmpstats.packets));
atomic64_set(&rtpe_stats.errors, atomic64_get_na(&tmpstats.errors));
/* update statistics regarding requests per second */
offers = atomic64_get(&m->statsps.offers);
atomic64_set(&m->statsps.offers, 0);
update_requests_per_second_stats(&m->totalstats_interval.offers_ps, offers);
offers = atomic64_get(&rtpe_statsps.offers);
atomic64_set(&rtpe_statsps.offers, 0);
update_requests_per_second_stats(&rtpe_totalstats_interval.offers_ps, offers);
answers = atomic64_get(&m->statsps.answers);
atomic64_set(&m->statsps.answers, 0);
update_requests_per_second_stats(&m->totalstats_interval.answers_ps, answers);
answers = atomic64_get(&rtpe_statsps.answers);
atomic64_set(&rtpe_statsps.answers, 0);
update_requests_per_second_stats(&rtpe_totalstats_interval.answers_ps, answers);
deletes = atomic64_get(&m->statsps.deletes);
atomic64_set(&m->statsps.deletes, 0);
update_requests_per_second_stats(&m->totalstats_interval.deletes_ps, deletes);
deletes = atomic64_get(&rtpe_statsps.deletes);
atomic64_set(&rtpe_statsps.deletes, 0);
update_requests_per_second_stats(&rtpe_totalstats_interval.deletes_ps, deletes);
i = kernel_list();
while (i) {
@ -553,7 +558,7 @@ static void callmaster_timer(void *ptr) {
if (ke->stats.packets != atomic64_get(&ps->kernel_stats.packets))
atomic64_set(&ps->last_packet, poller_now);
atomic64_set(&ps->last_packet, rtpe_now.tv_sec);
ps->stats.in_tos_tclass = ke->stats.in_tos;
@ -613,7 +618,7 @@ static void callmaster_timer(void *ptr) {
rwlock_unlock_r(&sfd->call->master_lock);
if (update) {
redis_update_onekey(ps->call, m->conf.redis_write);
redis_update_onekey(ps->call, rtpe_redis_write);
}
next:
@ -631,57 +636,20 @@ next:
g_hash_table_destroy(hlp.addr_sfd);
kill_calls_timer(hlp.del_scheduled, NULL);
kill_calls_timer(hlp.del_timeout, m);
kill_calls_timer(hlp.del_timeout, rtpe_config.b2b_url);
}
#undef DS
struct callmaster *callmaster_new(struct poller *p) {
struct callmaster *c;
const char *errptr;
int erroff;
c = obj_alloc0("callmaster", sizeof(*c), NULL);
c->callhash = g_hash_table_new(str_hash, str_equal);
if (!c->callhash)
goto fail;
c->poller = p;
rwlock_init(&c->hashlock);
c->info_re = pcre_compile("^([^:,]+)(?::(.*?))?(?:$|,)", PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL);
if (!c->info_re)
goto fail;
c->info_ree = pcre_study(c->info_re, 0, &errptr);
c->streams_re = pcre_compile("^([\\d.]+):(\\d+)(?::(.*?))?(?:$|,)", PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL);
if (!c->streams_re)
goto fail;
c->streams_ree = pcre_study(c->streams_re, 0, &errptr);
poller_add_timer(p, callmaster_timer, &c->obj);
mutex_init(&c->totalstats.total_average_lock);
mutex_init(&c->totalstats_interval.total_average_lock);
mutex_init(&c->totalstats_interval.managed_sess_lock);
mutex_init(&c->totalstats_interval.total_calls_duration_lock);
c->totalstats.started = poller_now;
//c->totalstats_interval.managed_sess_min = 0; // already zeroed
//c->totalstats_interval.managed_sess_max = 0;
int call_init() {
rtpe_callhash = g_hash_table_new(str_hash, str_equal);
if (!rtpe_callhash)
return -1;
rwlock_init(&rtpe_callhash_lock);
mutex_init(&c->totalstats_lastinterval_lock);
mutex_init(&c->cngs_lock);
c->cngs_hash = g_hash_table_new(g_sockaddr_hash, g_sockaddr_eq);
poller_add_timer(rtpe_poller, call_timer, NULL);
mutex_init(&c->totalstats_interval.offers_ps.lock);
mutex_init(&c->totalstats_interval.answers_ps.lock);
mutex_init(&c->totalstats_interval.deletes_ps.lock);
return c;
fail:
obj_put(c);
return NULL;
return 0;
}
@ -717,7 +685,7 @@ static struct call_media *__get_media(struct call_monologue *ml, GList **it, con
med->call = ml->call;
med->index = sp->index;
call_str_cpy(ml->call, &med->type, &sp->type);
med->rtp_payload_types = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, __payload_type_free);
med->codecs = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, __payload_type_free);
g_queue_push_tail(&ml->medias, med);
@ -879,7 +847,7 @@ struct packet_stream *__packet_stream_new(struct call *call) {
mutex_init(&stream->in_lock);
mutex_init(&stream->out_lock);
stream->call = call;
atomic64_set_na(&stream->last_packet, poller_now);
atomic64_set_na(&stream->last_packet, rtpe_now.tv_sec);
stream->rtp_stats = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, __rtp_stats_free);
recording_init_stream(stream);
@ -1014,7 +982,7 @@ void __rtp_stats_update(GHashTable *dst, GHashTable *src) {
struct rtp_payload_type *pt;
GList *values, *l;
/* "src" is a call_media->rtp_payload_types table, while "dst" is a
/* "src" is a call_media->codecs table, while "dst" is a
* packet_stream->rtp_stats table */
values = g_hash_table_get_values(src);
@ -1052,7 +1020,7 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru
a->rtp_sink = b;
PS_SET(a, RTP); /* XXX technically not correct, could be udptl too */
__rtp_stats_update(a->rtp_stats, A->rtp_payload_types);
__rtp_stats_update(a->rtp_stats, A->codecs);
if (sp) {
__fill_stream(a, &sp->rtp_endpoint, port_off, sp);
@ -1395,7 +1363,7 @@ static void __tos_change(struct call *call, const struct sdp_ng_flags *flags) {
return;
if (!flags || flags->tos > 255)
new_tos = call->callmaster->conf.default_tos;
new_tos = rtpe_config.default_tos;
else
new_tos = flags->tos;
@ -1416,7 +1384,9 @@ static void __init_interface(struct call_media *media, const str *ifname, int nu
goto get;
if (!ifname || !ifname->s)
return;
if (!str_cmp_str(&media->logical_intf->name, ifname) || !str_cmp(ifname, ALGORITHM_ROUND_ROBIN_CALLS))
if (!str_cmp_str(&media->logical_intf->name, ifname))
return;
if (g_hash_table_lookup(media->logical_intf->rr_specs, ifname))
return;
get:
media->logical_intf = get_logical_interface(ifname, media->desired_family, num_ports);
@ -1481,18 +1451,56 @@ static void __dtls_logic(const struct sdp_ng_flags *flags,
MEDIA_SET(other_media, DTLS);
}
static void __rtp_payload_types(struct call_media *media, GQueue *types) {
struct rtp_payload_type *pt;
static void __rtp_payload_type_add(struct call_media *media, struct rtp_payload_type *pt) {
struct call *call = media->call;
/* we must duplicate the contents */
call_str_cpy(call, &pt->encoding_with_params, &pt->encoding_with_params);
call_str_cpy(call, &pt->encoding, &pt->encoding);
call_str_cpy(call, &pt->encoding_parameters, &pt->encoding_parameters);
call_str_cpy(call, &pt->format_parameters, &pt->format_parameters);
g_hash_table_replace(media->codecs, &pt->payload_type, pt);
g_queue_push_tail(&media->codecs_prefs, pt);
}
static void __rtp_payload_types(struct call_media *media, GQueue *types, GHashTable *strip,
const GQueue *offer)
{
struct rtp_payload_type *pt;
static const str str_all = STR_CONST_INIT("all");
GHashTable *removed = g_hash_table_new_full(str_hash, str_equal, NULL, __payload_type_free);
int remove_all = 0;
// start fresh
g_queue_clear(&media->codecs_prefs);
if (strip && g_hash_table_lookup(strip, &str_all))
remove_all = 1;
/* we steal the entire list to avoid duplicate allocs */
while ((pt = g_queue_pop_head(types))) {
/* but we must duplicate the contents */
call_str_cpy(call, &pt->encoding_with_params, &pt->encoding_with_params);
call_str_cpy(call, &pt->encoding, &pt->encoding);
call_str_cpy(call, &pt->encoding_parameters, &pt->encoding_parameters);
g_hash_table_replace(media->rtp_payload_types, &pt->payload_type, pt);
// codec stripping
if (strip) {
if (remove_all || g_hash_table_lookup(strip, &pt->encoding)) {
g_hash_table_replace(removed, &pt->encoding, pt);
continue;
}
}
__rtp_payload_type_add(media, pt);
}
if (offer) {
// now restore codecs that have been removed, but should be offered
for (GList *l = offer->head; l; l = l->next) {
str *codec = l->data;
pt = g_hash_table_lookup(removed, codec);
if (!pt)
continue;
g_hash_table_steal(removed, codec);
__rtp_payload_type_add(media, pt);
}
}
g_hash_table_destroy(removed);
}
static void __ice_start(struct call_media *media) {
@ -1506,33 +1514,6 @@ static void __ice_start(struct call_media *media) {
ice_agent_init(&media->ice_agent, media);
}
static int get_algorithm_num_ports(GQueue *streams, char *algorithm) {
unsigned int algorithm_ports = 0;
struct stream_params *sp;
GList *media_iter;
if (algorithm == NULL) {
return 0;
}
for (media_iter = streams->head; media_iter; media_iter = media_iter->next) {
sp = media_iter->data;
if (!str_cmp(&sp->direction[0], algorithm)) {
algorithm_ports += sp->consecutive_ports;
}
if (!str_cmp(&sp->direction[1], algorithm)) {
algorithm_ports += sp->consecutive_ports;
}
}
// XXX only do *=2 for RTP streams?
algorithm_ports *= 2;
return algorithm_ports;
}
static void __endpoint_loop_protect(struct stream_params *sp, struct call_media *media) {
struct intf_address intf_addr;
@ -1564,7 +1545,6 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
GList *media_iter, *ml_media, *other_ml_media;
struct call_media *media, *other_media;
unsigned int num_ports;
unsigned int rr_calls_ports;
struct call_monologue *monologue;
struct endpoint_map *em;
struct call *call;
@ -1579,12 +1559,9 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
monologue = other_ml->active_dialogue;
call = monologue->call;
call->last_signal = poller_now;
call->last_signal = rtpe_now.tv_sec;
call->deleted = 0;
// get the total number of ports needed for ALGORITHM_ROUND_ROBIN_CALLS algorithm
rr_calls_ports = get_algorithm_num_ports(streams, ALGORITHM_ROUND_ROBIN_CALLS);
__C_DBG("this="STR_FORMAT" other="STR_FORMAT, STR_FMT(&monologue->tag), STR_FMT(&other_ml->tag));
__tos_change(call, flags);
@ -1594,7 +1571,6 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
for (media_iter = streams->head; media_iter; media_iter = media_iter->next) {
sp = media_iter->data;
__C_DBG("processing media stream #%u", sp->index);
__C_DBG("free ports needed for round-robin-calls, left for this call %u", rr_calls_ports);
/* first, check for existence of call_media struct on both sides of
* the dialogue */
@ -1647,7 +1623,7 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
MEDIA_SET(other_media, SDES);
}
__rtp_payload_types(media, &sp->rtp_payload_types);
__rtp_payload_types(media, &sp->rtp_payload_types, flags->codec_strip, &flags->codec_offer);
/* send and recv are from our POV */
bf_copy_same(&media->media_flags, &sp->sp_flags,
@ -1674,9 +1650,15 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
media->desired_family = sp->desired_family;
}
/* determine number of consecutive ports needed locally.
* XXX only do *=2 for RTP streams? */
num_ports = sp->consecutive_ports;
num_ports *= 2;
/* local interface selection */
__init_interface(media, &sp->direction[1], rr_calls_ports);
__init_interface(other_media, &sp->direction[0], rr_calls_ports);
__init_interface(media, &sp->direction[1], num_ports);
__init_interface(other_media, &sp->direction[0], num_ports);
if (media->logical_intf == NULL || other_media->logical_intf == NULL) {
goto error_intf;
@ -1693,12 +1675,6 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
MEDIA_SET(other_media, INITIALIZED);
/* determine number of consecutive ports needed locally.
* XXX only do *=2 for RTP streams? */
num_ports = sp->consecutive_ports;
num_ports *= 2;
if (!sp->rtp_endpoint.port) {
/* Zero port: stream has been rejected.
* RFC 3264, chapter 6:
@ -1721,11 +1697,6 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
em = __get_endpoint_map(media, num_ports, &sp->rtp_endpoint, flags);
if (!em) {
goto error_ports;
} else {
// update the ports needed for ALGORITHM_ROUND_ROBIN_CALLS algorithm
if (str_cmp(&sp->direction[1], ALGORITHM_ROUND_ROBIN_CALLS) == 0) {
rr_calls_ports -= num_ports;
}
}
__num_media_streams(media, num_ports);
@ -1737,11 +1708,6 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
* when the answer comes. */
if (__wildcard_endpoint_map(other_media, num_ports))
goto error_ports;
// update the ports needed for ALGORITHM_ROUND_ROBIN_CALLS algorithm
if (str_cmp(&sp->direction[0], ALGORITHM_ROUND_ROBIN_CALLS) == 0) {
rr_calls_ports -= num_ports;
}
}
init:
@ -1803,35 +1769,35 @@ const struct rtp_payload_type *__rtp_stats_codec(struct call_media *m) {
if (atomic64_get(&rtp_s->packets) == 0)
goto out;
rtp_pt = rtp_payload_type(rtp_s->payload_type, m->rtp_payload_types);
rtp_pt = rtp_payload_type(rtp_s->payload_type, m->codecs);
out:
g_list_free(values);
return rtp_pt; /* may be NULL */
}
void add_total_calls_duration_in_interval(struct callmaster *cm,
struct timeval *interval_tv) {
struct timeval ongoing_calls_dur = add_ongoing_calls_dur_in_interval(cm,
&cm->latest_graphite_interval_start, interval_tv);
void add_total_calls_duration_in_interval(struct timeval *interval_tv) {
struct timeval ongoing_calls_dur = add_ongoing_calls_dur_in_interval(
&rtpe_latest_graphite_interval_start, interval_tv);
mutex_lock(&cm->totalstats_interval.total_calls_duration_lock);
timeval_add(&cm->totalstats_interval.total_calls_duration_interval,
&cm->totalstats_interval.total_calls_duration_interval,
mutex_lock(&rtpe_totalstats_interval.total_calls_duration_lock);
timeval_add(&rtpe_totalstats_interval.total_calls_duration_interval,
&rtpe_totalstats_interval.total_calls_duration_interval,
&ongoing_calls_dur);
mutex_unlock(&cm->totalstats_interval.total_calls_duration_lock);
mutex_unlock(&rtpe_totalstats_interval.total_calls_duration_lock);
}
static struct timeval add_ongoing_calls_dur_in_interval(struct callmaster *m,
struct timeval *interval_start, struct timeval *interval_duration) {
static struct timeval add_ongoing_calls_dur_in_interval(struct timeval *interval_start,
struct timeval *interval_duration)
{
GHashTableIter iter;
gpointer key, value;
struct timeval call_duration, res = {0};
struct call *call;
struct call_monologue *ml;
rwlock_lock_r(&m->hashlock);
g_hash_table_iter_init(&iter, m->callhash);
rwlock_lock_r(&rtpe_callhash_lock);
g_hash_table_iter_init(&iter, rtpe_callhash);
while (g_hash_table_iter_next(&iter, &key, &value)) {
call = (struct call*) value;
@ -1841,20 +1807,18 @@ static struct timeval add_ongoing_calls_dur_in_interval(struct callmaster *m,
if (timercmp(interval_start, &ml->started, >)) {
timeval_add(&res, &res, interval_duration);
} else {
timeval_subtract(&call_duration, &g_now, &ml->started);
timeval_subtract(&call_duration, &rtpe_now, &ml->started);
timeval_add(&res, &res, &call_duration);
}
}
rwlock_unlock_r(&m->hashlock);
rwlock_unlock_r(&rtpe_callhash_lock);
return res;
}
/* called lock-free, but must hold a reference to the call */
void call_destroy(struct call *c) {
struct callmaster *m;
struct packet_stream *ps=0;
struct stream_fd *sfd;
struct poller *p;
GList *l;
int ret;
struct call_monologue *ml;
@ -1866,14 +1830,11 @@ void call_destroy(struct call *c) {
return;
}
m = c->callmaster;
p = m->poller;
rwlock_lock_w(&m->hashlock);
ret = (g_hash_table_lookup(m->callhash, &c->callid) == c);
rwlock_lock_w(&rtpe_callhash_lock);
ret = (g_hash_table_lookup(rtpe_callhash, &c->callid) == c);
if (ret)
g_hash_table_remove(m->callhash, &c->callid);
rwlock_unlock_w(&m->hashlock);
g_hash_table_remove(rtpe_callhash, &c->callid);
rwlock_unlock_w(&rtpe_callhash_lock);
// if call not found in callhash => previously deleted
if (!ret)
@ -1885,7 +1846,7 @@ void call_destroy(struct call *c) {
statistics_update_foreignown_dec(c);
if (IS_OWN_CALL(c)) {
redis_delete(c, m->conf.redis_write);
redis_delete(c, rtpe_redis_write);
}
rwlock_lock_w(&c->master_lock);
@ -1905,8 +1866,8 @@ void call_destroy(struct call *c) {
ml->label.s ? " (label '" : "",
STR_FMT(ml->label.s ? &ml->label : &STR_EMPTY),
ml->label.s ? "')" : "",
(unsigned int) (poller_now - ml->created) / 60,
(unsigned int) (poller_now - ml->created) % 60,
(unsigned int) (rtpe_now.tv_sec - ml->created) / 60,
(unsigned int) (rtpe_now.tv_sec - ml->created) % 60,
STR_FMT(&ml->viabranch),
ml->active_dialogue ? ml->active_dialogue->tag.len : 6,
ml->active_dialogue ? ml->active_dialogue->tag.s : "(none)");
@ -1945,9 +1906,9 @@ void call_destroy(struct call *c) {
atomic64_get(&ps->stats.packets),
atomic64_get(&ps->stats.bytes),
atomic64_get(&ps->stats.errors),
g_now.tv_sec - atomic64_get(&ps->last_packet));
rtpe_now.tv_sec - atomic64_get(&ps->last_packet));
statistics_update_totals(m,ps);
statistics_update_totals(ps);
}
@ -1999,7 +1960,7 @@ no_stats_output:
while (c->stream_fds.head) {
sfd = g_queue_pop_head(&c->stream_fds);
poller_del_item(p, sfd->socket.fd);
poller_del_item(rtpe_poller, sfd->socket.fd);
obj_put(sfd);
}
@ -2071,7 +2032,8 @@ static void __call_free(void *p) {
crypto_params_cleanup(&md->sdes_out.params);
g_queue_clear(&md->streams);
g_queue_clear(&md->endpoint_maps);
g_hash_table_destroy(md->rtp_payload_types);
g_hash_table_destroy(md->codecs);
g_queue_clear(&md->codecs_prefs);
g_slice_free1(sizeof(*md), md);
}
@ -2097,58 +2059,57 @@ static void __call_free(void *p) {
assert(c->stream_fds.head == NULL);
}
static struct call *call_create(const str *callid, struct callmaster *m) {
static struct call *call_create(const str *callid) {
struct call *c;
ilog(LOG_NOTICE, "Creating new call");
c = obj_alloc0("call", sizeof(*c), __call_free);
c->callmaster = m;
mutex_init(&c->buffer_lock);
call_buffer_init(&c->buffer);
rwlock_init(&c->master_lock);
c->tags = g_hash_table_new(str_hash, str_equal);
c->viabranches = g_hash_table_new(str_hash, str_equal);
call_str_cpy(c, &c->callid, callid);
c->created = g_now;
c->created = rtpe_now;
c->dtls_cert = dtls_cert();
c->tos = m->conf.default_tos;
c->tos = rtpe_config.default_tos;
c->ssrc_hash = create_ssrc_hash();
return c;
}
/* returns call with master_lock held in W */
struct call *call_get_or_create(const str *callid, struct callmaster *m, enum call_type type) {
struct call *call_get_or_create(const str *callid, enum call_type type) {
struct call *c;
restart:
rwlock_lock_r(&m->hashlock);
c = g_hash_table_lookup(m->callhash, callid);
rwlock_lock_r(&rtpe_callhash_lock);
c = g_hash_table_lookup(rtpe_callhash, callid);
if (!c) {
rwlock_unlock_r(&m->hashlock);
rwlock_unlock_r(&rtpe_callhash_lock);
/* completely new call-id, create call */
c = call_create(callid, m);
rwlock_lock_w(&m->hashlock);
if (g_hash_table_lookup(m->callhash, callid)) {
c = call_create(callid);
rwlock_lock_w(&rtpe_callhash_lock);
if (g_hash_table_lookup(rtpe_callhash, callid)) {
/* preempted */
rwlock_unlock_w(&m->hashlock);
rwlock_unlock_w(&rtpe_callhash_lock);
obj_put(c);
goto restart;
}
g_hash_table_insert(m->callhash, &c->callid, obj_get(c));
g_hash_table_insert(rtpe_callhash, &c->callid, obj_get(c));
if (type == CT_FOREIGN_CALL) /* foreign call*/
c->foreign_call = 1;
statistics_update_foreignown_inc(m,c);
statistics_update_foreignown_inc(c);
rwlock_lock_w(&c->master_lock);
rwlock_unlock_w(&m->hashlock);
rwlock_unlock_w(&rtpe_callhash_lock);
}
else {
obj_hold(c);
rwlock_lock_w(&c->master_lock);
rwlock_unlock_r(&m->hashlock);
rwlock_unlock_r(&rtpe_callhash_lock);
}
log_info_call(c);
@ -2156,29 +2117,29 @@ restart:
}
/* returns call with master_lock held in W, or NULL if not found */
struct call *call_get(const str *callid, struct callmaster *m) {
struct call *call_get(const str *callid) {
struct call *ret;
rwlock_lock_r(&m->hashlock);
ret = g_hash_table_lookup(m->callhash, callid);
rwlock_lock_r(&rtpe_callhash_lock);
ret = g_hash_table_lookup(rtpe_callhash, callid);
if (!ret) {
rwlock_unlock_r(&m->hashlock);
rwlock_unlock_r(&rtpe_callhash_lock);
return NULL;
}
rwlock_lock_w(&ret->master_lock);
obj_hold(ret);
rwlock_unlock_r(&m->hashlock);
rwlock_unlock_r(&rtpe_callhash_lock);
log_info_call(ret);
return ret;
}
/* returns call with master_lock held in W, or possibly NULL iff opmode == OP_ANSWER */
struct call *call_get_opmode(const str *callid, struct callmaster *m, enum call_opmode opmode) {
struct call *call_get_opmode(const str *callid, enum call_opmode opmode) {
if (opmode == OP_OFFER)
return call_get_or_create(callid, m, CT_OWN_CALL);
return call_get(callid, m);
return call_get_or_create(callid, CT_OWN_CALL);
return call_get(callid);
}
/* must be called with call->master_lock held in W */
@ -2189,7 +2150,7 @@ struct call_monologue *__monologue_create(struct call *call) {
ret = uid_slice_alloc0(ret, &call->monologues);
ret->call = call;
ret->created = poller_now;
ret->created = rtpe_now.tv_sec;
ret->other_tags = g_hash_table_new(str_hash, str_equal);
g_queue_init(&ret->medias);
@ -2447,7 +2408,7 @@ struct call_monologue *call_get_mono_dialogue(struct call *call, const str *from
}
int call_delete_branch(struct callmaster *m, const str *callid, const str *branch,
int call_delete_branch(const str *callid, const str *branch,
const str *fromtag, const str *totag, bencode_item_t *output, int delete_delay)
{
struct call *c;
@ -2457,9 +2418,9 @@ int call_delete_branch(struct callmaster *m, const str *callid, const str *branc
GList *i;
if (delete_delay < 0)
delete_delay = m->conf.delete_delay;
delete_delay = rtpe_config.delete_delay;
c = call_get(callid, m);
c = call_get(callid);
if (!c) {
ilog(LOG_INFO, "Call-ID to delete not found");
goto err;
@ -2513,7 +2474,7 @@ do_delete:
ilog(LOG_INFO, "Scheduling deletion of call branch '"STR_FORMAT"' "
"(via-branch '"STR_FORMAT"') in %d seconds",
STR_FMT(&ml->tag), STR_FMT0(branch), delete_delay);
ml->deleted = poller_now + delete_delay;
ml->deleted = rtpe_now.tv_sec + delete_delay;
if (!c->ml_deleted || c->ml_deleted > ml->deleted)
c->ml_deleted = ml->deleted;
}
@ -2528,7 +2489,7 @@ do_delete:
del_all:
if (delete_delay > 0) {
ilog(LOG_INFO, "Scheduling deletion of entire call in %d seconds", delete_delay);
c->deleted = poller_now + delete_delay;
c->deleted = rtpe_now.tv_sec + delete_delay;
rwlock_unlock_w(&c->master_lock);
}
else {
@ -2557,63 +2518,18 @@ out:
}
static void callmaster_get_all_calls_interator(void *key, void *val, void *ptr) {
static void call_get_all_calls_interator(void *key, void *val, void *ptr) {
GQueue *q = ptr;
g_queue_push_tail(q, obj_get_o(val));
}
void callmaster_get_all_calls(struct callmaster *m, GQueue *q) {
rwlock_lock_r(&m->hashlock);
g_hash_table_foreach(m->callhash, callmaster_get_all_calls_interator, q);
rwlock_unlock_r(&m->hashlock);
}
#if 0
// unused
// simplifty redis_write <> redis if put back into use
static void calls_dump_iterator(void *key, void *val, void *ptr) {
struct call *c = val;
struct callmaster *m = c->callmaster;
if (m->conf.redis_write) {
redis_update(c, m->conf.redis_write);
} else if (m->conf.redis) {
redis_update(c, m->conf.redis);
}
}
void calls_dump_redis(struct callmaster *m) {
if (!m->conf.redis)
return;
ilog(LOG_DEBUG, "Start dumping all call data to Redis...\n");
redis_wipe(m->conf.redis);
g_hash_table_foreach(m->callhash, calls_dump_iterator, NULL);
ilog(LOG_DEBUG, "Finished dumping all call data to Redis\n");
}
void calls_dump_redis_read(struct callmaster *m) {
if (!m->conf.redis_read)
return;
void call_get_all_calls(GQueue *q) {
rwlock_lock_r(&rtpe_callhash_lock);
g_hash_table_foreach(rtpe_callhash, call_get_all_calls_interator, q);
rwlock_unlock_r(&rtpe_callhash_lock);
ilog(LOG_DEBUG, "Start dumping all call data to read Redis...\n");
redis_wipe(m->conf.redis_read);
g_hash_table_foreach(m->callhash, calls_dump_iterator, NULL);
ilog(LOG_DEBUG, "Finished dumping all call data to read Redis\n");
}
void calls_dump_redis_write(struct callmaster *m) {
if (!m->conf.redis_write)
return;
ilog(LOG_DEBUG, "Start dumping all call data to write Redis...\n");
redis_wipe(m->conf.redis_write);
g_hash_table_foreach(m->callhash, calls_dump_iterator, NULL);
ilog(LOG_DEBUG, "Finished dumping all call data to write Redis\n");
}
#endif
const struct transport_protocol *transport_protocol(const str *s) {
int i;

@ -61,11 +61,6 @@ enum transport_protocol_index {
__PROTO_LAST,
};
enum xmlrpc_format {
XF_SEMS = 0,
XF_CALLID,
};
enum call_stream_state {
CSS_UNKNOWN = 0,
CSS_SHUTDOWN,
@ -327,7 +322,9 @@ struct call_media {
GQueue streams; /* normally RTP + RTCP */
GQueue endpoint_maps;
GHashTable *rtp_payload_types;
GHashTable *codecs;
GQueue codecs_prefs;
volatile unsigned int media_flags;
};
@ -355,8 +352,6 @@ struct call_monologue {
struct call {
struct obj obj;
struct callmaster *callmaster; /* RO */
mutex_t buffer_lock;
call_buffer_t buffer;
@ -387,78 +382,31 @@ struct call {
struct recording *recording;
};
struct callmaster_config {
/* everything below protected by config_lock */
rwlock_t config_lock;
int max_sessions;
unsigned int timeout;
unsigned int silent_timeout;
unsigned int final_timeout;
unsigned int delete_delay;
struct redis *redis;
struct redis *redis_write;
struct redis *redis_notify;
struct event_base *redis_notify_event_base;
GQueue *redis_subscribed_keyspaces;
struct redisAsyncContext *redis_notify_async_context;
unsigned int redis_expires_secs;
char *b2b_url;
unsigned char default_tos;
enum xmlrpc_format fmt;
endpoint_t graphite_ep;
int graphite_interval;
int redis_num_threads;
};
struct callmaster {
struct obj obj;
rwlock_t hashlock;
GHashTable *callhash;
/* XXX rework these */
struct stats statsps; /* per second stats, running timer */
struct stats stats; /* copied from statsps once a second */
struct totalstats totalstats;
struct totalstats totalstats_interval;
mutex_t totalstats_lastinterval_lock;
struct totalstats totalstats_lastinterval;
/* control_ng_stats stuff */
mutex_t cngs_lock;
GHashTable *cngs_hash;
struct poller *poller;
pcre *info_re;
pcre_extra *info_ree;
pcre *streams_re;
pcre_extra *streams_ree;
struct callmaster_config conf;
struct timeval latest_graphite_interval_start;
};
extern rwlock_t rtpe_callhash_lock;
extern GHashTable *rtpe_callhash;
extern struct stats rtpe_statsps; /* per second stats, running timer */
extern struct stats rtpe_stats; /* copied from statsps once a second */
struct callmaster *callmaster_new(struct poller *);
void callmaster_get_all_calls(struct callmaster *m, GQueue *q);
int call_init(void);
void call_get_all_calls(GQueue *q);
//void calls_dump_redis(struct callmaster *);
//void calls_dump_redis_read(struct callmaster *);
//void calls_dump_redis_write(struct callmaster *);
struct call_monologue *__monologue_create(struct call *call);
void __monologue_tag(struct call_monologue *ml, const str *tag);
void __monologue_viabranch(struct call_monologue *ml, const str *viabranch);
struct packet_stream *__packet_stream_new(struct call *call);
struct call *call_get_or_create(const str *callid, struct callmaster *m, enum call_type);
struct call *call_get_opmode(const str *callid, struct callmaster *m, enum call_opmode opmode);
struct call *call_get_or_create(const str *callid, enum call_type);
struct call *call_get_opmode(const str *callid, enum call_opmode opmode);
struct call_monologue *call_get_mono_dialogue(struct call *call, const str *fromtag, const str *totag,
const str *viabranch);
struct call *call_get(const str *callid, struct callmaster *m);
struct call *call_get(const str *callid);
int monologue_offer_answer(struct call_monologue *monologue, GQueue *streams, const struct sdp_ng_flags *flags);
int call_delete_branch(struct callmaster *m, const str *callid, const str *branch,
int call_delete_branch(const str *callid, const str *branch,
const str *fromtag, const str *totag, bencode_item_t *output, int delete_delay);
void call_destroy(struct call *);
enum call_stream_state call_stream_state_machine(struct packet_stream *);
@ -469,7 +417,7 @@ int call_stream_address46(char *o, struct packet_stream *ps, enum stream_address
int *len, const struct local_intf *ifa, int keep_unspec);
const struct transport_protocol *transport_protocol(const str *s);
void add_total_calls_duration_in_interval(struct callmaster *cm, struct timeval *interval_tv);
void add_total_calls_duration_in_interval(struct timeval *interval_tv);
void __payload_type_free(void *p);
void __rtp_stats_update(GHashTable *dst, GHashTable *src);

@ -22,8 +22,15 @@
#include "recording.h"
#include "rtplib.h"
#include "ssrc.h"
#include "tcp_listener.h"
#include "streambuf.h"
#include "main.h"
static pcre *info_re;
static pcre_extra *info_ree;
static pcre *streams_re;
static pcre_extra *streams_ree;
int trust_address_def;
int dtls_passive_def;
@ -135,7 +142,7 @@ fail:
return -1;
}
static str *call_update_lookup_udp(char **out, struct callmaster *m, enum call_opmode opmode, const char* addr,
static str *call_update_lookup_udp(char **out, enum call_opmode opmode, const char* addr,
const endpoint_t *sin)
{
struct call *c;
@ -152,7 +159,7 @@ static str *call_update_lookup_udp(char **out, struct callmaster *m, enum call_o
if (opmode == OP_ANSWER)
str_swap(&fromtag, &totag);
c = call_get_opmode(&callid, m, opmode);
c = call_get_opmode(&callid, opmode);
if (!c) {
ilog(LOG_WARNING, "["STR_FORMAT"] Got UDP LOOKUP for unknown call-id",
STR_FMT(&callid));
@ -188,7 +195,7 @@ static str *call_update_lookup_udp(char **out, struct callmaster *m, enum call_o
sp.index, sp.index, out[RE_UDP_COOKIE], SAF_UDP);
rwlock_unlock_w(&c->master_lock);
redis_update_onekey(c, m->conf.redis_write);
redis_update_onekey(c, rtpe_redis_write);
gettimeofday(&(monologue->started), NULL);
@ -212,11 +219,11 @@ out:
return ret;
}
str *call_update_udp(char **out, struct callmaster *m, const char* addr, const endpoint_t *sin) {
return call_update_lookup_udp(out, m, OP_OFFER, addr, sin);
str *call_update_udp(char **out, const char* addr, const endpoint_t *sin) {
return call_update_lookup_udp(out, OP_OFFER, addr, sin);
}
str *call_lookup_udp(char **out, struct callmaster *m) {
return call_update_lookup_udp(out, m, OP_ANSWER, NULL, NULL);
str *call_lookup_udp(char **out) {
return call_update_lookup_udp(out, OP_ANSWER, NULL, NULL);
}
@ -228,8 +235,8 @@ static int info_parse_func(char **a, void **ret, void *p) {
return -1;
}
static void info_parse(const char *s, GHashTable *ih, struct callmaster *m) {
pcre_multi_match(m->info_re, m->info_ree, s, 2, info_parse_func, ih, NULL);
static void info_parse(const char *s, GHashTable *ih) {
pcre_multi_match(info_re, info_ree, s, 2, info_parse_func, ih, NULL);
}
@ -266,10 +273,10 @@ fail:
}
static void streams_parse(const char *s, struct callmaster *m, GQueue *q) {
static void streams_parse(const char *s, GQueue *q) {
int i;
i = 0;
pcre_multi_match(m->streams_re, m->streams_ree, s, 3, streams_parse_func, &i, q);
pcre_multi_match(streams_re, streams_ree, s, 3, streams_parse_func, &i, q);
}
/* XXX move these somewhere else */
@ -291,7 +298,7 @@ static void streams_free(GQueue *q) {
static str *call_request_lookup_tcp(char **out, struct callmaster *m, enum call_opmode opmode) {
static str *call_request_lookup_tcp(char **out, enum call_opmode opmode) {
struct call *c;
struct call_monologue *monologue;
GQueue s = G_QUEUE_INIT;
@ -300,14 +307,14 @@ static str *call_request_lookup_tcp(char **out, struct callmaster *m, enum call_
str_init(&callid, out[RE_TCP_RL_CALLID]);
infohash = g_hash_table_new_full(g_str_hash, g_str_equal, free, free);
c = call_get_opmode(&callid, m, opmode);
c = call_get_opmode(&callid, opmode);
if (!c) {
ilog(LOG_WARNING, "["STR_FORMAT"] Got LOOKUP for unknown call-id", STR_FMT(&callid));
goto out;
}
info_parse(out[RE_TCP_RL_INFO], infohash, m);
streams_parse(out[RE_TCP_RL_STREAMS], m, &s);
info_parse(out[RE_TCP_RL_INFO], infohash);
streams_parse(out[RE_TCP_RL_STREAMS], &s);
str_init(&fromtag, g_hash_table_lookup(infohash, "fromtag"));
if (!fromtag.s) {
ilog(LOG_WARNING, "No from-tag in message");
@ -336,7 +343,7 @@ out2:
rwlock_unlock_w(&c->master_lock);
streams_free(&s);
redis_update_onekey(c, m->conf.redis_write);
redis_update_onekey(c, rtpe_redis_write);
ilog(LOG_INFO, "Returning to SIP proxy: "STR_FORMAT"", STR_FMT0(ret));
obj_put(c);
@ -346,14 +353,14 @@ out:
return ret;
}
str *call_request_tcp(char **out, struct callmaster *m) {
return call_request_lookup_tcp(out, m, OP_OFFER);
str *call_request_tcp(char **out) {
return call_request_lookup_tcp(out, OP_OFFER);
}
str *call_lookup_tcp(char **out, struct callmaster *m) {
return call_request_lookup_tcp(out, m, OP_ANSWER);
str *call_lookup_tcp(char **out) {
return call_request_lookup_tcp(out, OP_ANSWER);
}
str *call_delete_udp(char **out, struct callmaster *m) {
str *call_delete_udp(char **out) {
str callid, branch, fromtag, totag;
__C_DBG("got delete for callid '%s' and viabranch '%s'",
@ -364,12 +371,12 @@ str *call_delete_udp(char **out, struct callmaster *m) {
str_init(&fromtag, out[RE_UDP_DQ_FROMTAG]);
str_init(&totag, out[RE_UDP_DQ_TOTAG]);
if (call_delete_branch(m, &callid, &branch, &fromtag, &totag, NULL, -1))
if (call_delete_branch(&callid, &branch, &fromtag, &totag, NULL, -1))
return str_sprintf("%s E8\n", out[RE_UDP_COOKIE]);
return str_sprintf("%s 0\n", out[RE_UDP_COOKIE]);
}
str *call_query_udp(char **out, struct callmaster *m) {
str *call_query_udp(char **out) {
struct call *c;
str *ret, callid, fromtag, totag;
struct call_stats stats;
@ -380,7 +387,7 @@ str *call_query_udp(char **out, struct callmaster *m) {
str_init(&fromtag, out[RE_UDP_DQ_FROMTAG]);
str_init(&totag, out[RE_UDP_DQ_TOTAG]);
c = call_get_opmode(&callid, m, OP_OTHER);
c = call_get_opmode(&callid, OP_OTHER);
if (!c) {
ilog(LOG_INFO, "["STR_FORMAT"] Call-ID to query not found", STR_FMT(&callid));
goto err;
@ -390,12 +397,12 @@ str *call_query_udp(char **out, struct callmaster *m) {
rwlock_unlock_w(&c->master_lock);
rwlock_lock_r(&m->conf.config_lock);
rwlock_lock_r(&rtpe_config.config_lock);
ret = str_sprintf("%s %lld "UINT64F" "UINT64F" "UINT64F" "UINT64F"\n", out[RE_UDP_COOKIE],
(long long int) m->conf.silent_timeout - (poller_now - stats.last_packet),
(long long int) rtpe_config.silent_timeout - (rtpe_now.tv_sec - stats.last_packet),
atomic64_get_na(&stats.totals[0].packets), atomic64_get_na(&stats.totals[1].packets),
atomic64_get_na(&stats.totals[2].packets), atomic64_get_na(&stats.totals[3].packets));
rwlock_unlock_r(&m->conf.config_lock);
rwlock_unlock_r(&rtpe_config.config_lock);
goto out;
err:
@ -410,43 +417,41 @@ out:
return ret;
}
void call_delete_tcp(char **out, struct callmaster *m) {
void call_delete_tcp(char **out) {
str callid;
str_init(&callid, out[RE_TCP_D_CALLID]);
call_delete_branch(m, &callid, NULL, NULL, NULL, NULL, -1);
call_delete_branch(&callid, NULL, NULL, NULL, NULL, -1);
}
static void call_status_iterator(struct call *c, struct control_stream *s) {
static void call_status_iterator(struct call *c, struct streambuf_stream *s) {
// GList *l;
// struct callstream *cs;
// struct peer *p;
// struct streamrelay *r1, *r2;
// struct streamrelay *rx1, *rx2;
// struct callmaster *m;
// char addr1[64], addr2[64], addr3[64];
// m = c->callmaster;
// mutex_lock(&c->master_lock);
control_stream_printf(s, "session "STR_FORMAT" - - - - %lli\n",
streambuf_printf(s->outbuf, "session "STR_FORMAT" - - - - %lli\n",
STR_FMT(&c->callid),
timeval_diff(&g_now, &c->created) / 1000000);
timeval_diff(&rtpe_now, &c->created) / 1000000);
/* XXX restore function */
// mutex_unlock(&c->master_lock);
}
void calls_status_tcp(struct callmaster *m, struct control_stream *s) {
void calls_status_tcp(struct streambuf_stream *s) {
GQueue q = G_QUEUE_INIT;
struct call *c;
callmaster_get_all_calls(m, &q);
call_get_all_calls(&q);
control_stream_printf(s, "proxy %u "UINT64F"/%i/%i\n",
streambuf_printf(s->outbuf, "proxy %u "UINT64F"/%i/%i\n",
g_queue_get_length(&q),
atomic64_get(&m->stats.bytes), 0, 0);
atomic64_get(&rtpe_stats.bytes), 0, 0);
while (q.head) {
c = g_queue_pop_head(&q);
@ -476,13 +481,11 @@ INLINE void call_bencode_hold_ref(struct call *c, bencode_item_t *bi) {
bencode_buffer_destroy_add(bi->buffer, call_release_ref, obj_get(c));
}
INLINE void str_hyphenate(bencode_item_t *it) {
INLINE void str_hyphenate(str *s_ori) {
str s;
if (!bencode_get_str(it, &s))
return;
s = *s_ori;
while (s.len) {
str_chr_str(&s, &s, ' ');
if (!s.s || !s.len)
if (!str_chr_str(&s, &s, ' '))
break;
*s.s = '-';
str_shift(&s, 1);
@ -495,90 +498,142 @@ INLINE char *bencode_get_alt(bencode_item_t *i, const char *one, const char *two
return bencode_dictionary_get_str(i, two, out);
}
INLINE void ng_sdes_option(struct sdp_ng_flags *out, bencode_item_t *it, unsigned int strip) {
str s;
if (!bencode_get_str(it, &s))
return;
str_shift(&s, strip);
if (!str_cmp(&s, "no") || !str_cmp(&s, "off") || !str_cmp(&s, "disabled")
|| !str_cmp(&s, "disable"))
INLINE void ng_sdes_option(struct sdp_ng_flags *out, str *s, void *dummy) {
if (!str_cmp(s, "no") || !str_cmp(s, "off") || !str_cmp(s, "disabled")
|| !str_cmp(s, "disable"))
out->sdes_off = 1;
else if (!str_cmp(&s, "unencrypted_srtp") || !str_cmp(&s, "UNENCRYPTED_SRTP"))
else if (!str_cmp(s, "unencrypted_srtp") || !str_cmp(s, "UNENCRYPTED_SRTP"))
out->sdes_unencrypted_srtp = 1;
else if (!str_cmp(&s, "unencrypted_srtcp") || !str_cmp(&s, "UNENCRYPTED_SRTCP"))
else if (!str_cmp(s, "unencrypted_srtcp") || !str_cmp(s, "UNENCRYPTED_SRTCP"))
out->sdes_unencrypted_srtcp = 1;
else if (!str_cmp(&s, "unauthenticated_srtp") || !str_cmp(&s, "UNAUTHENTICATED_SRTP"))
else if (!str_cmp(s, "unauthenticated_srtp") || !str_cmp(s, "UNAUTHENTICATED_SRTP"))
out->sdes_unauthenticated_srtp = 1;
else if (!str_cmp(&s, "encrypted_srtp") || !str_cmp(&s, "ENCRYPTED_SRTP"))
else if (!str_cmp(s, "encrypted_srtp") || !str_cmp(s, "ENCRYPTED_SRTP"))
out->sdes_encrypted_srtp = 1;
else if (!str_cmp(&s, "encrypted_srtcp") || !str_cmp(&s, "ENCRYPTED_SRTCP"))
else if (!str_cmp(s, "encrypted_srtcp") || !str_cmp(s, "ENCRYPTED_SRTCP"))
out->sdes_encrypted_srtcp = 1;
else if (!str_cmp(&s, "authenticated_srtp") || !str_cmp(&s, "AUTHENTICATED_SRTP"))
else if (!str_cmp(s, "authenticated_srtp") || !str_cmp(s, "AUTHENTICATED_SRTP"))
out->sdes_authenticated_srtp = 1;
else
ilog(LOG_WARN, "Unknown 'SDES' flag encountered: '"STR_FORMAT"'",
STR_FMT(&s));
STR_FMT(s));
}
static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *input) {
static void call_ng_flags_list(struct sdp_ng_flags *out, bencode_item_t *input, const char *key,
void (*callback)(struct sdp_ng_flags *, str *, void *), void *parm)
{
bencode_item_t *list, *it;
str s;
if ((list = bencode_dictionary_get_expect(input, key, BENCODE_LIST))) {
for (it = list->child; it; it = it->sibling) {
if (!bencode_get_str(it, &s))
continue;
callback(out, &s, parm);
}
}
}
static void call_ng_flags_rtcp_mux(struct sdp_ng_flags *out, str *s, void *dummy) {
if (!str_cmp(s, "offer"))
out->rtcp_mux_offer = 1;
else if (!str_cmp(s, "require"))
out->rtcp_mux_require = 1;
else if (!str_cmp(s, "demux"))
out->rtcp_mux_demux = 1;
else if (!str_cmp(s, "accept"))
out->rtcp_mux_accept = 1;
else if (!str_cmp(s, "reject"))
out->rtcp_mux_reject = 1;
else
ilog(LOG_WARN, "Unknown 'rtcp-mux' flag encountered: '" STR_FORMAT "'",
STR_FMT(s));
}
static void call_ng_flags_replace(struct sdp_ng_flags *out, str *s, void *dummy) {
str_hyphenate(s);
if (!str_cmp(s, "origin"))
out->replace_origin = 1;
else if (!str_cmp(s, "session-connection"))
out->replace_sess_conn = 1;
else
ilog(LOG_WARN, "Unknown 'replace' flag encountered: '" STR_FORMAT "'",
STR_FMT(s));
}
static void call_ng_flags_codec_list(struct sdp_ng_flags *out, str *s, void *qp) {
str *s_copy;
s_copy = g_slice_alloc(sizeof(*s_copy));
*s_copy = *s;
g_queue_push_tail((GQueue *) qp, s_copy);
}
static void call_ng_flags_codec_ht(struct sdp_ng_flags *out, str *s, void *htp) {
str *s_copy;
s_copy = g_slice_alloc(sizeof(*s_copy));
*s_copy = *s;
g_hash_table_replace((GHashTable *) htp, s_copy, s_copy);
}
// helper to alias values from other dictionaries into the "flags" dictionary
INLINE int call_ng_flags_prefix(struct sdp_ng_flags *out, str *s_ori, const char *prefix,
void (*cb)(struct sdp_ng_flags *, str *, void *), void *ptr)
{
size_t len = strlen(prefix);
str s = *s_ori;
if (len > 0)
if (str_shift_cmp(&s, prefix))
return 0;
cb(out, &s, ptr);
return 1;
}
static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) {
str_hyphenate(s);
// XXX use internal hash tables for these
if (!str_cmp(s, "trust-address"))
out->trust_address = 1;
else if (!str_cmp(s, "SIP-source-address"))
out->trust_address = 0;
else if (!str_cmp(s, "asymmetric"))
out->asymmetric = 1;
else if (!str_cmp(s, "no-redis-update"))
out->no_redis_update = 1;
else if (!str_cmp(s, "unidirectional"))
out->unidirectional = 1;
else if (!str_cmp(s, "strict-source"))
out->strict_source = 1;
else if (!str_cmp(s, "media-handover"))
out->media_handover = 1;
else if (!str_cmp(s, "reset"))
out->reset = 1;
else if (!str_cmp(s, "port-latching"))
out->port_latching = 1;
else if (!str_cmp(s, "record-call"))
out->record_call = 1;
else if (!str_cmp(s, "no-rtcp-attribute"))
out->no_rtcp_attr = 1;
else {
// handle values aliases from other dictionaries
if (call_ng_flags_prefix(out, s, "SDES-", ng_sdes_option, NULL))
return;
if (call_ng_flags_prefix(out, s, "codec-strip-", call_ng_flags_codec_ht, out->codec_strip))
return;
if (call_ng_flags_prefix(out, s, "codec-offer-", call_ng_flags_codec_list, &out->codec_offer))
return;
ilog(LOG_WARN, "Unknown flag encountered: '" STR_FORMAT "'",
STR_FMT(s));
}
}
static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *input) {
bencode_item_t *list, *it, *dict;
int diridx;
str s;
ZERO(*out);
out->codec_strip = g_hash_table_new_full(str_hash, str_equal, str_slice_free, NULL);
out->trust_address = trust_address_def;
out->dtls_passive = dtls_passive_def;
if ((list = bencode_dictionary_get_expect(input, "flags", BENCODE_LIST))) {
for (it = list->child; it; it = it->sibling) {
if (it->type != BENCODE_STRING)
continue;
str_hyphenate(it);
if (!bencode_strcmp(it, "trust-address"))
out->trust_address = 1;
else if (!bencode_strcmp(it, "SIP-source-address"))
out->trust_address = 0;
else if (!bencode_strcmp(it, "asymmetric"))
out->asymmetric = 1;
else if (!bencode_strcmp(it, "no-redis-update"))
out->no_redis_update = 1;
else if (!bencode_strcmp(it, "unidirectional"))
out->unidirectional = 1;
else if (!bencode_strcmp(it, "strict-source"))
out->strict_source = 1;
else if (!bencode_strcmp(it, "media-handover"))
out->media_handover = 1;
else if (!bencode_strcmp(it, "reset"))
out->reset = 1;
else if (it->iov[1].iov_len >= 5 && !memcmp(it->iov[1].iov_base, "SDES-", 5))
ng_sdes_option(out, it, 5);
else if (!bencode_strcmp(it, "port-latching"))
out->port_latching = 1;
else if (!bencode_strcmp(it, "record-call"))
out->record_call = 1;
else
ilog(LOG_WARN, "Unknown flag encountered: '"BENCODE_FORMAT"'",
BENCODE_FMT(it));
}
}
if ((list = bencode_dictionary_get_expect(input, "replace", BENCODE_LIST))) {
for (it = list->child; it; it = it->sibling) {
str_hyphenate(it);
if (!bencode_strcmp(it, "origin"))
out->replace_origin = 1;
else if (!bencode_strcmp(it, "session-connection"))
out->replace_sess_conn = 1;
else
ilog(LOG_WARN, "Unknown 'replace' flag encountered: '"BENCODE_FORMAT"'",
BENCODE_FMT(it));
}
}
call_ng_flags_list(out, input, "flags", call_ng_flags_flags, NULL);
call_ng_flags_list(out, input, "replace", call_ng_flags_replace, NULL);
diridx = 0;
if ((list = bencode_dictionary_get_expect(input, "direction", BENCODE_LIST))) {
@ -618,30 +673,10 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu
STR_FMT(&s));
}
if ((list = bencode_dictionary_get_expect(input, "rtcp-mux", BENCODE_LIST))) {
for (it = list->child; it; it = it->sibling) {
if (!bencode_strcmp(it, "offer"))
out->rtcp_mux_offer = 1;
else if (!bencode_strcmp(it, "require"))
out->rtcp_mux_require = 1;
else if (!bencode_strcmp(it, "demux"))
out->rtcp_mux_demux = 1;
else if (!bencode_strcmp(it, "accept"))
out->rtcp_mux_accept = 1;
else if (!bencode_strcmp(it, "reject"))
out->rtcp_mux_reject = 1;
else
ilog(LOG_WARN, "Unknown 'rtcp-mux' flag encountered: '"BENCODE_FORMAT"'",
BENCODE_FMT(it));
}
}
call_ng_flags_list(out, input, "rtcp-mux", call_ng_flags_rtcp_mux, NULL);
/* XXX abstractize the other list walking functions using callbacks */
/* XXX module still needs to support this list */
if ((list = bencode_dictionary_get_expect(input, "SDES", BENCODE_LIST))) {
for (it = list->child; it; it = it->sibling)
ng_sdes_option(out, it, 0);
}
call_ng_flags_list(out, input, "SDES", ng_sdes_option, NULL);
bencode_get_alt(input, "transport-protocol", "transport protocol", &out->transport_protocol_str);
out->transport_protocol = transport_protocol(&out->transport_protocol_str);
@ -651,9 +686,19 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu
out->tos = bencode_dictionary_get_integer(input, "TOS", 256);
bencode_get_alt(input, "record-call", "record call", &out->record_call_str);
bencode_dictionary_get_str(input, "metadata", &out->metadata);
if ((dict = bencode_dictionary_get_expect(input, "codec", BENCODE_DICTIONARY))) {
/* XXX module still needs to support these */
call_ng_flags_list(out, dict, "strip", call_ng_flags_codec_ht, out->codec_strip);
call_ng_flags_list(out, dict, "offer", call_ng_flags_codec_list, &out->codec_offer);
}
}
static void call_ng_free_flags(struct sdp_ng_flags *flags) {
g_hash_table_destroy(flags->codec_strip);
g_queue_clear_full(&flags->codec_offer, str_slice_free);
}
static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster *m,
static const char *call_offer_answer_ng(bencode_item_t *input,
bencode_item_t *output, enum call_opmode opmode, const char* addr,
const endpoint_t *sin)
{
@ -694,7 +739,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster
goto out;
/* OP_ANSWER; OP_OFFER && !IS_FOREIGN_CALL */
call = call_get(&callid, m);
call = call_get(&callid);
/* Failover scenario because of timeout on offer response: siprouter tries
* to establish session with another rtpengine2 even though rtpengine1
@ -707,12 +752,12 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster
rwlock_unlock_w(&call->master_lock);
call_destroy(call);
obj_put(call);
call = call_get_or_create(&callid, m, CT_OWN_CALL);
call = call_get_or_create(&callid, CT_OWN_CALL);
}
}
else {
/* call == NULL, should create call */
call = call_get_or_create(&callid, m, CT_OWN_CALL);
call = call_get_or_create(&callid, CT_OWN_CALL);
}
}
@ -775,7 +820,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster
rwlock_unlock_w(&call->master_lock);
if (!flags.no_redis_update) {
redis_update_onekey(call,m->conf.redis_write);
redis_update_onekey(call, rtpe_redis_write);
} else {
ilog(LOG_DEBUG, "Not updating Redis due to present no-redis-update flag");
}
@ -787,6 +832,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster
if (ret == ERROR_NO_FREE_PORTS || ret == ERROR_NO_FREE_LOGS) {
ilog(LOG_ERR, "Destroying call");
errstr = "Ran out of ports";
call_destroy(call);
}
@ -800,40 +846,41 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster
out:
sdp_free(&parsed);
streams_free(&streams);
call_ng_free_flags(&flags);
return errstr;
}
const char *call_offer_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output, const char* addr,
const char *call_offer_ng(bencode_item_t *input, bencode_item_t *output, const char* addr,
const endpoint_t *sin)
{
rwlock_lock_r(&m->conf.config_lock);
if (m->conf.max_sessions>=0) {
rwlock_lock_r(&m->hashlock);
if (g_hash_table_size(m->callhash) -
atomic64_get(&m->stats.foreign_sessions) >= m->conf.max_sessions) {
rwlock_unlock_r(&m->hashlock);
rwlock_lock_r(&rtpe_config.config_lock);
if (rtpe_config.max_sessions>=0) {
rwlock_lock_r(&rtpe_callhash_lock);
if (g_hash_table_size(rtpe_callhash) -
atomic64_get(&rtpe_stats.foreign_sessions) >= rtpe_config.max_sessions) {
rwlock_unlock_r(&rtpe_callhash_lock);
/* foreign calls can't get rejected
* total_rejected_sess applies only to "own" sessions */
atomic64_inc(&m->totalstats.total_rejected_sess);
atomic64_inc(&m->totalstats_interval.total_rejected_sess);
ilog(LOG_ERROR, "Parallel session limit reached (%i)",m->conf.max_sessions);
atomic64_inc(&rtpe_totalstats.total_rejected_sess);
atomic64_inc(&rtpe_totalstats_interval.total_rejected_sess);
ilog(LOG_ERROR, "Parallel session limit reached (%i)",rtpe_config.max_sessions);
rwlock_unlock_r(&m->conf.config_lock);
rwlock_unlock_r(&rtpe_config.config_lock);
return "Parallel session limit reached";
}
rwlock_unlock_r(&m->hashlock);
rwlock_unlock_r(&rtpe_callhash_lock);
}
rwlock_unlock_r(&m->conf.config_lock);
return call_offer_answer_ng(input, m, output, OP_OFFER, addr, sin);
rwlock_unlock_r(&rtpe_config.config_lock);
return call_offer_answer_ng(input, output, OP_OFFER, addr, sin);
}
const char *call_answer_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) {
return call_offer_answer_ng(input, m, output, OP_ANSWER, NULL, NULL);
const char *call_answer_ng(bencode_item_t *input, bencode_item_t *output) {
return call_offer_answer_ng(input, output, OP_ANSWER, NULL, NULL);
}
const char *call_delete_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) {
const char *call_delete_ng(bencode_item_t *input, bencode_item_t *output) {
str fromtag, totag, viabranch, callid;
bencode_item_t *flags, *it;
int fatal = 0, delete_delay;
@ -863,7 +910,7 @@ const char *call_delete_ng(bencode_item_t *input, struct callmaster *m, bencode_
}
}
if (call_delete_branch(m, &callid, &viabranch, &fromtag, &totag, output, delete_delay)) {
if (call_delete_branch(&callid, &viabranch, &fromtag, &totag, output, delete_delay)) {
if (fatal)
return "Call-ID not found or tags didn't match";
bencode_dictionary_add_string(output, "warning", "Call-ID not found or tags didn't match");
@ -1137,29 +1184,29 @@ stats:
ng_stats(bencode_dictionary_add_dictionary(dict, "RTCP"), &totals->totals[1], NULL);
}
static void ng_list_calls( struct callmaster *m, bencode_item_t *output, long long int limit) {
static void ng_list_calls(bencode_item_t *output, long long int limit) {
GHashTableIter iter;
gpointer key, value;
rwlock_lock_r(&m->hashlock);
rwlock_lock_r(&rtpe_callhash_lock);
g_hash_table_iter_init (&iter, m->callhash);
g_hash_table_iter_init (&iter, rtpe_callhash);
while (limit-- && g_hash_table_iter_next (&iter, &key, &value)) {
bencode_list_add_str_dup(output, key);
}
rwlock_unlock_r(&m->hashlock);
rwlock_unlock_r(&rtpe_callhash_lock);
}
const char *call_query_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) {
const char *call_query_ng(bencode_item_t *input, bencode_item_t *output) {
str callid, fromtag, totag;
struct call *call;
if (!bencode_dictionary_get_str(input, "call-id", &callid))
return "No call-id in message";
call = call_get_opmode(&callid, m, OP_OTHER);
call = call_get_opmode(&callid, OP_OTHER);
if (!call)
return "Unknown call-id";
bencode_dictionary_get_str(input, "from-tag", &fromtag);
@ -1173,7 +1220,7 @@ const char *call_query_ng(bencode_item_t *input, struct callmaster *m, bencode_i
}
const char *call_list_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) {
const char *call_list_ng(bencode_item_t *input, bencode_item_t *output) {
bencode_item_t *calls = NULL;
long long int limit;
@ -1184,13 +1231,13 @@ const char *call_list_ng(bencode_item_t *input, struct callmaster *m, bencode_it
}
calls = bencode_dictionary_add_list(output, "calls");
ng_list_calls(m, calls, limit);
ng_list_calls(calls, limit);
return NULL;
}
const char *call_start_recording_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) {
const char *call_start_recording_ng(bencode_item_t *input, bencode_item_t *output) {
str callid;
struct call *call;
str metadata;
@ -1198,7 +1245,7 @@ const char *call_start_recording_ng(bencode_item_t *input, struct callmaster *m,
if (!bencode_dictionary_get_str(input, "call-id", &callid))
return "No call-id in message";
bencode_dictionary_get_str(input, "metadata", &metadata);
call = call_get_opmode(&callid, m, OP_OTHER);
call = call_get_opmode(&callid, OP_OTHER);
if (!call)
return "Unknown call-id";
@ -1210,13 +1257,13 @@ const char *call_start_recording_ng(bencode_item_t *input, struct callmaster *m,
return NULL;
}
const char *call_stop_recording_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) {
const char *call_stop_recording_ng(bencode_item_t *input, bencode_item_t *output) {
str callid;
struct call *call;
if (!bencode_dictionary_get_str(input, "call-id", &callid))
return "No call-id in message";
call = call_get_opmode(&callid, m, OP_OTHER);
call = call_get_opmode(&callid, OP_OTHER);
if (!call)
return "Unknown call-id";
@ -1227,3 +1274,20 @@ const char *call_stop_recording_ng(bencode_item_t *input, struct callmaster *m,
return NULL;
}
int call_interfaces_init() {
const char *errptr;
int erroff;
info_re = pcre_compile("^([^:,]+)(?::(.*?))?(?:$|,)", PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL);
if (!info_re)
return -1;
info_ree = pcre_study(info_re, 0, &errptr);
streams_re = pcre_compile("^([\\d.]+):(\\d+)(?::(.*?))?(?:$|,)", PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL);
if (!streams_re)
return -1;
streams_ree = pcre_study(streams_re, 0, &errptr);
return 0;
}

@ -13,8 +13,7 @@
struct call;
struct call_stats;
struct callmaster;
struct control_stream;
struct streambuf_stream;
struct sockaddr_in6;
struct sdp_ng_flags {
@ -32,6 +31,8 @@ struct sdp_ng_flags {
int tos;
str record_call_str;
str metadata;
GHashTable *codec_strip;
GQueue codec_offer;
int asymmetric:1,
no_redis_update:1,
unidirectional:1,
@ -47,6 +48,7 @@ struct sdp_ng_flags {
rtcp_mux_demux:1,
rtcp_mux_accept:1,
rtcp_mux_reject:1,
no_rtcp_attr:1,
strict_source:1,
media_handover:1,
dtls_passive:1,
@ -66,26 +68,28 @@ extern int trust_address_def;
extern int dtls_passive_def;
str *call_request_tcp(char **, struct callmaster *);
str *call_lookup_tcp(char **, struct callmaster *);
void call_delete_tcp(char **, struct callmaster *);
void calls_status_tcp(struct callmaster *, struct control_stream *);
str *call_request_tcp(char **);
str *call_lookup_tcp(char **);
void call_delete_tcp(char **);
void calls_status_tcp(struct streambuf_stream *);
str *call_update_udp(char **, struct callmaster *, const char*, const endpoint_t *);
str *call_lookup_udp(char **, struct callmaster *);
str *call_delete_udp(char **, struct callmaster *);
str *call_query_udp(char **, struct callmaster *);
str *call_update_udp(char **, const char*, const endpoint_t *);
str *call_lookup_udp(char **);
str *call_delete_udp(char **);
str *call_query_udp(char **);
const char *call_offer_ng(bencode_item_t *, struct callmaster *, bencode_item_t *, const char*,
const char *call_offer_ng(bencode_item_t *, bencode_item_t *, const char*,
const endpoint_t *);
const char *call_answer_ng(bencode_item_t *, struct callmaster *, bencode_item_t *);
const char *call_delete_ng(bencode_item_t *, struct callmaster *, bencode_item_t *);
const char *call_query_ng(bencode_item_t *, struct callmaster *, bencode_item_t *);
const char *call_list_ng(bencode_item_t *, struct callmaster *, bencode_item_t *);
const char *call_start_recording_ng(bencode_item_t *, struct callmaster *, bencode_item_t *);
const char *call_stop_recording_ng(bencode_item_t *, struct callmaster *, bencode_item_t *);
const char *call_answer_ng(bencode_item_t *, bencode_item_t *);
const char *call_delete_ng(bencode_item_t *, bencode_item_t *);
const char *call_query_ng(bencode_item_t *, bencode_item_t *);
const char *call_list_ng(bencode_item_t *, bencode_item_t *);
const char *call_start_recording_ng(bencode_item_t *, bencode_item_t *);
const char *call_stop_recording_ng(bencode_item_t *, bencode_item_t *);
void ng_call_stats(struct call *call, const str *fromtag, const str *totag, bencode_item_t *output,
struct call_stats *totals);
int call_interfaces_init(void);
#endif

File diff suppressed because it is too large Load Diff

@ -3,17 +3,16 @@
#include "socket.h"
#include "obj.h"
#include "tcp_listener.h"
struct cli {
struct obj obj;
struct callmaster *callmaster;
socket_t sock;
struct poller *poller;
mutex_t lock;
struct streambuf_listener listeners[2];
};
struct cli *cli_new(struct poller *p, const endpoint_t *, struct callmaster *m);
struct cli *cli_new(struct poller *p, endpoint_t *);
#endif /* CLI_UDP_H_ */

@ -16,6 +16,10 @@
#include "log_funcs.h"
mutex_t rtpe_cngs_lock;
GHashTable *rtpe_cngs_hash;
static void timeval_update_request_time(struct request_time *request, const struct timeval *offer_diff) {
// lock offers
@ -88,23 +92,22 @@ static void pretty_print(bencode_item_t *el, GString *s) {
}
struct control_ng_stats* get_control_ng_stats(struct control_ng* c, const sockaddr_t *addr) {
struct callmaster *m = c->callmaster;
struct control_ng_stats* cur;
mutex_lock(&m->cngs_lock);
cur = g_hash_table_lookup(m->cngs_hash, addr);
mutex_lock(&rtpe_cngs_lock);
cur = g_hash_table_lookup(rtpe_cngs_hash, addr);
if (!cur) {
cur = g_slice_alloc0(sizeof(struct control_ng_stats));
cur->proxy = *addr;
ilog(LOG_DEBUG,"Adding a proxy for control ng stats:%s", sockaddr_print_buf(addr));
g_hash_table_insert(m->cngs_hash, &cur->proxy, cur);
g_hash_table_insert(rtpe_cngs_hash, &cur->proxy, cur);
}
mutex_unlock(&m->cngs_lock);
mutex_unlock(&rtpe_cngs_lock);
return cur;
}
static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin, char *addr,
struct udp_listener *ul)
socket_t *ul)
{
struct control_ng *c = (void *) obj;
bencode_buffer_t bencbuf;
@ -126,7 +129,8 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin
return;
}
assert( bencode_buffer_init(&bencbuf) == 0 );
int ret = bencode_buffer_init(&bencbuf);
assert(ret == 0);
resp = bencode_dictionary(&bencbuf);
assert(resp != NULL);
@ -169,6 +173,7 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin
g_string_free(log_str, TRUE);
}
// XXX do the strcmp's only once
errstr = NULL;
resultstr = "ok";
if (!str_cmp(&cmd, "ping")) {
@ -176,11 +181,10 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin
g_atomic_int_inc(&cur->ping);
}
else if (!str_cmp(&cmd, "offer")) {
//TODO inc offers per second
// start offer timer
gettimeofday(&offer_start, NULL);
errstr = call_offer_ng(dict, c->callmaster, resp, addr, sin);
errstr = call_offer_ng(dict, resp, addr, sin);
g_atomic_int_inc(&cur->offer);
// stop offer timer
@ -189,14 +193,13 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin
// print offer duration
timeval_from_us(&offer_stop, timeval_diff(&offer_stop, &offer_start));
ilog(LOG_INFO, "offer time = %llu.%06llu sec", (unsigned long long)offer_stop.tv_sec, (unsigned long long)offer_stop.tv_usec);
atomic64_inc(&c->callmaster->statsps.offers);
}
else if (!str_cmp(&cmd, "answer")) {
//TODO inc answers per second
// start answer timer
gettimeofday(&answer_start, NULL);
errstr = call_answer_ng(dict, c->callmaster, resp);
errstr = call_answer_ng(dict, resp);
g_atomic_int_inc(&cur->answer);
// stop answer timer
@ -205,14 +208,12 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin
// print answer duration
timeval_from_us(&answer_stop, timeval_diff(&answer_stop, &answer_start));
ilog(LOG_INFO, "answer time = %llu.%06llu sec", (unsigned long long)answer_stop.tv_sec, (unsigned long long)answer_stop.tv_usec);
atomic64_inc(&c->callmaster->statsps.answers);
}
else if (!str_cmp(&cmd, "delete")) {
//TODO inc deletes per second
// start delete timer
gettimeofday(&delete_start, NULL);
errstr = call_delete_ng(dict, c->callmaster, resp);
errstr = call_delete_ng(dict, resp);
g_atomic_int_inc(&cur->delete);
// stop delete timer
@ -221,22 +222,21 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin
// print delete duration
timeval_from_us(&delete_stop, timeval_diff(&delete_stop, &delete_start));
ilog(LOG_INFO, "delete time = %llu.%06llu sec", (unsigned long long)delete_stop.tv_sec, (unsigned long long)delete_stop.tv_usec);
atomic64_inc(&c->callmaster->statsps.deletes);
}
else if (!str_cmp(&cmd, "query")) {
errstr = call_query_ng(dict, c->callmaster, resp);
errstr = call_query_ng(dict, resp);
g_atomic_int_inc(&cur->query);
}
else if (!str_cmp(&cmd, "list")) {
errstr = call_list_ng(dict, c->callmaster, resp);
errstr = call_list_ng(dict, resp);
g_atomic_int_inc(&cur->list);
}
else if (!str_cmp(&cmd, "start recording")) {
errstr = call_start_recording_ng(dict, c->callmaster, resp);
errstr = call_start_recording_ng(dict, resp);
g_atomic_int_inc(&cur->start_recording);
}
else if (!str_cmp(&cmd, "stop recording")) {
errstr = call_stop_recording_ng(dict, c->callmaster, resp);
errstr = call_stop_recording_ng(dict, resp);
g_atomic_int_inc(&cur->stop_recording);
}
else
@ -249,11 +249,14 @@ static void control_ng_incoming(struct obj *obj, str *buf, const endpoint_t *sin
// update interval statistics
if (!str_cmp(&cmd, "offer")) {
timeval_update_request_time(&c->callmaster->totalstats_interval.offer, &offer_stop);
atomic64_inc(&rtpe_statsps.offers);
timeval_update_request_time(&rtpe_totalstats_interval.offer, &offer_stop);
} else if (!str_cmp(&cmd, "answer")) {
timeval_update_request_time(&c->callmaster->totalstats_interval.answer, &answer_stop);
atomic64_inc(&rtpe_statsps.answers);
timeval_update_request_time(&rtpe_totalstats_interval.answer, &answer_stop);
} else if (!str_cmp(&cmd, "delete")) {
timeval_update_request_time(&c->callmaster->totalstats_interval.delete, &delete_stop);
atomic64_inc(&rtpe_statsps.deletes);
timeval_update_request_time(&rtpe_totalstats_interval.delete, &delete_stop);
}
goto send_resp;
@ -295,7 +298,7 @@ send_only:
iov[2].iov_base = to_send->s;
iov[2].iov_len = to_send->len;
socket_sendiov(&ul->sock, iov, iovlen, sin);
socket_sendiov(ul, iov, iovlen, sin);
if (resp)
cookie_cache_insert(&c->cookie_cache, &cookie, &reply);
@ -311,22 +314,26 @@ out:
struct control_ng *control_ng_new(struct poller *p, endpoint_t *ep, struct callmaster *m) {
struct control_ng *control_ng_new(struct poller *p, endpoint_t *ep, unsigned char tos) {
struct control_ng *c;
if (!p || !m)
if (!p)
return NULL;
c = obj_alloc0("control_ng", sizeof(*c), NULL);
c->callmaster = m;
cookie_cache_init(&c->cookie_cache);
if (udp_listener_init(&c->udp_listeners[0], p, ep, control_ng_incoming, &c->obj))
goto fail2;
if (ipv46_any_convert(ep) && udp_listener_init(&c->udp_listeners[1], p, ep, control_ng_incoming, &c->obj))
goto fail2;
if (tos)
set_tos(&c->udp_listeners[0],tos);
if (ipv46_any_convert(ep)) {
if (udp_listener_init(&c->udp_listeners[1], p, ep, control_ng_incoming, &c->obj))
goto fail2;
if (tos)
set_tos(&c->udp_listeners[1],tos);
}
return c;
fail2:
@ -334,3 +341,9 @@ fail2:
return NULL;
}
void control_ng_init() {
mutex_init(&rtpe_cngs_lock);
rtpe_cngs_hash = g_hash_table_new(g_sockaddr_hash, g_sockaddr_eq);
}

@ -8,7 +8,6 @@
struct poller;
struct callmaster;
struct control_ng_stats {
sockaddr_t proxy;
@ -25,11 +24,14 @@ struct control_ng_stats {
struct control_ng {
struct obj obj;
struct callmaster *callmaster;
struct cookie_cache cookie_cache;
struct udp_listener udp_listeners[2];
socket_t udp_listeners[2];
};
struct control_ng *control_ng_new(struct poller *, endpoint_t *, struct callmaster *);
struct control_ng *control_ng_new(struct poller *, endpoint_t *, unsigned char);
void control_ng_init(void);
extern mutex_t rtpe_cngs_lock;
extern GHashTable *rtpe_cngs_hash;
#endif

@ -18,101 +18,67 @@
#include "call_interfaces.h"
#include "socket.h"
#include "log_funcs.h"
#include "tcp_listener.h"
struct control_stream {
struct obj obj;
int fd;
mutex_t lock;
struct streambuf *inbuf;
struct streambuf *outbuf;
struct sockaddr_in inaddr;
struct control_tcp *control;
struct poller *poller;
int linked:1;
};
struct control_tcp {
struct obj obj;
int fd;
struct streambuf_listener listeners[2];
pcre *parse_re;
pcre_extra *parse_ree;
mutex_t lock;
GList *streams;
struct poller *poller;
struct callmaster *callmaster;
};
static void control_stream_closed(int fd, void *p, uintptr_t u) {
struct control_stream *s = p;
struct control_tcp *c;
GList *l = NULL;
//static void control_stream_closed(int fd, void *p, uintptr_t u) {
static void control_stream_closed(struct streambuf_stream *s) {
ilog(LOG_INFO, "Control connection from %s closed", s->addr);
}
ilog(LOG_INFO, "Control connection from " DF " closed", DP(s->inaddr));
c = s->control;
static void control_list(struct control_tcp *c, struct streambuf_stream *s) {
for (int i = 0; i < G_N_ELEMENTS(c->listeners); i++) {
if (!c->listeners[i].listener.family || !c->listeners[i].poller)
continue; // not used
restart:
mutex_lock(&c->lock);
if (s->linked) {
/* we might get called when it's not quite linked yet */
l = g_list_find(c->streams, s);
if (!l) {
mutex_unlock(&c->lock);
goto restart;
}
c->streams = g_list_delete_link(c->streams, l);
s->linked = 0;
}
mutex_unlock(&c->lock);
if (l)
obj_put(s);
poller_del_item(s->poller, fd);
}
mutex_lock(&c->listeners[i].lock);
GList *streams = g_hash_table_get_values(c->listeners[i].streams);
for (GList *l = streams; l; l = l->next) {
struct streambuf_stream *cl = l->data;
streambuf_printf(s->outbuf, "%s\n", cl->addr);
}
static void control_list(struct control_tcp *c, struct control_stream *s) {
struct control_stream *i;
GList *l;
mutex_unlock(&c->listeners[i].lock);
mutex_lock(&c->lock);
for (l = c->streams; l; l = l->next) {
i = l->data;
mutex_lock(&s->lock);
streambuf_printf(s->outbuf, DF "\n", DP(i->inaddr));
mutex_unlock(&s->lock);
g_list_free(streams);
}
mutex_unlock(&c->lock);
streambuf_printf(s->outbuf, "End.\n");
}
static int control_stream_parse(struct control_stream *s, char *line) {
static int control_stream_parse(struct streambuf_stream *s, char *line) {
int ovec[60];
int ret;
char **out;
struct control_tcp *c = s->control;
struct control_tcp *c = (void *) s->parent;
str *output = NULL;
ret = pcre_exec(c->parse_re, c->parse_ree, line, strlen(line), 0, 0, ovec, G_N_ELEMENTS(ovec));
if (ret <= 0) {
ilog(LOG_WARNING, "Unable to parse command line from " DF ": %s", DP(s->inaddr), line);
ilog(LOG_WARNING, "Unable to parse command line from %s: %s", s->addr, line);
return -1;
}
ilog(LOG_INFO, "Got valid command from " DF ": %s", DP(s->inaddr), line);
ilog(LOG_INFO, "Got valid command from %s: %s", s->addr, line);
pcre_get_substring_list(line, ovec, ret, (const char ***) &out);
@ -124,175 +90,100 @@ static int control_stream_parse(struct control_stream *s, char *line) {
if (!strcmp(out[RE_TCP_RL_CMD], "request"))
output = call_request_tcp(out, c->callmaster);
output = call_request_tcp(out);
else if (!strcmp(out[RE_TCP_RL_CMD], "lookup"))
output = call_lookup_tcp(out, c->callmaster);
output = call_lookup_tcp(out);
else if (!strcmp(out[RE_TCP_D_CMD], "delete"))
call_delete_tcp(out, c->callmaster);
call_delete_tcp(out);
else if (!strcmp(out[RE_TCP_DIV_CMD], "status"))
calls_status_tcp(c->callmaster, s);
calls_status_tcp(s);
else if (!strcmp(out[RE_TCP_DIV_CMD], "build") || !strcmp(out[RE_TCP_DIV_CMD], "version"))
control_stream_printf(s, "Version: %s\n", RTPENGINE_VERSION);
streambuf_printf(s->outbuf, "Version: %s\n", RTPENGINE_VERSION);
else if (!strcmp(out[RE_TCP_DIV_CMD], "controls"))
control_list(c, s);
else if (!strcmp(out[RE_TCP_DIV_CMD], "quit") || !strcmp(out[RE_TCP_DIV_CMD], "exit"))
;
if (output) {
mutex_lock(&s->lock);
streambuf_write_str(s->outbuf, output);
mutex_unlock(&s->lock);
free(output);
}
pcre_free(out);
log_info_clear();
return -1;
return 1;
}
static void control_stream_timer(int fd, void *p, uintptr_t u) {
struct control_stream *s = p;
int i;
mutex_lock(&s->lock);
i = (poller_now - s->inbuf->active) >= 60 || (poller_now - s->outbuf->active) >= 60;
mutex_unlock(&s->lock);
if (i)
control_stream_closed(s->fd, s, 0);
static void control_stream_timer(struct streambuf_stream *s) {
if ((rtpe_now.tv_sec - s->inbuf->active) >= 60 || (rtpe_now.tv_sec - s->outbuf->active) >= 60)
control_stream_closed(s);
}
static void control_stream_readable(int fd, void *p, uintptr_t u) {
struct control_stream *s = p;
//static void control_stream_readable(int fd, void *p, uintptr_t u) {
static void control_stream_readable(struct streambuf_stream *s) {
char *line;
int ret;
mutex_lock(&s->lock);
if (streambuf_readable(s->inbuf))
goto close;
while ((line = streambuf_getline(s->inbuf))) {
mutex_unlock(&s->lock);
ilog(LOG_DEBUG, "Got control line from " DF ": %s", DP(s->inaddr), line);
ilog(LOG_DEBUG, "Got control line from %s: %s", s->addr, line);
ret = control_stream_parse(s, line);
free(line);
if (ret == 1) {
streambuf_stream_shutdown(s);
break;
}
if (ret)
goto close_nolock;
mutex_lock(&s->lock);
goto close;
}
if (streambuf_bufsize(s->inbuf) > 1024) {
ilog(LOG_WARNING, "Buffer length exceeded in control connection from " DF, DP(s->inaddr));
ilog(LOG_WARNING, "Buffer length exceeded in control connection from %s", s->addr);
goto close;
}
mutex_unlock(&s->lock);
return;
close:
mutex_unlock(&s->lock);
close_nolock:
control_stream_closed(fd, s, 0);
}
static void control_stream_writeable(int fd, void *p, uintptr_t u) {
struct control_stream *s = p;
if (streambuf_writeable(s->outbuf))
control_stream_closed(fd, s, 0);
streambuf_stream_close(s);
}
static void control_closed(int fd, void *p, uintptr_t u) {
abort();
static void control_incoming(struct streambuf_stream *s) {
ilog(LOG_INFO, "New TCP control connection from %s", s->addr);
}
static void control_stream_free(void *p) {
struct control_stream *s = p;
close(s->fd);
streambuf_destroy(s->inbuf);
streambuf_destroy(s->outbuf);
mutex_destroy(&s->lock);
}
static void control_incoming(int fd, void *p, uintptr_t u) {
int nfd;
struct control_tcp *c = p;
struct control_stream *s;
struct poller_item i;
struct sockaddr_in sin;
socklen_t sinl;
next:
sinl = sizeof(sin);
nfd = accept(fd, (struct sockaddr *) &sin, &sinl);
if (nfd == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK)
return;
goto next;
}
nonblock(nfd);
ilog(LOG_INFO, "New control connection from " DF, DP(sin));
s = obj_alloc0("control_stream", sizeof(*s), control_stream_free);
s->fd = nfd;
s->control = c;
s->poller = c->poller;
s->inbuf = streambuf_new(c->poller, nfd);
s->outbuf = streambuf_new(c->poller, nfd);
memcpy(&s->inaddr, &sin, sizeof(s->inaddr));
mutex_init(&s->lock);
s->linked = 1;
ZERO(i);
i.fd = nfd;
i.closed = control_stream_closed;
i.readable = control_stream_readable;
i.writeable = control_stream_writeable;
i.timer = control_stream_timer;
i.obj = &s->obj;
if (poller_add_item(c->poller, &i))
goto fail;
mutex_lock(&c->lock);
/* let the list steal our own ref */
c->streams = g_list_prepend(c->streams, s);
mutex_unlock(&c->lock);
goto next;
fail:
obj_put(s);
goto next;
}
struct control_tcp *control_tcp_new(struct poller *p, const endpoint_t *ep, struct callmaster *m) {
socket_t sock;
struct control_tcp *control_tcp_new(struct poller *p, endpoint_t *ep) {
struct control_tcp *c;
struct poller_item i;
const char *errptr;
int erroff;
if (!p)
return NULL;
if (!m)
return NULL;
if (open_socket(&sock, SOCK_STREAM, ep->port, &ep->address))
return NULL;
c = obj_alloc0("control", sizeof(*c), NULL);
if (listen(sock.fd, 5))
if (streambuf_listener_init(&c->listeners[0], p, ep,
control_incoming, control_stream_readable,
control_stream_closed,
control_stream_timer,
&c->obj))
{
ilog(LOG_ERR, "Failed to open TCP control port: %s", strerror(errno));
goto fail;
c = obj_alloc0("control", sizeof(*c), NULL);
}
if (ipv46_any_convert(ep)) {
if (streambuf_listener_init(&c->listeners[1], p, ep,
control_incoming, control_stream_readable,
control_stream_closed,
control_stream_timer,
&c->obj))
{
ilog(LOG_ERR, "Failed to open TCP control port: %s", strerror(errno));
goto fail;
}
}
c->parse_re = pcre_compile(
/* reqtype callid streams ip fromdom fromtype todom totype agent info |reqtype callid info | reqtype */
@ -300,36 +191,13 @@ struct control_tcp *control_tcp_new(struct poller *p, const endpoint_t *ep, stru
PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL);
c->parse_ree = pcre_study(c->parse_re, 0, &errptr);
c->fd = sock.fd;
c->poller = p;
c->callmaster = m;
mutex_init(&c->lock);
ZERO(i);
i.fd = sock.fd;
i.closed = control_closed;
i.readable = control_incoming;
i.obj = &c->obj;
if (poller_add_item(p, &i))
goto fail2;
obj_put(c);
return c;
fail2:
obj_put(c);
fail:
close_socket(&sock);
// XXX streambuf_listener_close ...
obj_put(c);
return NULL;
}
void control_stream_printf(struct control_stream *s, const char *f, ...) {
va_list va;
va_start(va, f);
mutex_lock(&s->lock);
streambuf_vprintf(s->outbuf, f, va);
mutex_unlock(&s->lock);
va_end(va);
}

@ -30,14 +30,12 @@
#define RE_TCP_DIV_CMD 14
struct poller;
struct callmaster;
struct control_tcp;
struct control_stream;
struct streambuf_stream;
struct control_tcp *control_tcp_new(struct poller *, const endpoint_t *, struct callmaster *);
void control_stream_printf(struct control_stream *, const char *, ...) __attribute__ ((format (printf, 2, 3)));
struct control_tcp *control_tcp_new(struct poller *, endpoint_t *);

@ -22,7 +22,7 @@
static void control_udp_incoming(struct obj *obj, str *buf, const endpoint_t *sin, char *addr,
struct udp_listener *ul) {
socket_t *ul) {
struct control_udp *u = (void *) obj;
int ret;
int ovec[100];
@ -60,7 +60,7 @@ static void control_udp_incoming(struct obj *obj, str *buf, const endpoint_t *si
iovlen = 2;
}
socket_sendiov(&ul->sock, iov, iovlen, sin);
socket_sendiov(ul, iov, iovlen, sin);
pcre_free(out);
@ -75,7 +75,7 @@ static void control_udp_incoming(struct obj *obj, str *buf, const endpoint_t *si
reply = cookie_cache_lookup(&u->cookie_cache, &cookie);
if (reply) {
ilog(LOG_INFO, "Detected command from udp:%s as a duplicate", addr);
socket_sendto(&ul->sock, reply->s, reply->len, sin);
socket_sendto(ul, reply->s, reply->len, sin);
free(reply);
goto out;
}
@ -86,13 +86,13 @@ static void control_udp_incoming(struct obj *obj, str *buf, const endpoint_t *si
log_info_c_string(out[RE_UDP_DQ_CALLID]);
if (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'U')
reply = call_update_udp(out, u->callmaster, addr, sin);
reply = call_update_udp(out, addr, sin);
else if (chrtoupper(out[RE_UDP_UL_CMD][0]) == 'L')
reply = call_lookup_udp(out, u->callmaster);
reply = call_lookup_udp(out);
else if (chrtoupper(out[RE_UDP_DQ_CMD][0]) == 'D')
reply = call_delete_udp(out, u->callmaster);
reply = call_delete_udp(out);
else if (chrtoupper(out[RE_UDP_DQ_CMD][0]) == 'Q')
reply = call_query_udp(out, u->callmaster);
reply = call_query_udp(out);
else if (chrtoupper(out[RE_UDP_V_CMD][0]) == 'V') {
iovlen = 2;
@ -118,11 +118,11 @@ static void control_udp_incoming(struct obj *obj, str *buf, const endpoint_t *si
iov[2].iov_len = 9;
iovlen++;
}
socket_sendiov(&ul->sock, iov, iovlen, sin);
socket_sendiov(ul, iov, iovlen, sin);
}
if (reply) {
socket_sendto(&ul->sock, reply->s, reply->len, sin);
socket_sendto(ul, reply->s, reply->len, sin);
cookie_cache_insert(&u->cookie_cache, &cookie, reply);
free(reply);
}
@ -134,17 +134,16 @@ out:
log_info_clear();
}
struct control_udp *control_udp_new(struct poller *p, endpoint_t *ep, struct callmaster *m) {
struct control_udp *control_udp_new(struct poller *p, endpoint_t *ep) {
struct control_udp *c;
const char *errptr;
int erroff;
if (!p || !m)
if (!p)
return NULL;
c = obj_alloc0("control_udp", sizeof(*c), NULL);
c->callmaster = m;
c->parse_re = pcre_compile(
/* cookie cmd flags callid viabranch:5 */
"^(\\S+)\\s+(?:([ul])(\\S*)\\s+([^;]+)(?:;(\\S+))?\\s+" \

@ -39,7 +39,6 @@
#define RE_UDP_V_PARMS 20
struct poller;
struct callmaster;
@ -48,9 +47,8 @@ struct callmaster;
struct control_udp {
struct obj obj;
struct callmaster *callmaster;
struct cookie_cache cookie_cache;
struct udp_listener udp_listeners[2];
socket_t udp_listeners[2];
pcre *parse_re;
pcre_extra *parse_ree;
@ -61,7 +59,7 @@ struct control_udp {
struct control_udp *control_udp_new(struct poller *, endpoint_t *, struct callmaster *);
struct control_udp *control_udp_new(struct poller *, endpoint_t *);

@ -18,19 +18,19 @@ INLINE void cookie_cache_state_init(struct cookie_cache_state *s) {
void cookie_cache_init(struct cookie_cache *c) {
cookie_cache_state_init(&c->current);
cookie_cache_state_init(&c->old);
c->swap_time = poller_now;
c->swap_time = rtpe_now.tv_sec;
mutex_init(&c->lock);
cond_init(&c->cond);
}
/* lock must be held */
static void __cookie_cache_check_swap(struct cookie_cache *c) {
if (poller_now - c->swap_time >= 30) {
if (rtpe_now.tv_sec - c->swap_time >= 30) {
g_hash_table_remove_all(c->old.cookies);
g_string_chunk_clear(c->old.chunks);
swap_ptrs(&c->old.chunks, &c->current.chunks);
swap_ptrs(&c->old.cookies, &c->current.cookies);
c->swap_time = poller_now;
c->swap_time = rtpe_now.tv_sec;
}
}

@ -317,7 +317,7 @@ static void __dtls_timer(void *p) {
long int left;
c = dtls_cert();
left = c->expires - poller_now;
left = c->expires - rtpe_now.tv_sec;
if (left > CERT_EXPIRY_TIME/2)
goto out;

@ -20,12 +20,16 @@
#include "call.h"
#include "graphite.h"
#include "socket.h"
#include "statistics.h"
#include "main.h"
struct timeval rtpe_latest_graphite_interval_start;
static socket_t graphite_sock;
static int connection_state = STATE_DISCONNECTED;
//struct totalstats totalstats_prev;
static time_t next_run;
// HEAD: static time_t g_now, next_run;
// HEAD: static time_t rtpe_now, next_run;
static char* graphite_prefix = NULL;
static struct timeval graphite_interval_tv;
static struct totalstats graphite_stats;
@ -93,16 +97,10 @@ int connect_to_graphite_server(const endpoint_t *graphite_ep) {
return 0;
}
int send_graphite_data(struct callmaster *cm, struct totalstats *sent_data) {
int send_graphite_data(struct totalstats *sent_data) {
int rc=0;
// sanity checks
if (!cm) {
ilog(LOG_ERROR, "NULL callmaster when trying to send data");
return -1;
}
if (graphite_sock.fd < 0) {
ilog(LOG_ERROR,"Graphite socket is not connected.");
return -1;
@ -114,50 +112,50 @@ int send_graphite_data(struct callmaster *cm, struct totalstats *sent_data) {
struct totalstats *ts = sent_data;
/* atomically copy values to stack and reset to zero */
atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_timeout_sess);
atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_rejected_sess);
atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_silent_timeout_sess);
atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_final_timeout_sess);
atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_regular_term_sess);
atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_forced_term_sess);
atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_relayed_packets);
atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_relayed_errors);
atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_nopacket_relayed_sess);
atomic64_local_copy_zero_struct(ts, &cm->totalstats_interval, total_oneway_stream_sess);
mutex_lock(&cm->totalstats_interval.total_average_lock);
ts->total_average_call_dur = cm->totalstats_interval.total_average_call_dur;
ts->total_managed_sess = cm->totalstats_interval.total_managed_sess;
ZERO(cm->totalstats_interval.total_average_call_dur);
ZERO(cm->totalstats_interval.total_managed_sess);
mutex_unlock(&cm->totalstats_interval.total_average_lock);
mutex_lock(&cm->totalstats_interval.total_calls_duration_lock);
ts->total_calls_duration_interval = cm->totalstats_interval.total_calls_duration_interval;
cm->totalstats_interval.total_calls_duration_interval.tv_sec = 0;
cm->totalstats_interval.total_calls_duration_interval.tv_usec = 0;
//ZERO(cm->totalstats_interval.total_calls_duration_interval);
mutex_unlock(&cm->totalstats_interval.total_calls_duration_lock);
ts->offer = timeval_clear_request_time(&cm->totalstats_interval.offer);
ts->answer = timeval_clear_request_time(&cm->totalstats_interval.answer);
ts->delete = timeval_clear_request_time(&cm->totalstats_interval.delete);
ts->offers_ps = clear_requests_per_second(&cm->totalstats_interval.offers_ps);
ts->answers_ps = clear_requests_per_second(&cm->totalstats_interval.answers_ps);
ts->deletes_ps = clear_requests_per_second(&cm->totalstats_interval.deletes_ps);
rwlock_lock_r(&cm->hashlock);
mutex_lock(&cm->totalstats_interval.managed_sess_lock);
ts->managed_sess_max = cm->totalstats_interval.managed_sess_max;
ts->managed_sess_min = cm->totalstats_interval.managed_sess_min;
ts->total_sessions = g_hash_table_size(cm->callhash);
ts->foreign_sessions = atomic64_get(&cm->stats.foreign_sessions);
atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_timeout_sess);
atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_rejected_sess);
atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_silent_timeout_sess);
atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_final_timeout_sess);
atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_regular_term_sess);
atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_forced_term_sess);
atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_relayed_packets);
atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_relayed_errors);
atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_nopacket_relayed_sess);
atomic64_local_copy_zero_struct(ts, &rtpe_totalstats_interval, total_oneway_stream_sess);
mutex_lock(&rtpe_totalstats_interval.total_average_lock);
ts->total_average_call_dur = rtpe_totalstats_interval.total_average_call_dur;
ts->total_managed_sess = rtpe_totalstats_interval.total_managed_sess;
ZERO(rtpe_totalstats_interval.total_average_call_dur);
ZERO(rtpe_totalstats_interval.total_managed_sess);
mutex_unlock(&rtpe_totalstats_interval.total_average_lock);
mutex_lock(&rtpe_totalstats_interval.total_calls_duration_lock);
ts->total_calls_duration_interval = rtpe_totalstats_interval.total_calls_duration_interval;
rtpe_totalstats_interval.total_calls_duration_interval.tv_sec = 0;
rtpe_totalstats_interval.total_calls_duration_interval.tv_usec = 0;
//ZERO(rtpe_totalstats_interval.total_calls_duration_interval);
mutex_unlock(&rtpe_totalstats_interval.total_calls_duration_lock);
ts->offer = timeval_clear_request_time(&rtpe_totalstats_interval.offer);
ts->answer = timeval_clear_request_time(&rtpe_totalstats_interval.answer);
ts->delete = timeval_clear_request_time(&rtpe_totalstats_interval.delete);
ts->offers_ps = clear_requests_per_second(&rtpe_totalstats_interval.offers_ps);
ts->answers_ps = clear_requests_per_second(&rtpe_totalstats_interval.answers_ps);
ts->deletes_ps = clear_requests_per_second(&rtpe_totalstats_interval.deletes_ps);
rwlock_lock_r(&rtpe_callhash_lock);
mutex_lock(&rtpe_totalstats_interval.managed_sess_lock);
ts->managed_sess_max = rtpe_totalstats_interval.managed_sess_max;
ts->managed_sess_min = rtpe_totalstats_interval.managed_sess_min;
ts->total_sessions = g_hash_table_size(rtpe_callhash);
ts->foreign_sessions = atomic64_get(&rtpe_stats.foreign_sessions);
ts->own_sessions = ts->total_sessions - ts->foreign_sessions;
cm->totalstats_interval.managed_sess_max = ts->own_sessions;;
cm->totalstats_interval.managed_sess_min = ts->own_sessions;
mutex_unlock(&cm->totalstats_interval.managed_sess_lock);
rwlock_unlock_r(&cm->hashlock);
rtpe_totalstats_interval.managed_sess_max = ts->own_sessions;;
rtpe_totalstats_interval.managed_sess_min = ts->own_sessions;
mutex_unlock(&rtpe_totalstats_interval.managed_sess_lock);
rwlock_unlock_r(&rtpe_callhash_lock);
// compute average offer/answer/delete time
timeval_divide(&ts->offer.time_avg, &ts->offer.time_avg, ts->offer.count);
@ -169,90 +167,90 @@ int send_graphite_data(struct callmaster *cm, struct totalstats *sent_data) {
ts->deletes_ps.ps_avg = (ts->deletes_ps.count?(ts->deletes_ps.ps_avg/ts->deletes_ps.count):0);
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"offer_time_min %llu.%06llu %llu\n",(unsigned long long)ts->offer.time_min.tv_sec,(unsigned long long)ts->offer.time_min.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"offer_time_min %llu.%06llu %llu\n",(unsigned long long)ts->offer.time_min.tv_sec,(unsigned long long)ts->offer.time_min.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"offer_time_max %llu.%06llu %llu\n",(unsigned long long)ts->offer.time_max.tv_sec,(unsigned long long)ts->offer.time_max.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"offer_time_max %llu.%06llu %llu\n",(unsigned long long)ts->offer.time_max.tv_sec,(unsigned long long)ts->offer.time_max.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"offer_time_avg %llu.%06llu %llu\n",(unsigned long long)ts->offer.time_avg.tv_sec,(unsigned long long)ts->offer.time_avg.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"offer_time_avg %llu.%06llu %llu\n",(unsigned long long)ts->offer.time_avg.tv_sec,(unsigned long long)ts->offer.time_avg.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"answer_time_min %llu.%06llu %llu\n",(unsigned long long)ts->answer.time_min.tv_sec,(unsigned long long)ts->answer.time_min.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"answer_time_min %llu.%06llu %llu\n",(unsigned long long)ts->answer.time_min.tv_sec,(unsigned long long)ts->answer.time_min.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"answer_time_max %llu.%06llu %llu\n",(unsigned long long)ts->answer.time_max.tv_sec,(unsigned long long)ts->answer.time_max.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"answer_time_max %llu.%06llu %llu\n",(unsigned long long)ts->answer.time_max.tv_sec,(unsigned long long)ts->answer.time_max.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"answer_time_avg %llu.%06llu %llu\n",(unsigned long long)ts->answer.time_avg.tv_sec,(unsigned long long)ts->answer.time_avg.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"answer_time_avg %llu.%06llu %llu\n",(unsigned long long)ts->answer.time_avg.tv_sec,(unsigned long long)ts->answer.time_avg.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"delete_time_min %llu.%06llu %llu\n",(unsigned long long)ts->delete.time_min.tv_sec,(unsigned long long)ts->delete.time_min.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"delete_time_min %llu.%06llu %llu\n",(unsigned long long)ts->delete.time_min.tv_sec,(unsigned long long)ts->delete.time_min.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"delete_time_max %llu.%06llu %llu\n",(unsigned long long)ts->delete.time_max.tv_sec,(unsigned long long)ts->delete.time_max.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"delete_time_max %llu.%06llu %llu\n",(unsigned long long)ts->delete.time_max.tv_sec,(unsigned long long)ts->delete.time_max.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"delete_time_avg %llu.%06llu %llu\n",(unsigned long long)ts->delete.time_avg.tv_sec,(unsigned long long)ts->delete.time_avg.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"delete_time_avg %llu.%06llu %llu\n",(unsigned long long)ts->delete.time_avg.tv_sec,(unsigned long long)ts->delete.time_avg.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr, "call_dur %llu.%06llu %llu\n",(unsigned long long)ts->total_calls_duration_interval.tv_sec,(unsigned long long)ts->total_calls_duration_interval.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr, "call_dur %llu.%06llu %llu\n",(unsigned long long)ts->total_calls_duration_interval.tv_sec,(unsigned long long)ts->total_calls_duration_interval.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"average_call_dur %llu.%06llu %llu\n",(unsigned long long)ts->total_average_call_dur.tv_sec,(unsigned long long)ts->total_average_call_dur.tv_usec,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"average_call_dur %llu.%06llu %llu\n",(unsigned long long)ts->total_average_call_dur.tv_sec,(unsigned long long)ts->total_average_call_dur.tv_usec,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"forced_term_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_forced_term_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"forced_term_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_forced_term_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"managed_sess "UINT64F" %llu\n", ts->total_managed_sess,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"managed_sess "UINT64F" %llu\n", ts->total_managed_sess,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"managed_sess_min "UINT64F" %llu\n", ts->managed_sess_min,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"managed_sess_min "UINT64F" %llu\n", ts->managed_sess_min,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"managed_sess_max "UINT64F" %llu\n", ts->managed_sess_max,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"managed_sess_max "UINT64F" %llu\n", ts->managed_sess_max,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"current_sessions_total "UINT64F" %llu\n", ts->total_sessions,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"current_sessions_total "UINT64F" %llu\n", ts->total_sessions,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"current_sessions_own "UINT64F" %llu\n", ts->own_sessions,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"current_sessions_own "UINT64F" %llu\n", ts->own_sessions,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"current_sessions_foreign "UINT64F" %llu\n", ts->foreign_sessions,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"current_sessions_foreign "UINT64F" %llu\n", ts->foreign_sessions,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"nopacket_relayed_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_nopacket_relayed_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"nopacket_relayed_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_nopacket_relayed_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"oneway_stream_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_oneway_stream_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"oneway_stream_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_oneway_stream_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"regular_term_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_regular_term_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"regular_term_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_regular_term_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"relayed_errors "UINT64F" %llu\n", atomic64_get_na(&ts->total_relayed_errors),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"relayed_errors "UINT64F" %llu\n", atomic64_get_na(&ts->total_relayed_errors),(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"relayed_packets "UINT64F" %llu\n", atomic64_get_na(&ts->total_relayed_packets),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"relayed_packets "UINT64F" %llu\n", atomic64_get_na(&ts->total_relayed_packets),(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"silent_timeout_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_silent_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"silent_timeout_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_silent_timeout_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"final_timeout_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_final_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"final_timeout_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_final_timeout_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"timeout_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_timeout_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"timeout_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_timeout_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"reject_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_rejected_sess),(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"reject_sess "UINT64F" %llu\n", atomic64_get_na(&ts->total_rejected_sess),(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"offers_ps_min %llu %llu\n",(unsigned long long)ts->offers_ps.ps_min,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"offers_ps_min %llu %llu\n",(unsigned long long)ts->offers_ps.ps_min,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"offers_ps_max %llu %llu\n",(unsigned long long)ts->offers_ps.ps_max,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"offers_ps_max %llu %llu\n",(unsigned long long)ts->offers_ps.ps_max,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"offers_ps_avg %llu %llu\n",(unsigned long long)ts->offers_ps.ps_avg,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"offers_ps_avg %llu %llu\n",(unsigned long long)ts->offers_ps.ps_avg,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"answers_ps_min %llu %llu\n",(unsigned long long)ts->answers_ps.ps_min,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"answers_ps_min %llu %llu\n",(unsigned long long)ts->answers_ps.ps_min,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"answers_ps_max %llu %llu\n",(unsigned long long)ts->answers_ps.ps_max,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"answers_ps_max %llu %llu\n",(unsigned long long)ts->answers_ps.ps_max,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"answers_ps_avg %llu %llu\n",(unsigned long long)ts->answers_ps.ps_avg,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"answers_ps_avg %llu %llu\n",(unsigned long long)ts->answers_ps.ps_avg,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"deletes_ps_min %llu %llu\n",(unsigned long long)ts->deletes_ps.ps_min,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"deletes_ps_min %llu %llu\n",(unsigned long long)ts->deletes_ps.ps_min,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"deletes_ps_max %llu %llu\n",(unsigned long long)ts->deletes_ps.ps_max,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"deletes_ps_max %llu %llu\n",(unsigned long long)ts->deletes_ps.ps_max,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
if (graphite_prefix!=NULL) { rc = sprintf(ptr,"%s",graphite_prefix); ptr += rc; }
rc = sprintf(ptr,"deletes_ps_avg %llu %llu\n",(unsigned long long)ts->deletes_ps.ps_avg,(unsigned long long)g_now.tv_sec); ptr += rc;
rc = sprintf(ptr,"deletes_ps_avg %llu %llu\n",(unsigned long long)ts->deletes_ps.ps_avg,(unsigned long long)rtpe_now.tv_sec); ptr += rc;
ilog(LOG_DEBUG, "min_sessions:%llu max_sessions:%llu, call_dur_per_interval:%llu.%06llu at time %llu\n",
(unsigned long long) ts->managed_sess_min,
(unsigned long long) ts->managed_sess_max,
(unsigned long long ) ts->total_calls_duration_interval.tv_sec,
(unsigned long long ) ts->total_calls_duration_interval.tv_usec,
(unsigned long long ) g_now.tv_sec);
(unsigned long long ) rtpe_now.tv_sec);
ilog(LOG_DEBUG, "Min/Max/Avg offer processing delay: %llu.%06llu/%llu.%06llu/%llu.%06llu sec",
(unsigned long long)ts->offer.time_min.tv_sec,(unsigned long long)ts->offer.time_min.tv_usec,
@ -285,17 +283,11 @@ static inline void copy_with_lock(struct totalstats *ts_dst, struct totalstats *
mutex_unlock(ts_lock);
}
void graphite_loop_run(struct callmaster *cm, endpoint_t *graphite_ep, int seconds) {
void graphite_loop_run(endpoint_t *graphite_ep, int seconds) {
int rc=0;
struct pollfd wfds[1];
// sanity checks
if (!cm) {
ilog(LOG_ERROR, "NULL callmaster");
return ;
}
if (!graphite_ep) {
ilog(LOG_ERROR, "NULL graphite_ep");
return ;
@ -335,50 +327,42 @@ void graphite_loop_run(struct callmaster *cm, endpoint_t *graphite_ep, int secon
}
}
gettimeofday(&g_now, NULL);
if (g_now.tv_sec < next_run) {
gettimeofday(&rtpe_now, NULL);
if (rtpe_now.tv_sec < next_run) {
usleep(100000);
return;
}
next_run = g_now.tv_sec + seconds;
next_run = rtpe_now.tv_sec + seconds;
if (graphite_sock.fd < 0 && connection_state == STATE_DISCONNECTED) {
connect_to_graphite_server(graphite_ep);
}
if (graphite_sock.fd >= 0 && connection_state == STATE_CONNECTED) {
add_total_calls_duration_in_interval(cm, &graphite_interval_tv);
add_total_calls_duration_in_interval(&graphite_interval_tv);
rc = send_graphite_data(cm, &graphite_stats);
gettimeofday(&cm->latest_graphite_interval_start, NULL);
rc = send_graphite_data(&graphite_stats);
gettimeofday(&rtpe_latest_graphite_interval_start, NULL);
if (rc < 0) {
ilog(LOG_ERROR,"Sending graphite data failed.");
close_socket(&graphite_sock);
connection_state = STATE_DISCONNECTED;
}
copy_with_lock(&cm->totalstats_lastinterval, &graphite_stats, &cm->totalstats_lastinterval.total_average_lock);
copy_with_lock(&rtpe_totalstats_lastinterval, &graphite_stats, &rtpe_totalstats_lastinterval.total_average_lock);
}
}
void graphite_loop(void *d) {
struct callmaster *cm = d;
// sanity checks
if (!cm) {
ilog(LOG_ERROR, "NULL callmaster");
return ;
}
if (cm->conf.graphite_interval <= 0) {
if (rtpe_config.graphite_interval <= 0) {
ilog(LOG_WARNING,"Graphite send interval was not set. Setting it to 1 second.");
cm->conf.graphite_interval=1;
rtpe_config.graphite_interval=1;
}
connect_to_graphite_server(&cm->conf.graphite_ep);
connect_to_graphite_server(&rtpe_config.graphite_ep);
while (!g_shutdown)
graphite_loop_run(cm, &cm->conf.graphite_ep, cm->conf.graphite_interval); // time in seconds
while (!rtpe_shutdown)
graphite_loop_run(&rtpe_config.graphite_ep, rtpe_config.graphite_interval); // time in seconds
}

@ -16,9 +16,11 @@ enum connection_state {
STATE_CONNECTED,
};
extern struct timeval rtpe_latest_graphite_interval_start;
int connect_to_graphite_server(const endpoint_t *ep);
int send_graphite_data(struct callmaster *cm, struct totalstats *sent_data);
void graphite_loop_run(struct callmaster *cm, endpoint_t *graphite_ep, int seconds);
int send_graphite_data(struct totalstats *sent_data);
void graphite_loop_run(endpoint_t *graphite_ep, int seconds);
void set_prefix(char* prefix);
void graphite_loop(void *d);
void set_latest_graphite_interval_start(struct timeval *tv);

@ -240,7 +240,7 @@ static void __ice_agent_initialize(struct ice_agent *ag) {
create_random_ice_string(call, &ag->ufrag[1], 8);
create_random_ice_string(call, &ag->pwd[1], 26);
atomic64_set(&ag->last_activity, poller_now);
atomic64_set(&ag->last_activity, rtpe_now.tv_sec);
}
static struct ice_agent *__ice_agent_new(struct call_media *media) {
@ -315,7 +315,7 @@ void ice_update(struct ice_agent *ag, struct stream_params *sp) {
if (!ag)
return;
atomic64_set(&ag->last_activity, poller_now);
atomic64_set(&ag->last_activity, rtpe_now.tv_sec);
media = ag->media;
call = media->call;
@ -519,7 +519,7 @@ static void __ice_agent_free(void *p) {
static void __agent_schedule(struct ice_agent *ag, unsigned long usec) {
struct timeval nxt;
nxt = g_now;
nxt = rtpe_now;
timeval_add_usec(&nxt, usec);
__agent_schedule_abs(ag, &nxt);
}
@ -622,7 +622,7 @@ static void __do_ice_check(struct ice_candidate_pair *pair) {
mutex_lock(&ag->lock);
pair->retransmit = g_now;
pair->retransmit = rtpe_now;
if (!PAIR_SET(pair, IN_PROGRESS)) {
PAIR_CLEAR2(pair, FROZEN, FAILED);
pair->retransmit_ms = STUN_RETRANSMIT_INTERVAL;
@ -721,7 +721,7 @@ static void __do_ice_checks(struct ice_agent *ag) {
if (!ag->pwd[0].s)
return;
atomic64_set(&ag->last_activity, poller_now);
atomic64_set(&ag->last_activity, rtpe_now.tv_sec);
__DBG("running checks, call "STR_FORMAT" tag "STR_FORMAT"", STR_FMT(&ag->call->callid),
STR_FMT(&ag->media->monologue->tag));
@ -730,7 +730,7 @@ static void __do_ice_checks(struct ice_agent *ag) {
/* check if we're done and should start nominating pairs */
if (AGENT_ISSET(ag, CONTROLLING) && !AGENT_ISSET(ag, NOMINATING) && ag->start_nominating.tv_sec) {
if (timeval_cmp(&g_now, &ag->start_nominating) >= 0)
if (timeval_cmp(&rtpe_now, &ag->start_nominating) >= 0)
__nominate_pairs(ag);
timeval_lowest(&next_run, &ag->start_nominating);
}
@ -739,7 +739,7 @@ static void __do_ice_checks(struct ice_agent *ag) {
pair = g_queue_pop_head(&ag->triggered);
if (pair) {
PAIR_CLEAR(pair, TRIGGERED);
next_run = g_now;
next_run = rtpe_now;
goto check;
}
@ -764,7 +764,7 @@ static void __do_ice_checks(struct ice_agent *ag) {
if (valid && valid->pair_priority > pair->pair_priority)
continue;
if (timeval_cmp(&pair->retransmit, &g_now) <= 0)
if (timeval_cmp(&pair->retransmit, &rtpe_now) <= 0)
g_queue_push_tail(&retransmits, pair); /* can't run check directly
due to locks */
else
@ -1090,7 +1090,7 @@ int ice_request(struct stream_fd *sfd, const endpoint_t *src,
if (!ag)
return -1;
atomic64_set(&ag->last_activity, poller_now);
atomic64_set(&ag->last_activity, rtpe_now.tv_sec);
/* determine candidate pair */
mutex_lock(&ag->lock);
@ -1198,7 +1198,7 @@ int ice_response(struct stream_fd *sfd, const endpoint_t *src,
if (!ag)
return -1;
atomic64_set(&ag->last_activity, poller_now);
atomic64_set(&ag->last_activity, rtpe_now.tv_sec);
mutex_lock(&ag->lock);
@ -1261,7 +1261,7 @@ int ice_response(struct stream_fd *sfd, const endpoint_t *src,
if (!ag->start_nominating.tv_sec) {
if (__check_succeeded_complete(ag)) {
ag->start_nominating = g_now;
ag->start_nominating = rtpe_now;
timeval_add_usec(&ag->start_nominating, 100000);
__agent_schedule_abs(ag, &ag->start_nominating);
}
@ -1323,8 +1323,8 @@ void ice_thread_run(void *p) {
mutex_lock(&ice_agents_timers_lock);
while (!g_shutdown) {
gettimeofday(&g_now, NULL);
while (!rtpe_shutdown) {
gettimeofday(&rtpe_now, NULL);
/* lock our list and get the first element */
ag = g_tree_find_first(ice_agents_timers, NULL, NULL);
@ -1332,12 +1332,12 @@ void ice_thread_run(void *p) {
* steal the reference and run it */
if (!ag)
goto sleep;
if (timeval_cmp(&g_now, &ag->next_check) < 0)
if (timeval_cmp(&rtpe_now, &ag->next_check) < 0)
goto sleep;
g_tree_remove(ice_agents_timers, ag);
ZERO(ag->next_check);
ag->last_run = g_now;
ag->last_run = rtpe_now;
mutex_unlock(&ice_agents_timers_lock);
/* this agent is scheduled to run right now */
@ -1359,9 +1359,9 @@ void ice_thread_run(void *p) {
sleep:
/* figure out how long we should sleep */
sleeptime = ag ? timeval_diff(&ag->next_check, &g_now) : 100000;
sleeptime = ag ? timeval_diff(&ag->next_check, &rtpe_now) : 100000;
sleeptime = MIN(100000, sleeptime); /* 100 ms at the most */
tv = g_now;
tv = rtpe_now;
timeval_add_usec(&tv, sleeptime);
cond_timedwait(&ice_agents_timers_cond, &ice_agents_timers_lock, &tv);
continue;

@ -1,6 +1,5 @@
#include "iptables.h"
char *g_iptables_chain;
int (*iptables_add_rule)(const socket_t *local_sock, const str *comment);
int (*iptables_del_rule)(const socket_t *local_sock);
@ -23,6 +22,7 @@ int (*iptables_del_rule)(const socket_t *local_sock);
#include "log.h"
#include "socket.h"
#include "str.h"
#include "main.h"
#undef __ALIGN_KERNEL
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (__typeof(x))(a) - 1)
@ -139,7 +139,7 @@ static const char *ip4tables_add_rule(const socket_t *local_sock, const str *com
ip4_fill_entry(&entry, local_sock, comment);
err = "failed to append iptables entry";
if (!iptc_append_entry(g_iptables_chain, &entry.entry, h))
if (!iptc_append_entry(rtpe_config.iptables_chain, &entry.entry, h))
goto err;
err = "failed to commit iptables changes";
if (!iptc_commit(h))
@ -169,7 +169,7 @@ static const char *ip6tables_add_rule(const socket_t *local_sock, const str *com
ip6_fill_entry(&entry, local_sock, comment);
err = "failed to append ip6tables entry";
if (!ip6tc_append_entry(g_iptables_chain, &entry.entry, h))
if (!ip6tc_append_entry(rtpe_config.iptables_chain, &entry.entry, h))
goto err;
err = "failed to commit ip6tables changes";
if (!ip6tc_commit(h))
@ -207,7 +207,7 @@ static const char *ip4tables_del_rule(const socket_t *local_sock) {
memset(&mask.matches.target, 0xff, sizeof(mask.matches.target));
err = "failed to delete iptables entry";
if (!iptc_delete_entry(g_iptables_chain, &entry.entry, (unsigned char *) &mask, h))
if (!iptc_delete_entry(rtpe_config.iptables_chain, &entry.entry, (unsigned char *) &mask, h))
goto err;
err = "failed to commit iptables changes";
if (!iptc_commit(h))
@ -245,7 +245,7 @@ static const char *ip6tables_del_rule(const socket_t *local_sock) {
memset(&mask.matches.target, 0xff, sizeof(mask.matches.target));
err = "failed to delete ip6tables entry";
if (!ip6tc_delete_entry(g_iptables_chain, &entry.entry, (unsigned char *) &mask, h))
if (!ip6tc_delete_entry(rtpe_config.iptables_chain, &entry.entry, (unsigned char *) &mask, h))
goto err;
err = "failed to commit ip6tables changes";
if (!ip6tc_commit(h))
@ -313,10 +313,10 @@ static int __iptables_stub(void) {
void iptables_init(void) {
if (g_iptables_chain && !g_iptables_chain[0])
g_iptables_chain = NULL;
if (rtpe_config.iptables_chain && !rtpe_config.iptables_chain[0])
rtpe_config.iptables_chain = NULL;
if (!g_iptables_chain) {
if (!rtpe_config.iptables_chain) {
iptables_add_rule = (void *) __iptables_stub;
iptables_del_rule = (void *) __iptables_stub;
return;
@ -340,7 +340,7 @@ void iptables_init(void) {
if (!h)
goto out;
err = "could not flush iptables chain";
if (!iptc_flush_entries(g_iptables_chain, h))
if (!iptc_flush_entries(rtpe_config.iptables_chain, h))
goto err2;
err = "could not commit iptables changes";
if (!iptc_commit(h))
@ -352,7 +352,7 @@ void iptables_init(void) {
if (!h)
goto out;
err = "could not flush ip6tables chain";
if (!ip6tc_flush_entries(g_iptables_chain, h))
if (!ip6tc_flush_entries(rtpe_config.iptables_chain, h))
goto err1;
err = "could not commit iptables changes";
if (!ip6tc_commit(h))

@ -6,8 +6,6 @@
#include "str.h"
extern char *g_iptables_chain;
void iptables_init(void);
extern int (*iptables_add_rule)(const socket_t *local_sock, const str *comment);
extern int (*iptables_del_rule)(const socket_t *local_sock);

@ -28,15 +28,18 @@ struct kernel_interface kernel;
static int kernel_create_table(unsigned int id) {
static int kernel_action_table(const char *action, unsigned int id) {
char str[64];
int saved_errno;
int fd;
int i;
fd = open(PREFIX "/control", O_WRONLY | O_TRUNC);
if (fd == -1)
return -1;
sprintf(str, "add %u\n", id);
i = snprintf(str, sizeof(str), "%s %u\n", action, id);
if (i >= sizeof(str))
goto fail;
i = write(fd, str, strlen(str));
if (i == -1)
goto fail;
@ -45,12 +48,23 @@ static int kernel_create_table(unsigned int id) {
return 0;
fail:
saved_errno = errno;
close(fd);
errno = saved_errno;
return -1;
}
static int kernel_create_table(unsigned int id) {
return kernel_action_table("add", id);
}
static int kernel_delete_table(unsigned int id) {
return kernel_action_table("del", id);
}
static int kernel_open_table(unsigned int id) {
char str[64];
int saved_errno;
int fd;
struct rtpengine_message msg;
int i;
@ -69,7 +83,9 @@ static int kernel_open_table(unsigned int id) {
return fd;
fail:
saved_errno = errno;
close(fd);
errno = saved_errno;
return -1;
}
@ -79,6 +95,11 @@ int kernel_setup_table(unsigned int id) {
kernel.is_wanted = 1;
if (kernel_delete_table(id) && errno != ENOENT) {
ilog(LOG_ERR, "FAILED TO DELETE KERNEL TABLE %i (%s), KERNEL FORWARDING DISABLED",
id, strerror(errno));
return -1;
}
if (kernel_create_table(id)) {
ilog(LOG_ERR, "FAILED TO CREATE KERNEL TABLE %i (%s), KERNEL FORWARDING DISABLED",
id, strerror(errno));

@ -8,6 +8,7 @@
#include "poller.h"
#include "ice.h"
#include "loglib.h"
#include "main.h"
@ -17,46 +18,101 @@ struct log_info __thread log_info;
int _log_facility_cdr = 0;
int _log_facility_rtcp = 0;
typedef void (ilog_prefix_func)(char *prefix, size_t prefix_len);
static ilog_prefix_func ilog_prefix_default;
static ilog_prefix_func ilog_prefix_parsable;
static ilog_prefix_func *ilog_prefix = ilog_prefix_default;
static ilog_prefix_func * const ilog_prefix_funcs[__LF_LAST] = {
[LF_DEFAULT] = ilog_prefix_default,
[LF_PARSABLE] = ilog_prefix_parsable,
};
void __ilog(int prio, const char *fmt, ...) {
char prefix[300];
va_list ap;
static void ilog_prefix_default(char *prefix, size_t prefix_len) {
switch (log_info.e) {
case LOG_INFO_NONE:
prefix[0] = 0;
break;
case LOG_INFO_CALL:
snprintf(prefix, sizeof(prefix), "["STR_FORMAT"]: ",
snprintf(prefix, prefix_len, "["STR_FORMAT"]: ",
STR_FMT(&log_info.u.call->callid));
break;
case LOG_INFO_STREAM_FD:
if (log_info.u.stream_fd->call)
snprintf(prefix, sizeof(prefix), "["STR_FORMAT" port %5u]: ",
snprintf(prefix, prefix_len, "["STR_FORMAT" port %5u]: ",
STR_FMT(&log_info.u.stream_fd->call->callid),
log_info.u.stream_fd->socket.local.port);
break;
case LOG_INFO_STR:
snprintf(prefix, sizeof(prefix), "["STR_FORMAT"]: ",
snprintf(prefix, prefix_len, "["STR_FORMAT"]: ",
STR_FMT(log_info.u.str));
break;
case LOG_INFO_C_STRING:
snprintf(prefix, sizeof(prefix), "[%s]: ", log_info.u.cstr);
snprintf(prefix, prefix_len, "[%s]: ", log_info.u.cstr);
break;
case LOG_INFO_ICE_AGENT:
snprintf(prefix, sizeof(prefix), "["STR_FORMAT"/"STR_FORMAT"/%u]: ",
snprintf(prefix, prefix_len, "["STR_FORMAT"/"STR_FORMAT"/%u]: ",
STR_FMT(&log_info.u.ice_agent->call->callid),
STR_FMT(&log_info.u.ice_agent->media->monologue->tag),
log_info.u.ice_agent->media->index);
break;
}
}
static void ilog_prefix_parsable(char *prefix, size_t prefix_len) {
switch (log_info.e) {
case LOG_INFO_NONE:
prefix[0] = 0;
break;
case LOG_INFO_CALL:
snprintf(prefix, prefix_len, "[ID=\""STR_FORMAT"\"]: ",
STR_FMT(&log_info.u.call->callid));
break;
case LOG_INFO_STREAM_FD:
if (log_info.u.stream_fd->call)
snprintf(prefix, prefix_len, "[ID=\""STR_FORMAT"\" port=\"%5u\"]: ",
STR_FMT(&log_info.u.stream_fd->call->callid),
log_info.u.stream_fd->socket.local.port);
break;
case LOG_INFO_STR:
snprintf(prefix, prefix_len, "[ID=\""STR_FORMAT"\"]: ",
STR_FMT(log_info.u.str));
break;
case LOG_INFO_C_STRING:
snprintf(prefix, prefix_len, "[ID=\"%s\"]: ", log_info.u.cstr);
break;
case LOG_INFO_ICE_AGENT:
snprintf(prefix, prefix_len, "[ID=\""STR_FORMAT"\" tag=\""STR_FORMAT"\" index=\"%u\"]: ",
STR_FMT(&log_info.u.ice_agent->call->callid),
STR_FMT(&log_info.u.ice_agent->media->monologue->tag),
log_info.u.ice_agent->media->index);
break;
}
}
void __ilog(int prio, const char *fmt, ...) {
char prefix[300];
va_list ap;
ilog_prefix(prefix, sizeof(prefix));
va_start(ap, fmt);
__vpilog(prio, prefix, fmt, ap);
va_end(ap);
}
void log_format(enum log_format f) {
if (f < 0 || f >= __LF_LAST)
die("Invalid log format enum");
ilog_prefix = ilog_prefix_funcs[f];
if (!ilog_prefix)
die("Invalid log format enum");
}
void cdrlog(const char* cdrbuffer) {
if (_log_facility_cdr) {
syslog(LOG_INFO | _log_facility_cdr, "%s", cdrbuffer);

@ -10,6 +10,7 @@
struct call;
struct stream_fd;
struct ice_agent;
enum log_format;
struct log_info {
union {
@ -42,6 +43,7 @@ void cdrlog(const char* cdrbuffer);
void rtcplog(const char* cdrbuffer);
void log_format(enum log_format);
void __ilog(int prio, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));

@ -1,3 +1,5 @@
#include "main.h"
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
@ -34,53 +36,38 @@
#include "auxlib.h"
#include "rtcp.h"
#include "iptables.h"
#include "statistics.h"
#include "graphite.h"
struct main_context {
struct poller *p;
struct callmaster *m;
};
struct poller *rtpe_poller;
struct rtpengine_config rtpe_config = {
// non-zero defaults
.kernel_table = -1,
.max_sessions = -1,
.delete_delay = 30,
.redis_subscribed_keyspaces = G_QUEUE_INIT,
.redis_expires_secs = 86400,
.interfaces = G_QUEUE_INIT,
.homer_protocol = SOCK_DGRAM,
.homer_id = 2001,
.port_min = 30000,
.port_max = 40000,
.redis_db = -1,
.redis_write_db = -1,
.redis_allowed_errors = -1,
.redis_disable_time = 10,
.redis_connect_timeout = 1000,
.rec_method = "pcap",
.rec_format = "raw",
};
static GQueue interfaces = G_QUEUE_INIT;
static GQueue keyspaces = G_QUEUE_INIT;
static endpoint_t tcp_listen_ep;
static endpoint_t udp_listen_ep;
static endpoint_t ng_listen_ep;
static endpoint_t cli_listen_ep;
static endpoint_t graphite_ep;
static endpoint_t redis_ep;
static endpoint_t redis_write_ep;
static endpoint_t homer_ep;
static int homer_protocol = SOCK_DGRAM;
static int homer_id = 2001;
static int tos;
static int table = -1;
static int no_fallback;
static unsigned int timeout;
static unsigned int silent_timeout;
static unsigned int final_timeout;
static unsigned int redis_expires = 86400;
static int port_min = 30000;
static int port_max = 40000;
static int max_sessions = -1;
static int redis_db = -1;
static int redis_write_db = -1;
static int redis_num_threads;
static int no_redis_required;
static char *redis_auth;
static char *redis_write_auth;
static char *b2b_url;
static enum xmlrpc_format xmlrpc_fmt = XF_SEMS;
static int num_threads;
static int delete_delay = 30;
static int graphite_interval = 0;
static char *spooldir;
static char *rec_method = "pcap";
static char *rec_format = "raw";
static void sighandler(gpointer x) {
sigset_t ss;
@ -96,7 +83,7 @@ static void sighandler(gpointer x) {
ts.tv_sec = 0;
ts.tv_nsec = 100000000; /* 0.1 sec */
while (!g_shutdown) {
while (!rtpe_shutdown) {
ret = sigtimedwait(&ss, NULL, &ts);
if (ret == -1) {
if (errno == EAGAIN || errno == EINTR)
@ -105,17 +92,17 @@ static void sighandler(gpointer x) {
}
if (ret == SIGINT || ret == SIGTERM)
g_shutdown = 1;
rtpe_shutdown = 1;
else if (ret == SIGUSR1) {
if (get_log_level() > 0) {
g_atomic_int_add(&log_level, -1);
g_atomic_int_add(&rtpe_config.common.log_level, -1);
ilog(get_log_level(), "Set log level to %d\n",
get_log_level());
}
}
else if (ret == SIGUSR2) {
if (get_log_level() < 7) {
g_atomic_int_add(&log_level, 1);
g_atomic_int_add(&rtpe_config.common.log_level, 1);
ilog(get_log_level(), "Set log level to %d\n",
get_log_level());
}
@ -192,8 +179,12 @@ static struct intf_config *if_addr_parse(char *s) {
ifa->local_address.type = socktype_udp;
ifa->advertised_address.addr = adv;
ifa->advertised_address.type = ifa->local_address.type;
ifa->port_min = port_min;
ifa->port_max = port_max;
ifa->port_min = rtpe_config.port_min;
ifa->port_max = rtpe_config.port_max;
// handle "base:suffix" separation for round-robin selection
ifa->name_rr_spec = ifa->name;
str_token(&ifa->name_base, &ifa->name_rr_spec, ':'); // sets name_rr_spec to null string if no ':' found
return ifa;
}
@ -250,14 +241,15 @@ static void options(int *argc, char ***argv) {
char *redisps_write = NULL;
char *log_facility_cdr_s = NULL;
char *log_facility_rtcp_s = NULL;
char *log_format = NULL;
int sip_source = 0;
char *homerp = NULL;
char *homerproto = NULL;
char *endptr;
GOptionEntry e[] = {
{ "table", 't', 0, G_OPTION_ARG_INT, &table, "Kernel table to use", "INT" },
{ "no-fallback",'F', 0, G_OPTION_ARG_NONE, &no_fallback, "Only start when kernel module is available", NULL },
{ "table", 't', 0, G_OPTION_ARG_INT, &rtpe_config.kernel_table, "Kernel table to use", "INT" },
{ "no-fallback",'F', 0, G_OPTION_ARG_NONE, &rtpe_config.no_fallback, "Only start when kernel module is available", NULL },
{ "interface", 'i', 0, G_OPTION_ARG_STRING_ARRAY,&if_a, "Local interface for RTP", "[NAME/]IP[!IP]"},
{ "subscribe-keyspace", 'k', 0, G_OPTION_ARG_STRING_ARRAY,&ks_a, "Subscription keyspace list", "INT INT ..."},
{ "listen-tcp", 'l', 0, G_OPTION_ARG_STRING, &listenps, "TCP port to listen on", "[IP:]PORT" },
@ -265,42 +257,48 @@ static void options(int *argc, char ***argv) {
{ "listen-ng", 'n', 0, G_OPTION_ARG_STRING, &listenngs, "UDP port to listen on, NG protocol", "[IP46|HOSTNAME:]PORT" },
{ "listen-cli", 'c', 0, G_OPTION_ARG_STRING, &listencli, "UDP port to listen on, CLI", "[IP46|HOSTNAME:]PORT" },
{ "graphite", 'g', 0, G_OPTION_ARG_STRING, &graphitep, "Address of the graphite server", "IP46|HOSTNAME:PORT" },
{ "graphite-interval", 'G', 0, G_OPTION_ARG_INT, &graphite_interval, "Graphite send interval in seconds", "INT" },
{ "graphite-interval", 'G', 0, G_OPTION_ARG_INT, &rtpe_config.graphite_interval, "Graphite send interval in seconds", "INT" },
{ "graphite-prefix",0, 0, G_OPTION_ARG_STRING, &graphite_prefix_s, "Prefix for graphite line", "STRING"},
{ "tos", 'T', 0, G_OPTION_ARG_INT, &tos, "Default TOS value to set on streams", "INT" },
{ "timeout", 'o', 0, G_OPTION_ARG_INT, &timeout, "RTP timeout", "SECS" },
{ "silent-timeout",'s',0,G_OPTION_ARG_INT, &silent_timeout,"RTP timeout for muted", "SECS" },
{ "final-timeout",'a',0,G_OPTION_ARG_INT, &final_timeout, "Call timeout", "SECS" },
{ "port-min", 'm', 0, G_OPTION_ARG_INT, &port_min, "Lowest port to use for RTP", "INT" },
{ "port-max", 'M', 0, G_OPTION_ARG_INT, &port_max, "Highest port to use for RTP", "INT" },
{ "tos", 'T', 0, G_OPTION_ARG_INT, &rtpe_config.default_tos, "Default TOS value to set on streams", "INT" },
{ "control-tos",0 , 0, G_OPTION_ARG_INT, &rtpe_config.control_tos, "Default TOS value to set on control-ng", "INT" },
{ "timeout", 'o', 0, G_OPTION_ARG_INT, &rtpe_config.timeout, "RTP timeout", "SECS" },
{ "silent-timeout",'s',0,G_OPTION_ARG_INT, &rtpe_config.silent_timeout,"RTP timeout for muted", "SECS" },
{ "final-timeout",'a',0,G_OPTION_ARG_INT, &rtpe_config.final_timeout, "Call timeout", "SECS" },
{ "port-min", 'm', 0, G_OPTION_ARG_INT, &rtpe_config.port_min, "Lowest port to use for RTP", "INT" },
{ "port-max", 'M', 0, G_OPTION_ARG_INT, &rtpe_config.port_max, "Highest port to use for RTP", "INT" },
{ "redis", 'r', 0, G_OPTION_ARG_STRING, &redisps, "Connect to Redis database", "[PW@]IP:PORT/INT" },
{ "redis-write",'w', 0, G_OPTION_ARG_STRING, &redisps_write, "Connect to Redis write database", "[PW@]IP:PORT/INT" },
{ "redis-num-threads", 0, 0, G_OPTION_ARG_INT, &redis_num_threads, "Number of Redis restore threads", "INT" },
{ "redis-expires", 0, 0, G_OPTION_ARG_INT, &redis_expires, "Expire time in seconds for redis keys", "INT" },
{ "no-redis-required", 'q', 0, G_OPTION_ARG_NONE, &no_redis_required, "Start no matter of redis connection state", NULL },
{ "b2b-url", 'b', 0, G_OPTION_ARG_STRING, &b2b_url, "XMLRPC URL of B2B UA" , "STRING" },
{ "redis-num-threads", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_num_threads, "Number of Redis restore threads", "INT" },
{ "redis-expires", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_expires_secs, "Expire time in seconds for redis keys", "INT" },
{ "no-redis-required", 'q', 0, G_OPTION_ARG_NONE, &rtpe_config.no_redis_required, "Start no matter of redis connection state", NULL },
{ "redis-allowed-errors", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_allowed_errors, "Number of allowed errors before redis is temporarily disabled", "INT" },
{ "redis-disable-time", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_disable_time, "Number of seconds redis communication is disabled because of errors", "INT" },
{ "redis-cmd-timeout", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_cmd_timeout, "Sets a timeout in milliseconds for redis commands", "INT" },
{ "redis-connect-timeout", 0, 0, G_OPTION_ARG_INT, &rtpe_config.redis_connect_timeout, "Sets a timeout in milliseconds for redis connections", "INT" },
{ "b2b-url", 'b', 0, G_OPTION_ARG_STRING, &rtpe_config.b2b_url, "XMLRPC URL of B2B UA" , "STRING" },
{ "log-facility-cdr",0, 0, G_OPTION_ARG_STRING, &log_facility_cdr_s, "Syslog facility to use for logging CDRs", "daemon|local0|...|local7"},
{ "log-facility-rtcp",0, 0, G_OPTION_ARG_STRING, &log_facility_rtcp_s, "Syslog facility to use for logging RTCP", "daemon|local0|...|local7"},
{ "xmlrpc-format",'x', 0, G_OPTION_ARG_INT, &xmlrpc_fmt, "XMLRPC timeout request format to use. 0: SEMS DI, 1: call-id only", "INT" },
{ "num-threads", 0, 0, G_OPTION_ARG_INT, &num_threads, "Number of worker threads to create", "INT" },
{ "delete-delay", 'd', 0, G_OPTION_ARG_INT, &delete_delay, "Delay for deleting a session from memory.", "INT" },
{ "log-format", 0, 0, G_OPTION_ARG_STRING, &log_format, "Log prefix format", "default|parsable"},
{ "xmlrpc-format",'x', 0, G_OPTION_ARG_INT, &rtpe_config.fmt, "XMLRPC timeout request format to use. 0: SEMS DI, 1: call-id only", "INT" },
{ "num-threads", 0, 0, G_OPTION_ARG_INT, &rtpe_config.num_threads, "Number of worker threads to create", "INT" },
{ "delete-delay", 'd', 0, G_OPTION_ARG_INT, &rtpe_config.delete_delay, "Delay for deleting a session from memory.", "INT" },
{ "sip-source", 0, 0, G_OPTION_ARG_NONE, &sip_source, "Use SIP source address by default", NULL },
{ "dtls-passive", 0, 0, G_OPTION_ARG_NONE, &dtls_passive_def,"Always prefer DTLS passive role", NULL },
{ "max-sessions", 0, 0, G_OPTION_ARG_INT, &max_sessions, "Limit of maximum number of sessions", "INT" },
{ "max-sessions", 0, 0, G_OPTION_ARG_INT, &rtpe_config.max_sessions, "Limit of maximum number of sessions", "INT" },
{ "homer", 0, 0, G_OPTION_ARG_STRING, &homerp, "Address of Homer server for RTCP stats","IP46|HOSTNAME:PORT"},
{ "homer-protocol",0,0,G_OPTION_ARG_STRING, &homerproto, "Transport protocol for Homer (default udp)", "udp|tcp" },
{ "homer-id", 0, 0, G_OPTION_ARG_STRING, &homer_id, "'Capture ID' to use within the HEP protocol", "INT" },
{ "recording-dir", 0, 0, G_OPTION_ARG_STRING, &spooldir, "Directory for storing pcap and metadata files", "FILE" },
{ "recording-method",0, 0, G_OPTION_ARG_STRING, &rec_method, "Strategy for call recording", "pcap|proc" },
{ "recording-format",0, 0, G_OPTION_ARG_STRING, &rec_format, "File format for stored pcap files", "raw|eth" },
{ "homer-id", 0, 0, G_OPTION_ARG_STRING, &rtpe_config.homer_id, "'Capture ID' to use within the HEP protocol", "INT" },
{ "recording-dir", 0, 0, G_OPTION_ARG_STRING, &rtpe_config.spooldir, "Directory for storing pcap and metadata files", "FILE" },
{ "recording-method",0, 0, G_OPTION_ARG_STRING, &rtpe_config.rec_method, "Strategy for call recording", "pcap|proc" },
{ "recording-format",0, 0, G_OPTION_ARG_STRING, &rtpe_config.rec_format, "File format for stored pcap files", "raw|eth" },
#ifdef WITH_IPTABLES_OPTION
{ "iptables-chain",0,0, G_OPTION_ARG_STRING, &g_iptables_chain,"Add explicit firewall rules to this iptables chain","STRING" },
{ "iptables-chain",0,0, G_OPTION_ARG_STRING, &rtpe_config.iptables_chain,"Add explicit firewall rules to this iptables chain","STRING" },
#endif
{ NULL, }
};
config_load(argc, argv, e, " - next-generation media proxy",
"/etc/rtpengine/rtpengine.conf", "rtpengine");
"/etc/rtpengine/rtpengine.conf", "rtpengine", &rtpe_config.common);
if (!if_a)
die("Missing option --interface");
@ -311,7 +309,7 @@ static void options(int *argc, char ***argv) {
ifa = if_addr_parse(*iter);
if (!ifa)
die("Invalid interface specification: %s", *iter);
g_queue_push_tail(&interfaces, ifa);
g_queue_push_tail(&rtpe_config.interfaces, ifa);
}
if (ks_a) {
@ -326,29 +324,29 @@ static void options(int *argc, char ***argv) {
} else if (endptr == str_keyspace_db.s) {
ilog(LOG_ERR, "Fail adding keyspace %.*s to redis notifications; no digits found\n", str_keyspace_db.len, str_keyspace_db.s);
} else {
g_queue_push_tail(&keyspaces, GUINT_TO_POINTER(uint_keyspace_db));
g_queue_push_tail(&rtpe_config.redis_subscribed_keyspaces, GUINT_TO_POINTER(uint_keyspace_db));
}
}
}
if (listenps) {
if (endpoint_parse_any_getaddrinfo(&tcp_listen_ep, listenps))
if (endpoint_parse_any_getaddrinfo(&rtpe_config.tcp_listen_ep, listenps))
die("Invalid IP or port (--listen-tcp)");
}
if (listenudps) {
if (endpoint_parse_any_getaddrinfo(&udp_listen_ep, listenudps))
if (endpoint_parse_any_getaddrinfo(&rtpe_config.udp_listen_ep, listenudps))
die("Invalid IP or port (--listen-udp)");
}
if (listenngs) {
if (endpoint_parse_any_getaddrinfo(&ng_listen_ep, listenngs))
if (endpoint_parse_any_getaddrinfo(&rtpe_config.ng_listen_ep, listenngs))
die("Invalid IP or port (--listen-ng)");
}
if (listencli) {if (endpoint_parse_any_getaddrinfo(&cli_listen_ep, listencli))
if (listencli) {if (endpoint_parse_any_getaddrinfo(&rtpe_config.cli_listen_ep, listencli))
die("Invalid IP or port (--listen-cli)");
}
if (graphitep) {if (endpoint_parse_any_getaddrinfo_full(&graphite_ep, graphitep))
if (graphitep) {if (endpoint_parse_any_getaddrinfo_full(&rtpe_config.graphite_ep, graphitep))
die("Invalid IP or port (--graphite)");
}
@ -356,45 +354,45 @@ static void options(int *argc, char ***argv) {
set_prefix(graphite_prefix_s);
if (homerp) {
if (endpoint_parse_any_getaddrinfo_full(&homer_ep, homerp))
if (endpoint_parse_any_getaddrinfo_full(&rtpe_config.homer_ep, homerp))
die("Invalid IP or port (--homer)");
}
if (homerproto) {
if (!strcmp(homerproto, "tcp"))
homer_protocol = SOCK_STREAM;
rtpe_config.homer_protocol = SOCK_STREAM;
else if (!strcmp(homerproto, "udp"))
homer_protocol = SOCK_DGRAM;
rtpe_config.homer_protocol = SOCK_DGRAM;
else
die("Invalid protocol (--homer-protocol)");
}
if (tos < 0 || tos > 255)
if (rtpe_config.default_tos < 0 || rtpe_config.default_tos > 255)
die("Invalid TOS value");
if (timeout <= 0)
timeout = 60;
if (rtpe_config.control_tos < 0 || rtpe_config.control_tos > 255)
die("Invalid control-ng TOS value");
if (rtpe_config.timeout <= 0)
rtpe_config.timeout = 60;
if (silent_timeout <= 0)
silent_timeout = 3600;
if (rtpe_config.silent_timeout <= 0)
rtpe_config.silent_timeout = 3600;
if (final_timeout <= 0)
final_timeout = 0;
if (rtpe_config.final_timeout <= 0)
rtpe_config.final_timeout = 0;
if (redisps)
if (redis_ep_parse(&redis_ep, &redis_db, &redis_auth, "RTPENGINE_REDIS_AUTH_PW", redisps))
if (redis_ep_parse(&rtpe_config.redis_ep, &rtpe_config.redis_db, &rtpe_config.redis_auth, "RTPENGINE_REDIS_AUTH_PW", redisps))
die("Invalid Redis endpoint [IP:PORT/INT] (--redis)");
if (redisps_write)
if (redis_ep_parse(&redis_write_ep, &redis_write_db, &redis_write_auth,
if (redis_ep_parse(&rtpe_config.redis_write_ep, &rtpe_config.redis_write_db, &rtpe_config.redis_write_auth,
"RTPENGINE_REDIS_WRITE_AUTH_PW", redisps_write))
die("Invalid Redis endpoint [IP:PORT/INT] (--redis-write)");
if (xmlrpc_fmt > 1)
if (rtpe_config.fmt > 1)
die("Invalid XMLRPC format");
if ((log_level < LOG_EMERG) || (log_level > LOG_DEBUG))
die("Invalid log level (--log_level)");
if (log_facility_cdr_s) {
if (!parse_log_facility(log_facility_cdr_s, &_log_facility_cdr)) {
print_available_log_facilities();
@ -409,6 +407,15 @@ static void options(int *argc, char ***argv) {
}
}
if (log_format) {
if (!strcmp(log_format, "default"))
rtpe_config.log_format = LF_DEFAULT;
else if (!strcmp(log_format, "parsable"))
rtpe_config.log_format = LF_PARSABLE;
else
die("Invalid --log-format option");
}
if (!sip_source)
trust_address_def = 1;
}
@ -460,7 +467,8 @@ static void init_everything() {
struct timespec ts;
log_init("rtpengine");
recording_fs_init(spooldir, rec_method, rec_format);
log_format(rtpe_config.log_format);
recording_fs_init(rtpe_config.spooldir, rtpe_config.rec_method, rtpe_config.rec_format);
clock_gettime(CLOCK_REALTIME, &ts);
srandom(ts.tv_sec ^ ts.tv_nsec);
SSL_library_init();
@ -481,13 +489,16 @@ static void init_everything() {
dtls_init();
ice_init();
crypto_init_main();
interfaces_init(&interfaces);
interfaces_init(&rtpe_config.interfaces);
iptables_init();
control_ng_init();
if (call_interfaces_init())
abort();
statistics_init();
}
static void create_everything(struct main_context *ctx) {
struct callmaster_config mc;
static void create_everything(void) {
struct control_tcp *ct;
struct control_udp *cu;
struct control_ng *cn;
@ -496,10 +507,10 @@ static void create_everything(struct main_context *ctx) {
struct timeval redis_start, redis_stop;
double redis_diff = 0;
if (table < 0)
if (rtpe_config.kernel_table < 0)
goto no_kernel;
if (kernel_setup_table(table)) {
if (no_fallback) {
if (kernel_setup_table(rtpe_config.kernel_table)) {
if (rtpe_config.no_fallback) {
ilog(LOG_CRIT, "Userspace fallback disallowed - exiting");
exit(-1);
}
@ -507,109 +518,102 @@ static void create_everything(struct main_context *ctx) {
}
no_kernel:
ctx->p = poller_new();
if (!ctx->p)
rtpe_poller = poller_new();
if (!rtpe_poller)
die("poller creation failed");
ctx->m = callmaster_new(ctx->p);
if (!ctx->m)
die("callmaster creation failed");
dtls_timer(rtpe_poller);
dtls_timer(ctx->p);
if (call_init())
abort();
ZERO(mc);
rwlock_init(&mc.config_lock);
if (max_sessions < -1) {
max_sessions = -1;
rwlock_init(&rtpe_config.config_lock);
if (rtpe_config.max_sessions < -1) {
rtpe_config.max_sessions = -1;
}
mc.max_sessions = max_sessions;
mc.timeout = timeout;
mc.silent_timeout = silent_timeout;
mc.final_timeout = final_timeout;
mc.delete_delay = delete_delay;
mc.default_tos = tos;
mc.b2b_url = b2b_url;
mc.fmt = xmlrpc_fmt;
mc.graphite_ep = graphite_ep;
mc.graphite_interval = graphite_interval;
mc.redis_subscribed_keyspaces = g_queue_copy(&keyspaces);
if (redis_num_threads < 1) {
if (rtpe_config.redis_num_threads < 1) {
#ifdef _SC_NPROCESSORS_ONLN
redis_num_threads = sysconf( _SC_NPROCESSORS_ONLN );
rtpe_config.redis_num_threads = sysconf( _SC_NPROCESSORS_ONLN );
#endif
if (redis_num_threads < 1) {
redis_num_threads = REDIS_RESTORE_NUM_THREADS;
if (rtpe_config.redis_num_threads < 1) {
rtpe_config.redis_num_threads = REDIS_RESTORE_NUM_THREADS;
}
}
mc.redis_num_threads = redis_num_threads;
ct = NULL;
if (tcp_listen_ep.port) {
ct = control_tcp_new(ctx->p, &tcp_listen_ep, ctx->m);
if (rtpe_config.tcp_listen_ep.port) {
ct = control_tcp_new(rtpe_poller, &rtpe_config.tcp_listen_ep);
if (!ct)
die("Failed to open TCP control connection port");
}
cu = NULL;
if (udp_listen_ep.port) {
interfaces_exclude_port(udp_listen_ep.port);
cu = control_udp_new(ctx->p, &udp_listen_ep, ctx->m);
if (rtpe_config.udp_listen_ep.port) {
interfaces_exclude_port(rtpe_config.udp_listen_ep.port);
cu = control_udp_new(rtpe_poller, &rtpe_config.udp_listen_ep);
if (!cu)
die("Failed to open UDP control connection port");
}
cn = NULL;
if (ng_listen_ep.port) {
interfaces_exclude_port(ng_listen_ep.port);
cn = control_ng_new(ctx->p, &ng_listen_ep, ctx->m);
if (rtpe_config.ng_listen_ep.port) {
interfaces_exclude_port(rtpe_config.ng_listen_ep.port);
cn = control_ng_new(rtpe_poller, &rtpe_config.ng_listen_ep, rtpe_config.control_tos);
if (!cn)
die("Failed to open UDP control connection port");
}
cl = NULL;
if (cli_listen_ep.port) {
interfaces_exclude_port(cli_listen_ep.port);
cl = cli_new(ctx->p, &cli_listen_ep, ctx->m);
if (rtpe_config.cli_listen_ep.port) {
interfaces_exclude_port(rtpe_config.cli_listen_ep.port);
cl = cli_new(rtpe_poller, &rtpe_config.cli_listen_ep);
if (!cl)
die("Failed to open UDP CLI connection port");
}
if (!is_addr_unspecified(&redis_write_ep.address)) {
mc.redis_write = redis_new(&redis_write_ep, redis_write_db, redis_write_auth, ANY_REDIS_ROLE, no_redis_required);
if (!mc.redis_write)
if (!is_addr_unspecified(&rtpe_config.redis_write_ep.address)) {
rtpe_redis_write = redis_new(&rtpe_config.redis_write_ep,
rtpe_config.redis_write_db, rtpe_config.redis_write_auth,
ANY_REDIS_ROLE, rtpe_config.no_redis_required,
rtpe_config.redis_allowed_errors,
rtpe_config.redis_disable_time, rtpe_config.redis_cmd_timeout,
rtpe_config.redis_connect_timeout);
if (!rtpe_redis_write)
die("Cannot start up without running Redis %s write database! See also NO_REDIS_REQUIRED parameter.",
endpoint_print_buf(&redis_write_ep));
endpoint_print_buf(&rtpe_config.redis_write_ep));
}
if (!is_addr_unspecified(&redis_ep.address)) {
mc.redis = redis_new(&redis_ep, redis_db, redis_auth, mc.redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, no_redis_required);
mc.redis_notify = redis_new(&redis_ep, redis_db, redis_auth, mc.redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, no_redis_required);
if (!mc.redis || !mc.redis_notify)
if (!is_addr_unspecified(&rtpe_config.redis_ep.address)) {
rtpe_redis = redis_new(&rtpe_config.redis_ep, rtpe_config.redis_db, rtpe_config.redis_auth, rtpe_redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, rtpe_config.no_redis_required,
rtpe_config.redis_allowed_errors,
rtpe_config.redis_disable_time, rtpe_config.redis_cmd_timeout,
rtpe_config.redis_connect_timeout);
rtpe_redis_notify = redis_new(&rtpe_config.redis_ep, rtpe_config.redis_db, rtpe_config.redis_auth, rtpe_redis_write ? ANY_REDIS_ROLE : MASTER_REDIS_ROLE, rtpe_config.no_redis_required,
rtpe_config.redis_allowed_errors,
rtpe_config.redis_disable_time, rtpe_config.redis_cmd_timeout,
rtpe_config.redis_connect_timeout);
if (!rtpe_redis || !rtpe_redis_notify)
die("Cannot start up without running Redis %s database! See also NO_REDIS_REQUIRED parameter.",
endpoint_print_buf(&redis_ep));
endpoint_print_buf(&rtpe_config.redis_ep));
if (!mc.redis_write)
mc.redis_write = mc.redis;
if (!rtpe_redis_write)
rtpe_redis_write = rtpe_redis;
}
mc.redis_expires_secs = redis_expires;
ctx->m->conf = mc;
daemonize();
wpidfile();
homer_sender_init(&homer_ep, homer_protocol, homer_id);
homer_sender_init(&rtpe_config.homer_ep, rtpe_config.homer_protocol, rtpe_config.homer_id);
rtcp_init(); // must come after Homer init
if (mc.redis) {
if (rtpe_redis) {
// start redis restore timer
gettimeofday(&redis_start, NULL);
// restore
if (redis_restore(ctx->m, mc.redis))
if (redis_restore(rtpe_redis))
die("Refusing to continue without working Redis database");
// stop redis restore timer
@ -620,54 +624,53 @@ no_kernel:
ilog(LOG_INFO, "Redis restore time = %.0lf ms", redis_diff);
}
gettimeofday(&ctx->m->latest_graphite_interval_start, NULL);
gettimeofday(&rtpe_latest_graphite_interval_start, NULL);
timeval_from_us(&tmp_tv, (long long) graphite_interval*1000000);
timeval_from_us(&tmp_tv, (long long) rtpe_config.graphite_interval*1000000);
set_graphite_interval_tv(&tmp_tv);
}
int main(int argc, char **argv) {
struct main_context ctx;
int idx=0;
early_init();
options(&argc, &argv);
init_everything();
create_everything(&ctx);
create_everything();
ilog(LOG_INFO, "Startup complete, version %s", RTPENGINE_VERSION);
thread_create_detach(sighandler, NULL);
thread_create_detach(poller_timer_loop, ctx.p);
thread_create_detach(poller_timer_loop, rtpe_poller);
if (!is_addr_unspecified(&redis_ep.address))
thread_create_detach(redis_notify_loop, ctx.m);
if (!is_addr_unspecified(&rtpe_config.redis_ep.address))
thread_create_detach(redis_notify_loop, NULL);
if (!is_addr_unspecified(&graphite_ep.address))
thread_create_detach(graphite_loop, ctx.m);
if (!is_addr_unspecified(&rtpe_config.graphite_ep.address))
thread_create_detach(graphite_loop, NULL);
thread_create_detach(ice_thread_run, NULL);
if (num_threads < 1) {
if (rtpe_config.num_threads < 1) {
#ifdef _SC_NPROCESSORS_ONLN
num_threads = sysconf( _SC_NPROCESSORS_ONLN ) + 3;
rtpe_config.num_threads = sysconf( _SC_NPROCESSORS_ONLN ) + 3;
#endif
if (num_threads <= 1)
num_threads = 4;
if (rtpe_config.num_threads <= 1)
rtpe_config.num_threads = 4;
}
for (;idx<num_threads;++idx) {
thread_create_detach(poller_loop, ctx.p);
for (;idx<rtpe_config.num_threads;++idx) {
thread_create_detach(poller_loop, rtpe_poller);
}
while (!g_shutdown) {
while (!rtpe_shutdown) {
usleep(100000);
threads_join_all(0);
}
if (!is_addr_unspecified(&redis_ep.address))
redis_notify_event_base_action(ctx.m, EVENT_BASE_LOOPBREAK);
if (!is_addr_unspecified(&rtpe_config.redis_ep.address))
redis_notify_event_base_action(EVENT_BASE_LOOPBREAK);
threads_join_all(1);

@ -0,0 +1,81 @@
#ifndef _MAIN_H_
#define _MAIN_H_
#include "aux.h"
#include <glib.h>
#include "socket.h"
#include "auxlib.h"
enum xmlrpc_format {
XF_SEMS = 0,
XF_CALLID,
};
enum log_format {
LF_DEFAULT = 0,
LF_PARSABLE,
__LF_LAST
};
struct rtpengine_config {
/* everything below protected by config_lock */
rwlock_t config_lock;
struct rtpengine_common_config common;
int kernel_table;
int max_sessions;
int timeout;
int silent_timeout;
int final_timeout;
int delete_delay;
GQueue redis_subscribed_keyspaces;
int redis_expires_secs;
char *b2b_url;
int default_tos;
int control_tos;
enum xmlrpc_format fmt;
enum log_format log_format;
endpoint_t graphite_ep;
int graphite_interval;
int redis_num_threads;
GQueue interfaces;
endpoint_t tcp_listen_ep;
endpoint_t udp_listen_ep;
endpoint_t ng_listen_ep;
endpoint_t cli_listen_ep;
endpoint_t redis_ep;
endpoint_t redis_write_ep;
endpoint_t homer_ep;
int homer_protocol;
int homer_id;
int no_fallback;
int port_min;
int port_max;
int redis_db;
int redis_write_db;
int no_redis_required;
int redis_allowed_errors;
int redis_disable_time;
int redis_cmd_timeout;
int redis_connect_timeout;
char *redis_auth;
char *redis_write_auth;
int num_threads;
char *spooldir;
char *rec_method;
char *rec_format;
char *iptables_chain;
};
struct poller;
extern struct poller *rtpe_poller; // main global poller instance XXX convert to struct instead of pointer?
extern struct rtpengine_config rtpe_config;
#endif

@ -23,6 +23,7 @@
#include "rtcplib.h"
#include "ssrc.h"
#include "iptables.h"
#include "main.h"
#ifndef PORT_RANDOM_MIN
@ -48,6 +49,12 @@ struct streamhandler {
const struct streamhandler_io *in;
const struct streamhandler_io *out;
};
struct intf_rr {
struct logical_intf hash_key;
mutex_t lock;
GQueue logical_intfs;
struct logical_intf *singular; // set iff only one is present in the list - no lock needed
};
static void determine_handler(struct packet_stream *in, const struct packet_stream *out);
@ -74,6 +81,9 @@ static int call_savpf2avp_rtcp(str *s, struct packet_stream *, struct stream_fd
//static int call_savpf2savp_rtcp(str *s, struct packet_stream *);
static struct logical_intf *__get_logical_interface(const str *name, sockfamily_t *fam);
@ -256,13 +266,12 @@ static const struct rtpengine_srtp __res_null = {
static GQueue *__interface_list_for_family(sockfamily_t *fam);
static GHashTable *__logical_intf_name_family_hash;
static GHashTable *__intf_spec_addr_type_hash;
static GHashTable *__local_intf_addr_type_hash; // hash of lists
static GHashTable *__logical_intf_name_family_hash; // name + family -> struct logical_intf
static GHashTable *__logical_intf_name_family_rr_hash; // name + family -> struct intf_rr
static GHashTable *__intf_spec_addr_type_hash; // addr + type -> struct intf_spec
static GHashTable *__local_intf_addr_type_hash; // addr + type -> GList of struct local_intf
static GQueue __preferred_lists_for_family[__SF_LAST];
static __thread unsigned int selection_index = 0;
static __thread unsigned int selection_count = 0;
/* checks for free no_ports on a local interface */
@ -272,7 +281,7 @@ static int has_free_ports_loc(struct local_intf *loc, unsigned int num_ports) {
return 0;
}
if (num_ports > loc->spec->port_pool.free_ports) {
if (num_ports > g_atomic_int_get(&loc->spec->port_pool.free_ports)) {
ilog(LOG_ERR, "Didn't found %d ports available for %.*s/%s",
num_ports, loc->logical->name.len, loc->logical->name.s,
sockaddr_print_buf(&loc->spec->local_address.addr));
@ -332,64 +341,49 @@ static int has_free_ports_log_all(struct logical_intf *log, unsigned int num_por
}
/* run round-robin-calls algorithm */
static struct logical_intf* run_round_robin_calls(GQueue *q, unsigned int num_ports) {
static struct logical_intf* run_round_robin_calls(struct intf_rr *rr, unsigned int num_ports) {
struct logical_intf *log = NULL;
volatile unsigned int nr_tries = 0;
unsigned int nr_logs = 0;
nr_logs = g_queue_get_length(q);
mutex_lock(&rr->lock);
select_log:
// choose the next logical interface
log = g_queue_peek_nth(q, selection_index);
if (!log) {
if (selection_index == 0)
return NULL;
selection_index = 0;
goto select_log;
}
__C_DBG("Trying %d ports on logical interface %.*s", num_ports, log->name.len, log->name.s);
unsigned int max_tries = rr->logical_intfs.length;
unsigned int num_tries = 0;
// test for free ports for the logical interface
if(!has_free_ports_log_all(log, num_ports)) {
// count the logical interfaces tried
nr_tries++;
while (num_tries++ < max_tries) {
log = g_queue_pop_head(&rr->logical_intfs);
g_queue_push_tail(&rr->logical_intfs, log);
// the logical interface selected has no ports available, try another one
selection_index ++;
selection_index = selection_index % nr_logs;
mutex_unlock(&rr->lock);
// all the logical interfaces have no ports available
if (nr_tries == nr_logs) {
ilog(LOG_ERR, "No logical interface with free ports found; fallback to default behaviour");
return NULL;
}
__C_DBG("Trying %d ports on logical interface " STR_FORMAT, num_ports, STR_FMT(&log->name));
if (has_free_ports_log_all(log, num_ports))
goto done;
log = NULL;
goto select_log;
mutex_lock(&rr->lock);
}
__C_DBG("Round Robin Calls algorithm found logical %.*s; count=%u index=%u", log->name.len, log->name.s, selection_count, selection_index);
mutex_unlock(&rr->lock);
// 1 stream => 2 x get_logical_interface calls at offer
// 2 streams => 4 x get_logical_interface calls at offer
selection_count ++;
if (selection_count % (num_ports / 2) == 0) {
selection_count = 0;
selection_index ++;
selection_index = selection_index % nr_logs;
done:
if (!log) {
ilog(LOG_ERR, "No logical interface with free ports found; fallback to default behaviour");
return NULL;
}
__C_DBG("Round Robin Calls algorithm found logical " STR_FORMAT, STR_FMT(&log->name));
return log;
}
// 'fam' may only be NULL if 'name' is also NULL
struct logical_intf *get_logical_interface(const str *name, sockfamily_t *fam, int num_ports) {
struct logical_intf d, *log = NULL;
struct logical_intf *log = NULL;
__C_DBG("Get logical interface for %d ports", num_ports);
if (G_UNLIKELY(!name || !name->s ||
str_cmp(name, ALGORITHM_ROUND_ROBIN_CALLS) == 0)) {
if (G_UNLIKELY(!name || !name->s)) {
// trivial case: no interface given. just pick one suitable for the address family.
// always used for legacy TCP and UDP protocols.
GQueue *q;
if (fam)
q = __interface_list_for_family(fam);
@ -404,35 +398,46 @@ got_some:
;
}
// round-robin-calls behaviour - return next interface with free ports
if (name && name->s && str_cmp(name, ALGORITHM_ROUND_ROBIN_CALLS) == 0 && num_ports > 0) {
log = run_round_robin_calls(q, num_ports);
if (log) {
__C_DBG("Choose logical interface %.*s because of direction %.*s",
log->name.len, log->name.s,
name->len, name->s);
} else {
__C_DBG("Choose logical interface NULL because of direction %.*s",
name->len, name->s);
}
return log;
}
// default behaviour - return first logical interface
return q->head ? q->head->data : NULL;
}
// check if round-robin is desired
struct logical_intf key;
key.name = *name;
key.preferred_family = fam;
struct intf_rr *rr = g_hash_table_lookup(__logical_intf_name_family_rr_hash, &key);
if (!rr)
return __get_logical_interface(name, fam);
if (rr->singular) {
__C_DBG("Returning non-RR logical interface '" STR_FORMAT "' based on direction '" \
STR_FORMAT "'",
STR_FMT(&rr->singular->name),
STR_FMT(name));
return rr->singular;
}
__C_DBG("Running RR interface selection for direction '" STR_FORMAT "'",
STR_FMT(name));
log = run_round_robin_calls(rr, num_ports);
if (log)
return log;
return __get_logical_interface(name, fam);
}
static struct logical_intf *__get_logical_interface(const str *name, sockfamily_t *fam) {
struct logical_intf d, *log = NULL;
d.name = *name;
d.preferred_family = fam;
log = g_hash_table_lookup(__logical_intf_name_family_hash, &d);
if (log) {
__C_DBG("Choose logical interface %.*s because of direction %.*s",
log->name.len, log->name.s,
name->len, name->s);
__C_DBG("Choose logical interface " STR_FORMAT " because of direction " STR_FORMAT,
STR_FMT(&log->name),
STR_FMT(name));
} else {
__C_DBG("Choose logical interface NULL because of direction %.*s",
name->len, name->s);
__C_DBG("Choose logical interface NULL because of direction " STR_FORMAT,
STR_FMT(name));
}
return log;
@ -482,26 +487,50 @@ int is_local_endpoint(const struct intf_address *addr, unsigned int port) {
}
// called during single-threaded startup only
static void __add_intf_rr_1(struct logical_intf *lif, str *name_base, sockfamily_t *fam) {
struct logical_intf key;
key.name = *name_base;
key.preferred_family = fam;
struct intf_rr *rr = g_hash_table_lookup(__logical_intf_name_family_rr_hash, &key);
if (!rr) {
rr = g_slice_alloc0(sizeof(*rr));
rr->hash_key = key;
mutex_init(&rr->lock);
g_hash_table_insert(__logical_intf_name_family_rr_hash, &rr->hash_key, rr);
}
g_queue_push_tail(&rr->logical_intfs, lif);
rr->singular = (rr->logical_intfs.length == 1) ? lif : NULL;
g_hash_table_insert(lif->rr_specs, &rr->hash_key.name, lif);
}
static void __add_intf_rr(struct logical_intf *lif, str *name_base, sockfamily_t *fam) {
__add_intf_rr_1(lif, name_base, fam);
static str legacy_rr_str = STR_CONST_INIT("round-robin-calls");
__add_intf_rr_1(lif, &legacy_rr_str, fam);
}
static GQueue *__interface_list_for_family(sockfamily_t *fam) {
return &__preferred_lists_for_family[fam->idx];
}
// called during single-threaded startup only
static void __interface_append(struct intf_config *ifa, sockfamily_t *fam) {
struct logical_intf *lif;
GQueue *q;
struct local_intf *ifc;
struct intf_spec *spec;
lif = get_logical_interface(&ifa->name, fam, 0);
lif = __get_logical_interface(&ifa->name, fam);
if (!lif) {
lif = g_slice_alloc0(sizeof(*lif));
lif->name = ifa->name;
lif->preferred_family = fam;
lif->addr_hash = g_hash_table_new(__addr_type_hash, __addr_type_eq);
lif->rr_specs = g_hash_table_new(str_hash, str_equal);
g_hash_table_insert(__logical_intf_name_family_hash, lif, lif);
if (ifa->local_address.addr.family == fam) {
q = __interface_list_for_family(fam);
g_queue_push_tail(q, lif);
__add_intf_rr(lif, &ifa->name_base, fam);
}
}
@ -527,6 +556,7 @@ static void __interface_append(struct intf_config *ifa, sockfamily_t *fam) {
__insert_local_intf_addr_type(&ifc->advertised_address, ifc);
}
// called during single-threaded startup only
void interfaces_init(GQueue *interfaces) {
int i;
GList *l;
@ -535,6 +565,7 @@ void interfaces_init(GQueue *interfaces) {
/* init everything */
__logical_intf_name_family_hash = g_hash_table_new(__name_family_hash, __name_family_eq);
__logical_intf_name_family_rr_hash = g_hash_table_new(__name_family_hash, __name_family_eq);
__intf_spec_addr_type_hash = g_hash_table_new(__addr_type_hash, __addr_type_eq);
__local_intf_addr_type_hash = g_hash_table_new(__addr_type_hash, __addr_type_eq);
@ -1167,7 +1198,6 @@ static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin,
unk = 0;
int i;
struct call *call;
struct callmaster *cm;
/*unsigned char cc;*/
struct endpoint endpoint;
rewrite_func rwf_in, rwf_out;
@ -1178,7 +1208,6 @@ static int stream_packet(struct stream_fd *sfd, str *s, const endpoint_t *fsin,
struct ssrc_ctx *ssrc_in = NULL, *ssrc_out = NULL;
call = sfd->call;
cm = call->callmaster;
rwlock_lock_r(&call->master_lock);
@ -1274,7 +1303,8 @@ loop_ok:
if (G_LIKELY(media->protocol && media->protocol->rtp)) {
if (G_LIKELY(!rtcp && !rtp_payload(&rtp_h, NULL, s))) {
__stream_ssrc(in_srtp, out_srtp, rtp_h->ssrc, &ssrc_in, &ssrc_out, call->ssrc_hash);
if (G_LIKELY(out_srtp != NULL))
__stream_ssrc(in_srtp, out_srtp, rtp_h->ssrc, &ssrc_in, &ssrc_out, call->ssrc_hash);
// check the payload type
i = (rtp_h->m_pt & 0x7f);
@ -1287,7 +1317,7 @@ loop_ok:
ilog(LOG_WARNING | LOG_FLAG_LIMIT,
"RTP packet with unknown payload type %u received", i);
atomic64_inc(&stream->stats.errors);
atomic64_inc(&cm->statsps.errors);
atomic64_inc(&rtpe_statsps.errors);
}
else {
@ -1296,16 +1326,17 @@ loop_ok:
}
}
else if (rtcp && !rtcp_payload(&rtcp_h, NULL, s)) {
__stream_ssrc(in_srtp, out_srtp, rtcp_h->ssrc, &ssrc_in, &ssrc_out, call->ssrc_hash);
if (G_LIKELY(out_srtp != NULL))
__stream_ssrc(in_srtp, out_srtp, rtcp_h->ssrc, &ssrc_in, &ssrc_out, call->ssrc_hash);
}
}
/* do we have somewhere to forward it to? */
if (G_UNLIKELY(!sink || !sink->selected_sfd || !out_srtp->selected_sfd || !in_srtp->selected_sfd)) {
if (G_UNLIKELY(!sink || !sink->selected_sfd || !out_srtp || !out_srtp->selected_sfd || !in_srtp->selected_sfd)) {
ilog(LOG_WARNING, "RTP packet from %s discarded", endpoint_print_buf(fsin));
atomic64_inc(&stream->stats.errors);
atomic64_inc(&cm->statsps.errors);
atomic64_inc(&rtpe_statsps.errors);
goto unlock_out;
}
@ -1403,7 +1434,7 @@ loop_ok:
/* wait at least 3 seconds after last signal before committing to a particular
* endpoint address */
if (!call->last_signal || poller_now <= call->last_signal + 3)
if (!call->last_signal || rtpe_now.tv_sec <= call->last_signal + 3)
goto update_peerinfo;
ilog(LOG_INFO, "Confirmed peer address as %s", endpoint_print_buf(fsin));
@ -1478,7 +1509,7 @@ forward:
ret = -errno;
ilog(LOG_DEBUG,"Error when sending message. Error: %s",strerror(errno));
atomic64_inc(&stream->stats.errors);
atomic64_inc(&cm->statsps.errors);
atomic64_inc(&rtpe_statsps.errors);
goto out;
}
@ -1490,9 +1521,9 @@ drop:
ret = 0;
atomic64_inc(&stream->stats.packets);
atomic64_add(&stream->stats.bytes, s->len);
atomic64_set(&stream->last_packet, poller_now);
atomic64_inc(&cm->statsps.packets);
atomic64_add(&cm->statsps.bytes, s->len);
atomic64_set(&stream->last_packet, rtpe_now.tv_sec);
atomic64_inc(&rtpe_statsps.packets);
atomic64_add(&rtpe_statsps.bytes, s->len);
out:
if (ret == 0 && update)
@ -1565,7 +1596,7 @@ out:
ca = sfd->call ? : NULL;
if (ca && update) {
redis_update_onekey(ca, ca->callmaster->conf.redis_write);
redis_update_onekey(ca, rtpe_redis_write);
}
done:
log_info_clear();
@ -1587,7 +1618,6 @@ static void stream_fd_free(void *p) {
struct stream_fd *stream_fd_new(socket_t *fd, struct call *call, const struct local_intf *lif) {
struct stream_fd *sfd;
struct poller_item pi;
struct poller *po = call->callmaster->poller;
sfd = obj_alloc0("stream_fd", sizeof(*sfd), stream_fd_free);
sfd->unique_id = g_queue_get_length(&call->stream_fds);
@ -1605,7 +1635,7 @@ struct stream_fd *stream_fd_new(socket_t *fd, struct call *call, const struct lo
pi.readable = stream_fd_readable;
pi.closed = stream_fd_closed;
if (poller_add_item(po, &pi))
if (poller_add_item(rtpe_poller, &pi))
ilog(LOG_ERR, "Failed to add stream_fd to poller");
return sfd;

@ -21,7 +21,8 @@ struct logical_intf {
str name;
sockfamily_t *preferred_family;
GQueue list; /* struct local_intf */
GHashTable *addr_hash;
GHashTable *addr_hash; // addr + type -> struct local_intf XXX obsolete?
GHashTable *rr_specs;
};
struct port_pool {
BIT_ARRAY_DECLARE(ports_used, 0x10000);
@ -35,7 +36,9 @@ struct intf_address {
sockaddr_t addr;
};
struct intf_config {
str name;
str name; // full name (before the '/' separator in config)
str name_base; // if name is "foo:bar", this is "foo"
str name_rr_spec; // if name is "foo:bar", this is "bar"
struct intf_address local_address;
struct intf_address advertised_address;
unsigned int port_min, port_max;
@ -78,9 +81,7 @@ int is_local_endpoint(const struct intf_address *addr, unsigned int port);
//int get_port(socket_t *r, unsigned int port, const struct local_intf *lif, const struct call *c);
//void release_port(socket_t *r, const struct local_intf *);
INLINE void set_tos(socket_t *s, unsigned int tos) {
s->family->tos(s, tos);
}
int __get_consecutive_ports(GQueue *out, unsigned int num_ports, unsigned int wanted_start_port,
struct intf_spec *spec, const str *);
int get_consecutive_ports(GQueue *out, unsigned int num_ports, const struct logical_intf *log, const str *);

@ -55,7 +55,7 @@ struct poller *poller_new(void) {
p = malloc(sizeof(*p));
memset(p, 0, sizeof(*p));
gettimeofday(&g_now, NULL);
gettimeofday(&rtpe_now, NULL);
p->fd = epoll_create1(0);
if (p->fd == -1)
abort();
@ -311,7 +311,7 @@ int poller_poll(struct poller *p, int timeout) {
if (ret <= 0)
goto out;
gettimeofday(&g_now, NULL);
gettimeofday(&rtpe_now, NULL);
for (i = 0; i < ret; i++) {
ev = &evs[i];
@ -482,9 +482,9 @@ void poller_timer_loop(void *d) {
struct timeval tv;
int wt;
while (!g_shutdown) {
while (!rtpe_shutdown) {
gettimeofday(&tv, NULL);
if (tv.tv_sec != poller_now)
if (tv.tv_sec != rtpe_now.tv_sec)
goto now;
wt = 1000000 - tv.tv_usec;
@ -493,7 +493,7 @@ void poller_timer_loop(void *d) {
continue;
now:
gettimeofday(&g_now, NULL);
gettimeofday(&rtpe_now, NULL);
poller_timers_run(p);
}
}
@ -501,6 +501,6 @@ now:
void poller_loop(void *d) {
struct poller *p = d;
while (!g_shutdown)
while (!rtpe_shutdown)
poller_poll(p, 100);
}

@ -30,10 +30,6 @@ struct poller_item {
struct poller;
/* XXX replace all occurrences with g_now */
#define poller_now g_now.tv_sec
struct poller *poller_new(void);
int poller_add_item(struct poller *, struct poller_item *);
int poller_update_item(struct poller *, struct poller_item *);

@ -373,7 +373,7 @@ static int pcap_meta_finish_file(struct call *call) {
// Print start timestamp and end timestamp
// YYYY-MM-DDThh:mm:ss
time_t start = call->created.tv_sec;
time_t end = g_now.tv_sec;
time_t end = rtpe_now.tv_sec;
char timebuffer[20];
struct tm *timeinfo;
timeinfo = localtime(&start);
@ -503,7 +503,7 @@ static void stream_pcap_dump(pcap_dumper_t *pdumper, struct packet_stream *strea
// Set up PCAP packet header
struct pcap_pkthdr header;
ZERO(header);
header.ts = g_now;
header.ts = rtpe_now;
header.caplen = pkt_len;
header.len = pkt_len;
@ -730,7 +730,7 @@ static void setup_media_proc(struct call_media *media) {
if (!recording)
return;
GList *pltypes = g_hash_table_get_values(media->rtp_payload_types);
GList *pltypes = g_hash_table_get_values(media->codecs);
for (GList *l = pltypes; l; l = l->next) {
struct rtp_payload_type *pt = l->data;

@ -30,6 +30,15 @@
#include "rtplib.h"
#include "str.h"
#include "ssrc.h"
#include "main.h"
struct redis *rtpe_redis;
struct redis *rtpe_redis_write;
struct redis *rtpe_redis_notify;
struct event_base *rtpe_redis_notify_event_base;
struct redisAsyncContext *rtpe_redis_notify_async_context;
INLINE redisReply *redis_expect(int type, redisReply *r) {
@ -70,7 +79,7 @@ static int redisCommandNR(redisContext *r, const char *fmt, ...)
#define REDIS_FMT(x) (x)->len, (x)->str
static int redis_check_conn(struct redis *r);
static void json_restore_call(struct redis *r, struct callmaster *m, const str *id, enum call_type type);
static void json_restore_call(struct redis *r, const str *id, enum call_type type);
static void redis_pipe(struct redis *r, const char *fmt, ...) {
va_list ap;
@ -174,8 +183,8 @@ static int redis_connect(struct redis *r, int wait) {
redisFree(r->ctx);
r->ctx = NULL;
tv.tv_sec = 1;
tv.tv_usec = 0;
tv.tv_sec = (int) r->connect_timeout / 1000;
tv.tv_usec = (int) (r->connect_timeout % 1000) * 1000;
r->ctx = redisConnectWithTimeout(r->host, r->endpoint.port, tv);
if (!r->ctx)
@ -183,6 +192,15 @@ static int redis_connect(struct redis *r, int wait) {
if (r->ctx->err)
goto err2;
if (r->cmd_timeout) {
struct timeval tv_cmd;
tv_cmd.tv_sec = (int) r->cmd_timeout / 1000;
tv_cmd.tv_usec = (int) (r->cmd_timeout % 1000) * 1000;
if (redisSetTimeout(r->ctx, tv_cmd))
goto err2;
ilog(LOG_INFO, "Setting timeout for Redis commands to %d milliseconds",r->cmd_timeout);
}
if (r->auth) {
if (redisCommandNR(r->ctx, "AUTH %s", r->auth))
goto err2;
@ -259,25 +277,17 @@ err:
void on_redis_notification(redisAsyncContext *actx, void *reply, void *privdata) {
struct callmaster *cm = privdata;
struct redis *r = 0;
struct call *c = NULL;
str callid;
str keyspace_id;
// sanity checks
if (!cm) {
rlog(LOG_ERROR, "Struct callmaster is NULL on on_redis_notification");
return;
}
if (!cm->conf.redis_notify) {
if (!rtpe_redis_notify) {
rlog(LOG_ERROR, "A redis notification has been received but no redis_notify database found");
return;
}
r = cm->conf.redis_notify;
r = rtpe_redis_notify;
mutex_lock(&r->lock);
@ -322,7 +332,7 @@ void on_redis_notification(redisAsyncContext *actx, void *reply, void *privdata)
}
if (strncmp(rr->element[3]->str,"set",3)==0) {
c = call_get(&callid, cm);
c = call_get(&callid);
if (c) {
rwlock_unlock_w(&c->master_lock);
if (IS_FOREIGN_CALL(c))
@ -332,11 +342,11 @@ void on_redis_notification(redisAsyncContext *actx, void *reply, void *privdata)
goto err;
}
}
json_restore_call(r, cm, &callid, CT_FOREIGN_CALL);
json_restore_call(r, &callid, CT_FOREIGN_CALL);
}
if (strncmp(rr->element[3]->str,"del",3)==0) {
c = call_get(&callid, cm);
c = call_get(&callid);
if (!c) {
rlog(LOG_NOTICE, "Redis-Notifier: DEL did not find call with callid: %s\n", rr->element[2]->str);
goto err;
@ -375,36 +385,30 @@ void redis_async_context_disconnect(const redisAsyncContext *redis_notify_async_
}
}
int redis_async_context_alloc(struct callmaster *cm) {
int redis_async_context_alloc() {
struct redis *r = 0;
// sanity checks
if (!cm) {
rlog(LOG_ERROR, "Struct callmaster is NULL on context free");
return -1;
}
if (!cm->conf.redis_notify) {
if (!rtpe_redis_notify) {
rlog(LOG_INFO, "redis_notify database is NULL.");
return -1;
}
// get redis_notify database
r = cm->conf.redis_notify;
r = rtpe_redis_notify;
rlog(LOG_INFO, "Use Redis %s for notifications", endpoint_print_buf(&r->endpoint));
// alloc async context
cm->conf.redis_notify_async_context = redisAsyncConnect(r->host, r->endpoint.port);
if (!cm->conf.redis_notify_async_context) {
rtpe_redis_notify_async_context = redisAsyncConnect(r->host, r->endpoint.port);
if (!rtpe_redis_notify_async_context) {
rlog(LOG_ERROR, "redis_notify_async_context can't create new");
return -1;
}
if (cm->conf.redis_notify_async_context->err) {
rlog(LOG_ERROR, "redis_notify_async_context can't create new error: %s", cm->conf.redis_notify_async_context->errstr);
if (rtpe_redis_notify_async_context->err) {
rlog(LOG_ERROR, "redis_notify_async_context can't create new error: %s", rtpe_redis_notify_async_context->errstr);
return -1;
}
if (redisAsyncSetDisconnectCallback(cm->conf.redis_notify_async_context, redis_async_context_disconnect) != REDIS_OK) {
if (redisAsyncSetDisconnectCallback(rtpe_redis_notify_async_context, redis_async_context_disconnect) != REDIS_OK) {
rlog(LOG_ERROR, "redis_notify_async_context can't set disconnect callback");
return -1;
}
@ -412,14 +416,8 @@ int redis_async_context_alloc(struct callmaster *cm) {
return 0;
}
int redis_notify_event_base_action(struct callmaster *cm, enum event_base_action action) {
// sanity checks
if (!cm) {
rlog(LOG_ERROR, "Struct callmaster is NULL on event base action %d", action);
return -1;
}
if (!cm->conf.redis_notify_event_base && action!=EVENT_BASE_ALLOC) {
int redis_notify_event_base_action(enum event_base_action action) {
if (!rtpe_redis_notify_event_base && action!=EVENT_BASE_ALLOC) {
rlog(LOG_ERROR, "redis_notify_event_base is NULL on event base action %d", action);
return -1;
}
@ -427,8 +425,8 @@ int redis_notify_event_base_action(struct callmaster *cm, enum event_base_action
// exec event base action
switch (action) {
case EVENT_BASE_ALLOC:
cm->conf.redis_notify_event_base = event_base_new();
if (!cm->conf.redis_notify_event_base) {
rtpe_redis_notify_event_base = event_base_new();
if (!rtpe_redis_notify_event_base) {
rlog(LOG_ERROR, "Fail alloc redis_notify_event_base");
return -1;
} else {
@ -437,12 +435,12 @@ int redis_notify_event_base_action(struct callmaster *cm, enum event_base_action
break;
case EVENT_BASE_FREE:
event_base_free(cm->conf.redis_notify_event_base);
event_base_free(rtpe_redis_notify_event_base);
rlog(LOG_DEBUG, "Success free redis_notify_event_base");
break;
case EVENT_BASE_LOOPBREAK:
if (event_base_loopbreak(cm->conf.redis_notify_event_base)) {
if (event_base_loopbreak(rtpe_redis_notify_event_base)) {
rlog(LOG_ERROR, "Fail loopbreak redis_notify_event_base");
return -1;
} else {
@ -458,38 +456,32 @@ int redis_notify_event_base_action(struct callmaster *cm, enum event_base_action
return 0;
}
int redis_notify_subscribe_action(struct callmaster *cm, enum subscribe_action action, int keyspace) {
// sanity checks
if (!cm) {
rlog(LOG_ERROR, "Struct callmaster is NULL on subscribe action");
return -1;
}
if (!cm->conf.redis_notify_async_context) {
int redis_notify_subscribe_action(enum subscribe_action action, int keyspace) {
if (!rtpe_redis_notify_async_context) {
rlog(LOG_ERROR, "redis_notify_async_context is NULL on subscribe action");
return -1;
}
if (cm->conf.redis_notify_async_context->err) {
rlog(LOG_ERROR, "redis_notify_async_context error on subscribe action: %s", cm->conf.redis_notify_async_context->errstr);
if (rtpe_redis_notify_async_context->err) {
rlog(LOG_ERROR, "redis_notify_async_context error on subscribe action: %s", rtpe_redis_notify_async_context->errstr);
return -1;
}
switch (action) {
case SUBSCRIBE_KEYSPACE:
if (redisAsyncCommand(cm->conf.redis_notify_async_context, on_redis_notification, (void*)cm, "psubscribe __keyspace@%i__:*", keyspace) != REDIS_OK) {
if (redisAsyncCommand(rtpe_redis_notify_async_context, on_redis_notification, NULL, "psubscribe __keyspace@%i__:*", keyspace) != REDIS_OK) {
rlog(LOG_ERROR, "Fail redisAsyncCommand on JSON SUBSCRIBE_KEYSPACE");
return -1;
}
break;
case UNSUBSCRIBE_KEYSPACE:
if (redisAsyncCommand(cm->conf.redis_notify_async_context, on_redis_notification, (void*)cm, "punsubscribe __keyspace@%i__:*", keyspace) != REDIS_OK) {
if (redisAsyncCommand(rtpe_redis_notify_async_context, on_redis_notification, NULL, "punsubscribe __keyspace@%i__:*", keyspace) != REDIS_OK) {
rlog(LOG_ERROR, "Fail redisAsyncCommand on JSON UNSUBSCRIBE_KEYSPACE");
return -1;
}
break;
case UNSUBSCRIBE_ALL:
if (redisAsyncCommand(cm->conf.redis_notify_async_context, on_redis_notification, (void *) cm, "punsubscribe") != REDIS_OK) {
if (redisAsyncCommand(rtpe_redis_notify_async_context, on_redis_notification, NULL, "punsubscribe") != REDIS_OK) {
rlog(LOG_ERROR, "Fail redisAsyncCommand on JSON UNSUBSCRIBE_ALL");
return -1;
}
@ -502,39 +494,33 @@ int redis_notify_subscribe_action(struct callmaster *cm, enum subscribe_action a
return 0;
}
static int redis_notify(struct callmaster *cm) {
static int redis_notify() {
struct redis *r = 0;
GList *l;
// sanity checks
if (!cm) {
rlog(LOG_ERROR, "Struct callmaster is NULL on redis_notify()");
return -1;
}
if (!cm->conf.redis_notify) {
if (!rtpe_redis_notify) {
rlog(LOG_ERROR, "redis_notify database is NULL on redis_notify()");
return -1;
}
if (!cm->conf.redis_notify_async_context) {
if (!rtpe_redis_notify_async_context) {
rlog(LOG_ERROR, "redis_notify_async_context is NULL on redis_notify()");
return -1;
}
if (!cm->conf.redis_notify_event_base) {
if (!rtpe_redis_notify_event_base) {
rlog(LOG_ERROR, "redis_notify_event_base is NULL on redis_notify()");
return -1;
}
// get redis_notify database
r = cm->conf.redis_notify;
r = rtpe_redis_notify;
rlog(LOG_INFO, "Use Redis %s to subscribe to notifications", endpoint_print_buf(&r->endpoint));
// attach event base
if (redisLibeventAttach(cm->conf.redis_notify_async_context, cm->conf.redis_notify_event_base) == REDIS_ERR) {
if (cm->conf.redis_notify_async_context->err) {
rlog(LOG_ERROR, "redis_notify_async_context can't attach event base error: %s", cm->conf.redis_notify_async_context->errstr);
if (redisLibeventAttach(rtpe_redis_notify_async_context, rtpe_redis_notify_event_base) == REDIS_ERR) {
if (rtpe_redis_notify_async_context->err) {
rlog(LOG_ERROR, "redis_notify_async_context can't attach event base error: %s", rtpe_redis_notify_async_context->errstr);
} else {
rlog(LOG_ERROR, "redis_notify_async_context can't attach event base");
@ -543,14 +529,14 @@ static int redis_notify(struct callmaster *cm) {
}
// subscribe to the values in the configured keyspaces
rwlock_lock_r(&cm->conf.config_lock);
for (l = cm->conf.redis_subscribed_keyspaces->head; l; l = l->next) {
redis_notify_subscribe_action(cm, SUBSCRIBE_KEYSPACE, GPOINTER_TO_UINT(l->data));
rwlock_lock_r(&rtpe_config.config_lock);
for (l = rtpe_config.redis_subscribed_keyspaces.head; l; l = l->next) {
redis_notify_subscribe_action(SUBSCRIBE_KEYSPACE, GPOINTER_TO_UINT(l->data));
}
rwlock_unlock_r(&cm->conf.config_lock);
rwlock_unlock_r(&rtpe_config.config_lock);
// dispatch event base => thread blocks here
if (event_base_dispatch(cm->conf.redis_notify_event_base) < 0) {
if (event_base_dispatch(rtpe_redis_notify_event_base) < 0) {
rlog(LOG_ERROR, "Fail event_base_dispatch()");
return -1;
}
@ -560,17 +546,10 @@ static int redis_notify(struct callmaster *cm) {
void redis_notify_loop(void *d) {
int seconds = 1, redis_notify_return = 0;
time_t next_run = g_now.tv_sec;
struct callmaster *cm = (struct callmaster *)d;
time_t next_run = rtpe_now.tv_sec;
struct redis *r;
// sanity checks
if (!cm) {
ilog(LOG_ERROR, "NULL callmaster");
return ;
}
r = cm->conf.redis_notify;
r = rtpe_redis_notify;
if (!r) {
rlog(LOG_ERROR, "Don't use Redis notifications. See --redis-notifications parameter.");
return ;
@ -583,54 +562,55 @@ void redis_notify_loop(void *d) {
}
// alloc redis async context
if (redis_async_context_alloc(cm) < 0) {
if (redis_async_context_alloc() < 0) {
return ;
}
// alloc event base
if (redis_notify_event_base_action(cm, EVENT_BASE_ALLOC) < 0) {
if (redis_notify_event_base_action(EVENT_BASE_ALLOC) < 0) {
return ;
}
// initial redis_notify
if (redis_check_conn(r) == REDIS_STATE_CONNECTED) {
redis_notify_return = redis_notify(cm);
redis_notify_return = redis_notify();
}
// loop redis_notify => in case of lost connection
while (!g_shutdown) {
gettimeofday(&g_now, NULL);
if (g_now.tv_sec < next_run) {
while (!rtpe_shutdown) {
gettimeofday(&rtpe_now, NULL);
if (rtpe_now.tv_sec < next_run) {
usleep(100000);
continue;
}
next_run = g_now.tv_sec + seconds;
next_run = rtpe_now.tv_sec + seconds;
if (redis_check_conn(r) == REDIS_STATE_RECONNECTED || redis_notify_return < 0) {
// alloc new redis async context upon redis breakdown
if (redis_async_context_alloc(cm) < 0) {
if (redis_async_context_alloc() < 0) {
continue;
}
// prepare notifications
redis_notify_return = redis_notify(cm);
redis_notify_return = redis_notify();
}
}
// unsubscribe notifications
redis_notify_subscribe_action(cm, UNSUBSCRIBE_ALL, 0);
redis_notify_subscribe_action(UNSUBSCRIBE_ALL, 0);
// free async context
redisAsyncDisconnect(cm->conf.redis_notify_async_context);
redisAsyncDisconnect(rtpe_redis_notify_async_context);
// free event base
redis_notify_event_base_action(cm, EVENT_BASE_FREE);
redis_notify_event_base_action(EVENT_BASE_FREE);
}
struct redis *redis_new(const endpoint_t *ep, int db, const char *auth, enum redis_role role, int no_redis_required) {
struct redis *redis_new(const endpoint_t *ep, int db, const char *auth,
enum redis_role role, int no_redis_required, int redis_allowed_errors,
int redis_disable_time, int redis_cmd_timeout,int redis_connect_timeout) {
struct redis *r;
r = g_slice_alloc0(sizeof(*r));
r->endpoint = *ep;
@ -640,6 +620,12 @@ struct redis *redis_new(const endpoint_t *ep, int db, const char *auth, enum red
r->role = role;
r->state = REDIS_STATE_DISCONNECTED;
r->no_redis_required = no_redis_required;
r->allowed_errors = redis_allowed_errors;
r->disable_time = redis_disable_time;
r->restore_tick = 0;
r->consecutive_errors = 0;
r->cmd_timeout = redis_cmd_timeout;
r->connect_timeout = redis_connect_timeout;
mutex_init(&r->lock);
if (redis_connect(r, 10)) {
@ -671,9 +657,34 @@ static void redis_close(struct redis *r) {
g_slice_free1(sizeof(*r), r);
}
static void redis_count_err_and_disable(struct redis *r)
{
if (r->allowed_errors < 0) {
return;
}
r->consecutive_errors++;
if (r->consecutive_errors > r->allowed_errors) {
r->restore_tick = rtpe_now.tv_sec + r->disable_time;
ilog(LOG_WARNING, "Redis server %s disabled for %d seconds",
endpoint_print_buf(&r->endpoint),
r->disable_time);
}
}
/* must be called with r->lock held */
static int redis_check_conn(struct redis *r) {
if ((r->state == REDIS_STATE_DISCONNECTED) && (r->restore_tick > rtpe_now.tv_sec)) {
ilog(LOG_WARNING, "Redis server %s is disabled. Don't try RE-Establishing for %d seconds",
endpoint_print_buf(&r->endpoint),r->disable_time);
return REDIS_STATE_DISCONNECTED;
}
if (r->state == REDIS_STATE_DISCONNECTED)
ilog(LOG_INFO, "RE-Establishing connection for Redis server %s",endpoint_print_buf(&r->endpoint));
// try redis connection
if (redisCommandNR(r->ctx, "PING") == 0) {
// redis is connected
@ -690,9 +701,12 @@ static int redis_check_conn(struct redis *r) {
// try redis reconnect => will free current r->ctx
if (redis_connect(r, 1)) {
// redis is disconnected
redis_count_err_and_disable(r);
return REDIS_STATE_DISCONNECTED;
}
r->consecutive_errors = 0;
// redis is connected
if (r->state == REDIS_STATE_DISCONNECTED) {
rlog(LOG_INFO, "RE-Established connection to Redis %s",
@ -1163,7 +1177,7 @@ static int redis_tags(struct call *c, struct redis_list *tags) {
static int rbl_cb_plts(str *s, GQueue *q, struct redis_list *list, void *ptr) {
struct rtp_payload_type *pt;
str ptype, enc, clock, parms;
str ptype, enc, clock, enc_parms, fmt_parms;
struct call_media *med = ptr;
struct call *call = med->call;
@ -1173,7 +1187,12 @@ static int rbl_cb_plts(str *s, GQueue *q, struct redis_list *list, void *ptr) {
return -1;
if (str_token(&clock, s, '/'))
return -1;
parms = *s;
if (str_token(&enc_parms, s, '/')) {
enc_parms = *s;
fmt_parms = STR_EMPTY;
}
else
fmt_parms = *s;
// from call.c
// XXX remove all the duplicate code
@ -1181,8 +1200,9 @@ static int rbl_cb_plts(str *s, GQueue *q, struct redis_list *list, void *ptr) {
pt->payload_type = str_to_ui(&ptype, 0);
call_str_cpy(call, &pt->encoding, &enc);
pt->clock_rate = str_to_ui(&clock, 0);
call_str_cpy(call, &pt->encoding_parameters, &parms);
g_hash_table_replace(med->rtp_payload_types, &pt->payload_type, pt);
call_str_cpy(call, &pt->encoding_parameters, &enc_parms);
call_str_cpy(call, &pt->format_parameters, &fmt_parms);
g_hash_table_replace(med->codecs, &pt->payload_type, pt);
return 0;
}
static int json_medias(struct call *c, struct redis_list *medias, JsonReader *root_reader) {
@ -1197,7 +1217,7 @@ static int json_medias(struct call *c, struct redis_list *medias, JsonReader *ro
/* from call.c:__get_media() */
med = uid_slice_alloc0(med, &c->medias);
med->call = c;
med->rtp_payload_types = g_hash_table_new_full(g_int_hash, g_int_equal, NULL,
med->codecs = g_hash_table_new_full(g_int_hash, g_int_equal, NULL,
__payload_type_free);
if (redis_hash_get_unsigned(&med->index, rh, "index"))
@ -1342,7 +1362,7 @@ static int json_link_streams(struct call *c, struct redis_list *streams,
return -1;
if (ps->media)
__rtp_stats_update(ps->rtp_stats, ps->media->rtp_payload_types);
__rtp_stats_update(ps->rtp_stats, ps->media->codecs);
}
return 0;
@ -1435,7 +1455,7 @@ static int json_build_ssrc(struct call *c, JsonReader *root_reader) {
return 0;
}
static void json_restore_call(struct redis *r, struct callmaster *m, const str *callid, enum call_type type) {
static void json_restore_call(struct redis *r, const str *callid, enum call_type type) {
redisReply* rr_jsonStr;
struct redis_hash call;
struct redis_list tags, sfds, streams, medias, maps;
@ -1461,7 +1481,7 @@ static void json_restore_call(struct redis *r, struct callmaster *m, const str *
if (!root_reader)
goto err1;
c = call_get_or_create(callid, m, type);
c = call_get_or_create(callid, type);
err = "failed to create call struct";
if (!c)
goto err1;
@ -1581,9 +1601,9 @@ err1:
if (c)
call_destroy(c);
else {
mutex_lock(&m->conf.redis_write->lock);
redisCommandNR(m->conf.redis_write->ctx, "DEL " PB, STR(callid));
mutex_unlock(&m->conf.redis_write->lock);
mutex_lock(&rtpe_redis_write->lock);
redisCommandNR(rtpe_redis_write->ctx, "DEL " PB, STR(callid));
mutex_unlock(&rtpe_redis_write->lock);
}
}
if (c)
@ -1591,7 +1611,6 @@ err1:
}
struct thread_ctx {
struct callmaster *m;
GQueue r_q;
mutex_t r_m;
};
@ -1609,14 +1628,14 @@ static void restore_thread(void *call_p, void *ctx_p) {
r = g_queue_pop_head(&ctx->r_q);
mutex_unlock(&ctx->r_m);
json_restore_call(r, ctx->m, &callid, CT_OWN_CALL);
json_restore_call(r, &callid, CT_OWN_CALL);
mutex_lock(&ctx->r_m);
g_queue_push_tail(&ctx->r_q, r);
mutex_unlock(&ctx->r_m);
}
int redis_restore(struct callmaster *m, struct redis *r) {
int redis_restore(struct redis *r) {
redisReply *calls = NULL, *call;
int i, ret = -1;
GThreadPool *gtp;
@ -1625,7 +1644,7 @@ int redis_restore(struct callmaster *m, struct redis *r) {
if (!r)
return 0;
log_level |= LOG_FLAG_RESTORE;
rtpe_config.common.log_level |= LOG_FLAG_RESTORE;
rlog(LOG_DEBUG, "Restoring calls from Redis...");
@ -1646,12 +1665,14 @@ int redis_restore(struct callmaster *m, struct redis *r) {
goto err;
}
ctx.m = m;
mutex_init(&ctx.r_m);
g_queue_init(&ctx.r_q);
for (i = 0; i < m->conf.redis_num_threads; i++)
g_queue_push_tail(&ctx.r_q, redis_new(&r->endpoint, r->db, r->auth, r->role, r->no_redis_required));
gtp = g_thread_pool_new(restore_thread, &ctx, m->conf.redis_num_threads, TRUE, NULL);
for (i = 0; i < rtpe_config.redis_num_threads; i++)
g_queue_push_tail(&ctx.r_q,
redis_new(&r->endpoint, r->db, r->auth, r->role,
r->no_redis_required, r->allowed_errors,
r->disable_time, r->cmd_timeout, r->connect_timeout));
gtp = g_thread_pool_new(restore_thread, &ctx, rtpe_config.redis_num_threads, TRUE, NULL);
for (i = 0; i < calls->elements; i++) {
call = calls->element[i];
@ -1669,7 +1690,7 @@ int redis_restore(struct callmaster *m, struct redis *r) {
freeReplyObject(calls);
err:
log_level &= ~LOG_FLAG_RESTORE;
rtpe_config.common.log_level &= ~LOG_FLAG_RESTORE;
return ret;
}
@ -1965,15 +1986,16 @@ char* redis_encode_json(struct call *c) {
}
json_builder_end_array (builder);
k = g_hash_table_get_values(media->rtp_payload_types);
k = g_hash_table_get_values(media->codecs);
snprintf(tmp, sizeof(tmp), "payload_types-%u", media->unique_id);
json_builder_set_member_name(builder, tmp);
json_builder_begin_array (builder);
for (m = k; m; m = m->next) {
pt = m->data;
JSON_ADD_STRING("%u/" STR_FORMAT "/%u/" STR_FORMAT,
JSON_ADD_STRING("%u/" STR_FORMAT "/%u/" STR_FORMAT "/" STR_FORMAT,
pt->payload_type, STR_FMT(&pt->encoding),
pt->clock_rate, STR_FMT(&pt->encoding_parameters));
pt->clock_rate, STR_FMT(&pt->encoding_parameters),
STR_FMT(&pt->format_parameters));
}
json_builder_end_array (builder);
@ -2073,7 +2095,7 @@ void redis_update_onekey(struct call *c, struct redis *r) {
rwlock_lock_r(&c->master_lock);
redis_expires_s = c->callmaster->conf.redis_expires_secs;
redis_expires_s = rtpe_config.redis_expires_secs;
c->redis_hosted_db = r->db;
if (redisCommandNR(r->ctx, "SELECT %i", c->redis_hosted_db)) {

@ -43,7 +43,6 @@ enum subscribe_action {
UNSUBSCRIBE_ALL,
};
struct callmaster;
struct call;
@ -61,6 +60,12 @@ struct redis {
int state;
int no_redis_required;
int allowed_errors;
int consecutive_errors;
int disable_time;
time_t restore_tick;
int cmd_timeout;
int connect_timeout;
};
struct redis_hash {
@ -74,6 +79,15 @@ struct redis_list {
};
extern struct redis *rtpe_redis;
extern struct redis *rtpe_redis_write;
extern struct redis *rtpe_redis_notify;
extern struct event_base *rtpe_redis_notify_event_base;
extern struct redisAsyncContext *rtpe_redis_notify_async_context;
#if !GLIB_CHECK_VERSION(2,40,0)
INLINE gboolean g_hash_table_insert_check(GHashTable *h, gpointer k, gpointer v) {
gboolean ret = TRUE;
@ -92,14 +106,14 @@ INLINE gboolean g_hash_table_insert_check(GHashTable *h, gpointer k, gpointer v)
void redis_notify_loop(void *d);
struct redis *redis_new(const endpoint_t *, int, const char *, enum redis_role, int no_redis_required);
int redis_restore(struct callmaster *, struct redis *);
struct redis *redis_new(const endpoint_t *, int, const char *, enum redis_role, int, int, int, int, int);
int redis_restore(struct redis *);
void redis_update(struct call *, struct redis *);
void redis_update_onekey(struct call *c, struct redis *r);
void redis_delete(struct call *, struct redis *);
void redis_wipe(struct redis *);
int redis_notify_event_base_action(struct callmaster *cm, enum event_base_action);
int redis_notify_subscribe_action(struct callmaster *cm, enum subscribe_action action, int keyspace);
int redis_notify_event_base_action(enum event_base_action);
int redis_notify_subscribe_action(enum subscribe_action action, int keyspace);

@ -162,6 +162,13 @@ struct attribute_rtpmap {
struct rtp_payload_type rtp_pt;
};
struct attribute_fmtp {
str payload_type_str;
str format_parms_str;
unsigned int payload_type;
};
struct sdp_attribute {
str full_line, /* including a= and \r\n */
line_value, /* without a= and without \r\n */
@ -192,6 +199,7 @@ struct sdp_attribute {
ATTR_FINGERPRINT,
ATTR_SETUP,
ATTR_RTPMAP,
ATTR_FMTP,
ATTR_IGNORE,
ATTR_END_OF_CANDIDATES,
} attr;
@ -205,6 +213,7 @@ struct sdp_attribute {
struct attribute_fingerprint fingerprint;
struct attribute_setup setup;
struct attribute_rtpmap rtpmap;
struct attribute_fmtp fmtp;
} u;
};
@ -391,8 +400,7 @@ static int parse_attribute_ssrc(struct sdp_attribute *output) {
return -1;
s->attr = s->attr_str;
str_chr_str(&s->value, &s->attr, ':');
if (s->value.s) {
if (str_chr_str(&s->value, &s->attr, ':')) {
s->attr.len = s->value.s - s->attr.s;
str_shift(&s->value, 1);
}
@ -471,8 +479,7 @@ static int parse_attribute_crypto(struct sdp_attribute *output) {
if (c->lifetime_str.s[0] != '|')
goto error;
str_shift(&c->lifetime_str, 1);
str_chr_str(&c->mki_str, &c->lifetime_str, '|');
if (!c->mki_str.s) {
if (!str_chr_str(&c->mki_str, &c->lifetime_str, '|')) {
if (str_chr(&c->lifetime_str, ':')) {
c->mki_str = c->lifetime_str;
c->lifetime_str = STR_NULL;
@ -507,9 +514,8 @@ static int parse_attribute_crypto(struct sdp_attribute *output) {
}
if (c->mki_str.s) {
str_chr_str(&s, &c->mki_str, ':');
err = "invalid MKI specification";
if (!s.s)
if (!str_chr_str(&s, &c->mki_str, ':'))
goto error;
u32 = htonl(strtoul(c->mki_str.s, NULL, 10));
c->mki_len = strtoul(s.s + 1, NULL, 10);
@ -723,16 +729,14 @@ static int parse_attribute_rtpmap(struct sdp_attribute *output) {
if (ep == a->payload_type_str.s)
return -1;
str_chr_str(&a->clock_rate_str, &a->encoding_str, '/');
if (!a->clock_rate_str.s)
if (!str_chr_str(&a->clock_rate_str, &a->encoding_str, '/'))
return -1;
pt->encoding = a->encoding_str;
pt->encoding.len -= a->clock_rate_str.len;
str_shift(&a->clock_rate_str, 1);
str_chr_str(&pt->encoding_parameters, &a->clock_rate_str, '/');
if (pt->encoding_parameters.s) {
if (str_chr_str(&pt->encoding_parameters, &a->clock_rate_str, '/')) {
a->clock_rate_str.len -= pt->encoding_parameters.len;
str_shift(&pt->encoding_parameters, 1);
}
@ -747,19 +751,37 @@ static int parse_attribute_rtpmap(struct sdp_attribute *output) {
return 0;
}
static int parse_attribute_fmtp(struct sdp_attribute *output) {
PARSE_DECL;
char *ep;
struct attribute_fmtp *a;
output->attr = ATTR_FMTP;
PARSE_INIT;
EXTRACT_TOKEN(u.fmtp.payload_type_str);
EXTRACT_TOKEN(u.fmtp.format_parms_str);
a = &output->u.fmtp;
a->payload_type = strtoul(a->payload_type_str.s, &ep, 10);
if (ep == a->payload_type_str.s)
return -1;
return 0;
}
static int parse_attribute(struct sdp_attribute *a) {
int ret;
a->name = a->line_value;
str_chr_str(&a->value, &a->name, ':');
if (a->value.s) {
if (str_chr_str(&a->value, &a->name, ':')) {
a->name.len -= a->value.len;
a->value.s++;
a->value.len--;
a->key = a->name;
str_chr_str(&a->param, &a->value, ' ');
if (a->param.s) {
if (str_chr_str(&a->param, &a->value, ' ')) {
a->key.len += 1 +
(a->value.len - a->param.len);
@ -784,6 +806,8 @@ static int parse_attribute(struct sdp_attribute *a) {
ret = parse_attribute_rtcp(a);
else if (!str_cmp(&a->name, "ssrc"))
ret = parse_attribute_ssrc(a);
else if (!str_cmp(&a->name, "fmtp"))
ret = parse_attribute_fmtp(a);
break;
case 5:
if (!str_cmp(&a->name, "group"))
@ -1080,7 +1104,7 @@ static int fill_endpoint(struct endpoint *ep, const struct sdp_media *media, str
static int __rtp_payload_types(struct stream_params *sp, struct sdp_media *media)
{
GHashTable *ht;
GHashTable *ht_rtpmap, *ht_fmtp;
GQueue *q;
GList *ql;
struct sdp_attribute *attr;
@ -1090,15 +1114,21 @@ static int __rtp_payload_types(struct stream_params *sp, struct sdp_media *media
return 0;
/* first go through a=rtpmap and build a hash table of attrs */
ht = g_hash_table_new(g_int_hash, g_int_equal);
ht_rtpmap = g_hash_table_new(g_int_hash, g_int_equal);
q = attr_list_get_by_id(&media->attributes, ATTR_RTPMAP);
for (ql = q ? q->head : NULL; ql; ql = ql->next) {
struct rtp_payload_type *pt;
attr = ql->data;
pt = &attr->u.rtpmap.rtp_pt;
g_hash_table_insert(ht, &pt->payload_type, pt);
g_hash_table_insert(ht_rtpmap, &pt->payload_type, pt);
}
// do the same for a=fmtp
ht_fmtp = g_hash_table_new(g_int_hash, g_int_equal);
q = attr_list_get_by_id(&media->attributes, ATTR_FMTP);
for (ql = q ? q->head : NULL; ql; ql = ql->next) {
attr = ql->data;
g_hash_table_insert(ht_fmtp, &attr->u.fmtp.payload_type, &attr->u.fmtp.format_parms_str);
}
/* a=fmtp processing would go here */
/* then go through the format list and associate */
for (ql = media->format_list.head; ql; ql = ql->next) {
@ -1115,13 +1145,18 @@ static int __rtp_payload_types(struct stream_params *sp, struct sdp_media *media
/* first look in rtpmap for a match, then check RFC types,
* else fall back to an "unknown" type */
ptl = rtp_payload_type(i, ht);
ptl = rtp_payload_type(i, ht_rtpmap);
pt = g_slice_alloc0(sizeof(*pt));
if (ptl)
*pt = *ptl;
else
pt->payload_type = i;
s = g_hash_table_lookup(ht_fmtp, &i);
if (s)
pt->format_parameters = *s;
g_queue_push_tail(&sp->rtp_payload_types, pt);
}
@ -1131,7 +1166,8 @@ error:
ret = -1;
goto out;
out:
g_hash_table_destroy(ht);
g_hash_table_destroy(ht_rtpmap);
g_hash_table_destroy(ht_fmtp);
return ret;
}
@ -1312,6 +1348,7 @@ error:
return -1;
}
// XXX iovec can probably be eliminated now and this moved to a regular string builder
struct sdp_chopper *sdp_chopper_new(str *input) {
struct sdp_chopper *c = g_slice_alloc0(sizeof(*c));
c->input = input;
@ -1415,6 +1452,21 @@ static int replace_transport_protocol(struct sdp_chopper *chop,
return 0;
}
static int replace_codec_list(struct sdp_chopper *chop,
struct sdp_media *media, struct call_media *cm)
{
if (cm->codecs_prefs.length == 0)
return 0; // legacy protocol or usage error
for (GList *l = cm->codecs_prefs.head; l; l = l->next) {
struct rtp_payload_type *pt = l->data;
chopper_append_printf(chop, " %u", pt->payload_type);
}
if (skip_over(chop, &media->formats))
return -1;
return 0;
}
static int replace_media_port(struct sdp_chopper *chop, struct sdp_media *media, struct packet_stream *ps) {
str *port = &media->port;
unsigned int p;
@ -1650,6 +1702,22 @@ static int process_media_attributes(struct sdp_chopper *chop, struct sdp_media *
goto strip; // hack/workaround: always remove a=mid
break;
case ATTR_RTPMAP:
if (media->codecs_prefs.length == 0)
break; // legacy protocol or usage error
if (!g_hash_table_lookup(media->codecs,
&attr->u.rtpmap.rtp_pt.payload_type))
goto strip;
break;
case ATTR_FMTP:
if (media->codecs_prefs.length == 0)
break; // legacy protocol or usage error
if (!g_hash_table_lookup(media->codecs,
&attr->u.fmtp.payload_type))
goto strip;
break;
default:
break;
}
@ -1958,6 +2026,8 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu
goto error;
if (replace_transport_protocol(chop, sdp_media, call_media))
goto error;
if (replace_codec_list(chop, sdp_media, call_media))
goto error;
if (sdp_media->connection.parsed) {
if (replace_network_address(chop, &sdp_media->connection.address, ps,
@ -1998,18 +2068,28 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu
|| (flags->opmode == OP_OFFER
&& flags->rtcp_mux_require)))
{
chopper_append_c(chop, "a=rtcp:");
chopper_append_printf(chop, "%u", ps->selected_sfd->socket.local.port);
chopper_append_c(chop, "\r\na=rtcp-mux\r\n");
if (!flags->no_rtcp_attr) {
chopper_append_c(chop, "a=rtcp:");
chopper_append_printf(chop, "%u", ps->selected_sfd->socket.local.port);
chopper_append_c(chop, "\r\na=rtcp-mux\r\n");
}
else
chopper_append_c(chop, "a=rtcp-mux\r\n");
ps_rtcp = NULL;
}
else if (ps_rtcp && !flags->ice_force_relay) {
chopper_append_c(chop, "a=rtcp:");
chopper_append_printf(chop, "%u", ps_rtcp->selected_sfd->socket.local.port);
if (!MEDIA_ISSET(call_media, RTCP_MUX))
chopper_append_c(chop, "\r\n");
else
chopper_append_c(chop, "\r\na=rtcp-mux\r\n");
if (!flags->no_rtcp_attr) {
chopper_append_c(chop, "a=rtcp:");
chopper_append_printf(chop, "%u", ps_rtcp->selected_sfd->socket.local.port);
if (!MEDIA_ISSET(call_media, RTCP_MUX))
chopper_append_c(chop, "\r\n");
else
chopper_append_c(chop, "\r\na=rtcp-mux\r\n");
}
else {
if (MEDIA_ISSET(call_media, RTCP_MUX))
chopper_append_c(chop, "a=rtcp-mux\r\n");
}
}
}
else

@ -25,6 +25,8 @@ static int __ip4_is_specified(const sockaddr_t *a);
static int __ip6_is_specified(const sockaddr_t *a);
static int __ip_bind(socket_t *s, unsigned int, const sockaddr_t *);
static int __ip_connect(socket_t *s, const endpoint_t *);
static int __ip_listen(socket_t *s, int backlog);
static int __ip_accept(socket_t *s, socket_t *new_sock);
static int __ip_timestamping(socket_t *s);
static int __ip4_sockaddr2endpoint(endpoint_t *, const void *);
static int __ip6_sockaddr2endpoint(endpoint_t *, const void *);
@ -76,6 +78,8 @@ static struct socket_family __socket_families[__SF_LAST] = {
.addrport2sockaddr = __ip4_addrport2sockaddr,
.bind = __ip_bind,
.connect = __ip_connect,
.listen = __ip_listen,
.accept = __ip_accept,
.timestamping = __ip_timestamping,
.recvfrom = __ip_recvfrom,
.recvfrom_ts = __ip_recvfrom_ts,
@ -105,6 +109,8 @@ static struct socket_family __socket_families[__SF_LAST] = {
.addrport2sockaddr = __ip6_addrport2sockaddr,
.bind = __ip_bind,
.connect = __ip_connect,
.listen = __ip_listen,
.accept = __ip_accept,
.timestamping = __ip_timestamping,
.recvfrom = __ip_recvfrom,
.recvfrom_ts = __ip_recvfrom_ts,
@ -261,6 +267,30 @@ static int __ip_connect(socket_t *s, const endpoint_t *ep) {
}
return 0;
}
static int __ip_listen(socket_t *s, int backlog) {
return listen(s->fd, backlog);
}
static int __ip_accept(socket_t *s, socket_t *newsock) {
int nfd;
struct sockaddr_storage sin;
socklen_t sinlen;
ZERO(*newsock);
sinlen = sizeof(sin);
nfd = accept(s->fd, (struct sockaddr *) &sin, &sinlen);
if (nfd == -1) {
__C_DBG("accept fail, fd=%d, port=%d", s->fd, s->local.port);
return -1;
}
newsock->fd = nfd;
newsock->family = s->family;
newsock->local = s->local;
s->family->sockaddr2endpoint(&newsock->remote, &sin);
return 0;
}
static ssize_t __ip_recvfrom_ts(socket_t *s, void *buf, size_t len, endpoint_t *ep, struct timeval *tv) {
ssize_t ret;
struct sockaddr_storage sin;
@ -619,6 +649,8 @@ int open_socket(socket_t *r, int type, unsigned int port, const sockaddr_t *sa)
nonblock(r->fd);
reuseaddr(r->fd);
if (r->family->af == AF_INET6)
ipv6only(r->fd, 1);
if (port > 0xffff) {
__C_DBG("open socket fail, port=%d > 0xfffffd", port);

@ -64,6 +64,8 @@ struct socket_family {
int (*addrport2sockaddr)(void *, const sockaddr_t *, unsigned int);
int (*bind)(socket_t *, unsigned int, const sockaddr_t *);
int (*connect)(socket_t *, const endpoint_t *);
int (*listen)(socket_t *, int);
int (*accept)(socket_t *, socket_t *);
int (*timestamping)(socket_t *);
ssize_t (*recvfrom)(socket_t *, void *, size_t, endpoint_t *);
ssize_t (*recvfrom_ts)(socket_t *, void *, size_t, endpoint_t *, struct timeval *);
@ -261,7 +263,9 @@ INLINE int ipv46_any_convert(endpoint_t *ep) {
#define endpoint_packet_header(o, src, dst, len) (dst)->address.family->packet_header(o, src, dst, len)
INLINE void set_tos(socket_t *s, unsigned int tos) {
s->family->tos(s, tos);
}
socktype_t *get_socket_type(const str *s);
socktype_t *get_socket_type_c(const char *s);

@ -21,6 +21,7 @@ static struct ssrc_entry *create_ssrc_entry(u_int32_t ssrc) {
}
static void add_ssrc_entry(struct ssrc_entry *ent, struct ssrc_hash *ht) {
g_hash_table_replace(ht->ht, &ent->ssrc, ent);
g_queue_push_tail(&ht->q, ent);
}
static void free_sender_report(struct ssrc_sender_report_item *i) {
g_slice_free1(sizeof(*i), i);
@ -78,11 +79,11 @@ restart:
rwlock_lock_w(&ht->lock);
if (G_UNLIKELY(g_hash_table_size(ht->ht) > 20)) { // arbitrary limit
rwlock_unlock_w(&ht->lock);
free_ssrc_entry(ent);
ilog(LOG_INFO, "SSRC hash table exceeded size limit (trying to add %u)", ssrc);
return NULL;
while (G_UNLIKELY(ht->q.length > 20)) { // arbitrary limit
struct ssrc_entry *old_ent = g_queue_pop_head(&ht->q);
ilog(LOG_DEBUG, "SSRC hash table exceeded size limit (trying to add %u) - deleting SSRC %u",
ssrc, old_ent->ssrc);
g_hash_table_remove(ht->ht, &old_ent->ssrc);
}
if (g_hash_table_lookup(ht->ht, &ssrc)) {
@ -101,6 +102,7 @@ void free_ssrc_hash(struct ssrc_hash **ht) {
if (!*ht)
return;
g_hash_table_destroy((*ht)->ht);
g_queue_clear(&(*ht)->q);
g_slice_free1(sizeof(**ht), *ht);
*ht = NULL;
}
@ -249,7 +251,7 @@ void ssrc_receiver_report(struct call_media *m, const struct ssrc_receiver_repor
}
}
const struct rtp_payload_type *rpt = rtp_payload_type(pt, m->rtp_payload_types);
const struct rtp_payload_type *rpt = rtp_payload_type(pt, m->codecs);
if (!rpt) {
ilog(LOG_INFO, "Invalid RTP payload type %i, discarding RTCP RR", pt);
goto out_nl;

@ -21,6 +21,7 @@ enum ssrc_dir;
struct ssrc_hash {
GHashTable *ht;
GQueue q;
rwlock_t lock;
};
struct ssrc_ctx {

@ -1,5 +1,14 @@
#include "call.h"
#include "statistics.h"
#include "graphite.h"
#include "main.h"
struct totalstats rtpe_totalstats;
struct totalstats rtpe_totalstats_interval;
mutex_t rtpe_totalstats_lastinterval_lock;
struct totalstats rtpe_totalstats_lastinterval;
static void timeval_totalstats_average_add(struct totalstats *s, const struct timeval *add) {
struct timeval dp, oa;
@ -59,53 +68,48 @@ static void timeval_totalstats_interval_call_duration_add(struct totalstats *s,
}
void statistics_update_totals(struct callmaster* m, struct packet_stream *ps) {
atomic64_add(&m->totalstats.total_relayed_packets,
void statistics_update_totals(struct packet_stream *ps) {
atomic64_add(&rtpe_totalstats.total_relayed_packets,
atomic64_get(&ps->stats.packets));
atomic64_add(&m->totalstats_interval.total_relayed_packets,
atomic64_add(&rtpe_totalstats_interval.total_relayed_packets,
atomic64_get(&ps->stats.packets));
atomic64_add(&m->totalstats.total_relayed_errors,
atomic64_add(&rtpe_totalstats.total_relayed_errors,
atomic64_get(&ps->stats.errors));
atomic64_add(&m->totalstats_interval.total_relayed_errors,
atomic64_add(&rtpe_totalstats_interval.total_relayed_errors,
atomic64_get(&ps->stats.errors));
}
void statistics_update_foreignown_dec(struct call* c) {
struct callmaster *m;
m = c->callmaster;
if (IS_FOREIGN_CALL(c)) {
atomic64_dec(&m->stats.foreign_sessions);
atomic64_dec(&rtpe_stats.foreign_sessions);
}
if(IS_OWN_CALL(c)) {
mutex_lock(&m->totalstats_interval.managed_sess_lock);
m->totalstats_interval.managed_sess_min = MIN(m->totalstats_interval.managed_sess_min,
g_hash_table_size(m->callhash) - atomic64_get(&m->stats.foreign_sessions));
mutex_unlock(&m->totalstats_interval.managed_sess_lock);
mutex_lock(&rtpe_totalstats_interval.managed_sess_lock);
rtpe_totalstats_interval.managed_sess_min = MIN(rtpe_totalstats_interval.managed_sess_min,
g_hash_table_size(rtpe_callhash) - atomic64_get(&rtpe_stats.foreign_sessions));
mutex_unlock(&rtpe_totalstats_interval.managed_sess_lock);
}
}
void statistics_update_foreignown_inc(struct callmaster *m, struct call* c) {
void statistics_update_foreignown_inc(struct call* c) {
if (IS_OWN_CALL(c)) {
mutex_lock(&m->totalstats_interval.managed_sess_lock);
m->totalstats_interval.managed_sess_max = MAX(
m->totalstats_interval.managed_sess_max,
g_hash_table_size(m->callhash)
- atomic64_get(&m->stats.foreign_sessions));
mutex_unlock(&m->totalstats_interval.managed_sess_lock);
mutex_lock(&rtpe_totalstats_interval.managed_sess_lock);
rtpe_totalstats_interval.managed_sess_max = MAX(
rtpe_totalstats_interval.managed_sess_max,
g_hash_table_size(rtpe_callhash)
- atomic64_get(&rtpe_stats.foreign_sessions));
mutex_unlock(&rtpe_totalstats_interval.managed_sess_lock);
}
else if (IS_FOREIGN_CALL(c)) { /* foreign call*/
atomic64_inc(&m->stats.foreign_sessions);
atomic64_inc(&m->totalstats.total_foreign_sessions);
atomic64_inc(&rtpe_stats.foreign_sessions);
atomic64_inc(&rtpe_totalstats.total_foreign_sessions);
}
}
void statistics_update_oneway(struct call* c) {
struct callmaster *m;
struct packet_stream *ps = NULL, *ps2 = NULL;
struct call_monologue *ml;
struct call_media *md;
@ -114,8 +118,6 @@ void statistics_update_oneway(struct call* c) {
GList *l;
struct timeval tim_result_duration;
m = c->callmaster;
// --- for statistics getting one way stream or no relay at all
int total_nopacket_relayed_sess = 0;
@ -162,8 +164,8 @@ void statistics_update_oneway(struct call* c) {
if (ps && ps2 && atomic64_get(&ps2->stats.packets)==0) {
if (atomic64_get(&ps->stats.packets)!=0 && IS_OWN_CALL(c)){
if (atomic64_get(&ps->stats.packets)!=0) {
atomic64_inc(&m->totalstats.total_oneway_stream_sess);
atomic64_inc(&m->totalstats_interval.total_oneway_stream_sess);
atomic64_inc(&rtpe_totalstats.total_oneway_stream_sess);
atomic64_inc(&rtpe_totalstats_interval.total_oneway_stream_sess);
}
}
else {
@ -173,42 +175,59 @@ void statistics_update_oneway(struct call* c) {
}
if (IS_OWN_CALL(c)) {
atomic64_add(&m->totalstats.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2);
atomic64_add(&m->totalstats_interval.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2);
atomic64_add(&rtpe_totalstats.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2);
atomic64_add(&rtpe_totalstats_interval.total_nopacket_relayed_sess, total_nopacket_relayed_sess / 2);
}
if (c->monologues.head) {
ml = c->monologues.head->data;
timeval_subtract(&tim_result_duration, &g_now, &ml->started);
timeval_subtract(&tim_result_duration, &rtpe_now, &ml->started);
if (IS_OWN_CALL(c)) {
if (ml->term_reason==TIMEOUT) {
atomic64_inc(&m->totalstats.total_timeout_sess);
atomic64_inc(&m->totalstats_interval.total_timeout_sess);
atomic64_inc(&rtpe_totalstats.total_timeout_sess);
atomic64_inc(&rtpe_totalstats_interval.total_timeout_sess);
} else if (ml->term_reason==SILENT_TIMEOUT) {
atomic64_inc(&m->totalstats.total_silent_timeout_sess);
atomic64_inc(&m->totalstats_interval.total_silent_timeout_sess);
atomic64_inc(&rtpe_totalstats.total_silent_timeout_sess);
atomic64_inc(&rtpe_totalstats_interval.total_silent_timeout_sess);
} else if (ml->term_reason==REGULAR) {
atomic64_inc(&m->totalstats.total_regular_term_sess);
atomic64_inc(&m->totalstats_interval.total_regular_term_sess);
atomic64_inc(&rtpe_totalstats.total_regular_term_sess);
atomic64_inc(&rtpe_totalstats_interval.total_regular_term_sess);
} else if (ml->term_reason==FORCED) {
atomic64_inc(&m->totalstats.total_forced_term_sess);
atomic64_inc(&m->totalstats_interval.total_forced_term_sess);
atomic64_inc(&rtpe_totalstats.total_forced_term_sess);
atomic64_inc(&rtpe_totalstats_interval.total_forced_term_sess);
}
timeval_totalstats_average_add(&m->totalstats, &tim_result_duration);
timeval_totalstats_average_add(&m->totalstats_interval, &tim_result_duration);
timeval_totalstats_average_add(&rtpe_totalstats, &tim_result_duration);
timeval_totalstats_average_add(&rtpe_totalstats_interval, &tim_result_duration);
timeval_totalstats_interval_call_duration_add(
&m->totalstats_interval, &ml->started, &ml->terminated,
&m->latest_graphite_interval_start,
m->conf.graphite_interval);
&rtpe_totalstats_interval, &ml->started, &ml->terminated,
&rtpe_latest_graphite_interval_start,
rtpe_config.graphite_interval);
}
if (ml->term_reason==FINAL_TIMEOUT) {
atomic64_inc(&m->totalstats.total_final_timeout_sess);
atomic64_inc(&m->totalstats_interval.total_final_timeout_sess);
atomic64_inc(&rtpe_totalstats.total_final_timeout_sess);
atomic64_inc(&rtpe_totalstats_interval.total_final_timeout_sess);
}
}
}
void statistics_init() {
mutex_init(&rtpe_totalstats.total_average_lock);
mutex_init(&rtpe_totalstats_interval.total_average_lock);
mutex_init(&rtpe_totalstats_interval.managed_sess_lock);
mutex_init(&rtpe_totalstats_interval.total_calls_duration_lock);
rtpe_totalstats.started = rtpe_now.tv_sec;
//rtpe_totalstats_interval.managed_sess_min = 0; // already zeroed
//rtpe_totalstats_interval.managed_sess_max = 0;
mutex_init(&rtpe_totalstats_lastinterval_lock);
mutex_init(&rtpe_totalstats_interval.offers_ps.lock);
mutex_init(&rtpe_totalstats_interval.answers_ps.lock);
mutex_init(&rtpe_totalstats_interval.deletes_ps.lock);
}

@ -81,12 +81,16 @@ struct call_stats {
struct stats totals[4]; /* rtp in, rtcp in, rtp out, rtcp out */
};
struct callmaster;
extern struct totalstats rtpe_totalstats;
extern struct totalstats rtpe_totalstats_interval;
extern mutex_t rtpe_totalstats_lastinterval_lock;
extern struct totalstats rtpe_totalstats_lastinterval;
void statistics_update_oneway(struct call *);
void statistics_update_foreignown_dec(struct call *);
void statistics_update_foreignown_inc(struct callmaster *m, struct call* c);
void statistics_update_totals(struct callmaster *, struct packet_stream *) ;
void statistics_update_foreignown_inc(struct call* c);
void statistics_update_totals(struct packet_stream *) ;
void statistics_init(void);
#endif /* STATISTICS_H_ */

@ -13,16 +13,19 @@
struct streambuf *streambuf_new(struct poller *p, int fd) {
struct streambuf *b;
b = malloc(sizeof(*b));
ZERO(*b);
mutex_init(&b->lock);
b->buf = g_string_new("");
b->fd = fd;
b->poller = p;
b->active = poller_now;
b->active = rtpe_now.tv_sec;
return b;
}
@ -38,6 +41,8 @@ int streambuf_writeable(struct streambuf *b) {
int ret;
unsigned int out;
mutex_lock(&b->lock);
for (;;) {
if (!b->buf->len)
break;
@ -48,14 +53,16 @@ int streambuf_writeable(struct streambuf *b) {
if (ret < 0) {
if (errno == EINTR)
continue;
if (errno != EAGAIN && errno != EWOULDBLOCK)
if (errno != EAGAIN && errno != EWOULDBLOCK) {
mutex_unlock(&b->lock);
return -1;
}
ret = 0;
}
if (ret > 0) {
g_string_erase(b->buf, 0, ret);
b->active = poller_now;
b->active = rtpe_now.tv_sec;
}
if (ret != out) {
@ -64,6 +71,7 @@ int streambuf_writeable(struct streambuf *b) {
}
}
mutex_unlock(&b->lock);
return 0;
}
@ -71,32 +79,43 @@ int streambuf_readable(struct streambuf *b) {
int ret;
char buf[1024];
mutex_lock(&b->lock);
for (;;) {
ret = read(b->fd, buf, 1024);
if (ret == 0)
return -1;
if (ret == 0) {
// don't discard already read data in the buffer
b->eof = 1;
ret = b->buf->len ? -2 : -1;
mutex_unlock(&b->lock);
return ret;
}
if (ret < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN || errno == EWOULDBLOCK)
break;
mutex_unlock(&b->lock);
return -1;
}
g_string_append_len(b->buf, buf, ret);
b->active = poller_now;
b->active = rtpe_now.tv_sec;
}
mutex_unlock(&b->lock);
return 0;
}
char *streambuf_getline(struct streambuf *b) {
char *p;
int len;
int len, to_del;
char *s = NULL;
mutex_lock(&b->lock);
for (;;) {
if (s) {
free(s);
@ -104,19 +123,29 @@ char *streambuf_getline(struct streambuf *b) {
}
p = memchr(b->buf->str, '\n', b->buf->len);
if (!p)
return NULL;
len = p - b->buf->str;
if (len == 0) {
g_string_erase(b->buf, 0, 1);
continue;
if (!p) {
if (b->eof) {
// use entire string
len = b->buf->len;
to_del = len;
}
else
break;
}
else {
len = p - b->buf->str;
to_del = len + 1;
if (len == 0) {
// blank line, skip it
g_string_erase(b->buf, 0, 1);
continue;
}
}
s = malloc(len + 1);
memcpy(s, b->buf->str, len);
s[len] = '\0';
g_string_erase(b->buf, 0, len + 1);
g_string_erase(b->buf, 0, to_del);
if (s[--len] == '\r') {
if (len == 0)
@ -127,6 +156,7 @@ char *streambuf_getline(struct streambuf *b) {
break;
}
mutex_unlock(&b->lock);
return s;
}
@ -157,6 +187,8 @@ void streambuf_write(struct streambuf *b, const char *s, unsigned int len) {
unsigned int out;
int ret;
mutex_lock(&b->lock);
while (len && !poller_isblocked(b->poller, b->fd)) {
out = (len > 1024) ? 1024 : len;
ret = write(b->fd, s, out);
@ -176,11 +208,13 @@ void streambuf_write(struct streambuf *b, const char *s, unsigned int len) {
s += ret;
len -= ret;
b->active = poller_now;
b->active = rtpe_now.tv_sec;
}
if (b->buf->len > 5242880)
poller_error(b->poller, b->fd);
else if (len)
g_string_append_len(b->buf, s, len);
mutex_unlock(&b->lock);
}

@ -10,6 +10,7 @@
#include "compat.h"
#include "str.h"
#include "aux.h"
@ -18,10 +19,12 @@ struct poller;
struct streambuf {
mutex_t lock;
GString *buf;
int fd;
struct poller *poller;
time_t active;
int eof;
};

@ -222,9 +222,12 @@ static int stun_attributes(struct stun_attrs *out, str *s, u_int16_t *unknowns,
break;
default:
ilog(LOG_NOTICE, "Unknown STUN attribute: 0x%04x", type);
if ((type & 0x8000))
if ((type & 0x8000)) {
// comprehension optional
ilog(LOG_DEBUG, "Unknown STUN attribute: 0x%04x", type);
break;
}
ilog(LOG_NOTICE, "Unknown STUN attribute: 0x%04x", type);
unknowns[uc] = tlv->type;
unknowns[++uc] = 0xffff;
if (uc >= UNKNOWNS_COUNT - 1)
@ -437,8 +440,7 @@ static int check_auth(str *msg, struct stun_attrs *attrs, struct call_media *med
if (attrs->username.s) {
/* request */
ufrag[dst] = attrs->username;
str_chr_str(&ufrag[src], &ufrag[dst], ':');
if (!ufrag[src].s)
if (!str_chr_str(&ufrag[src], &ufrag[dst], ':'))
return -1;
ufrag[dst].len -= ufrag[src].len;
str_shift(&ufrag[src], 1);

@ -0,0 +1,229 @@
#include "tcp_listener.h"
#include <errno.h>
#include "poller.h"
#include "obj.h"
#include "socket.h"
#include "aux.h"
#include "log.h"
#include "streambuf.h"
struct tcp_listener_callback {
struct obj obj;
tcp_listener_callback_t func;
socket_t *ul;
struct obj *p;
};
struct streambuf_callback {
struct obj obj;
streambuf_callback_t newconn_func;
streambuf_callback_t newdata_func;
streambuf_callback_t closed_func;
streambuf_callback_t timer_func;
struct streambuf_listener *listener;
struct obj *parent;
};
static void tcp_listener_incoming(int fd, void *p, uintptr_t x) {
struct tcp_listener_callback *cb = p;
int ret;
char addr[64];
socket_t *listener;
socket_t newsock;
listener = cb->ul;
for (;;) {
ret = listener->family->accept(listener, &newsock);
if (ret == -1) {
if (errno == EINTR)
continue;
if (errno != EWOULDBLOCK && errno != EAGAIN)
ilog(LOG_WARNING, "Error accepting TCP connection: %s", strerror(errno));
return;
}
nonblock(newsock.fd);
endpoint_print(&newsock.remote, addr, sizeof(addr));
cb->func(cb->p, &newsock, addr, listener);
}
}
static void tcp_listener_closed(int fd, void *p, uintptr_t u) {
abort();
}
int tcp_listener_init(socket_t *sock, struct poller *p, const endpoint_t *ep,
tcp_listener_callback_t func, struct obj *obj)
{
struct poller_item i;
struct tcp_listener_callback *cb;
cb = obj_alloc("tcp_listener_callback", sizeof(*cb), NULL);
cb->func = func;
cb->p = obj_get_o(obj);
cb->ul = sock;
if (open_socket(sock, SOCK_STREAM, ep->port, &ep->address))
goto fail;
if (sock->family->listen(sock, 5))
goto fail;
ZERO(i);
i.fd = sock->fd;
i.closed = tcp_listener_closed;
i.readable = tcp_listener_incoming;
i.obj = &cb->obj;
if (poller_add_item(p, &i))
goto fail;
return 0;
fail:
close_socket(sock);
obj_put_o(obj);
obj_put(cb);
return -1;
}
static void streambuf_stream_free(void *p) {
struct streambuf_stream *s = p;
close_socket(&s->sock);
streambuf_destroy(s->inbuf);
streambuf_destroy(s->outbuf);
obj_put(s->cb);
obj_put_o(s->parent);
free(s->addr);
}
static void streambuf_stream_closed(int fd, void *p, uintptr_t u) {
struct streambuf_stream *s = p;
if (s->sock.fd == -1)
return;
if (s->cb->closed_func)
s->cb->closed_func(s);
struct streambuf_listener *l = s->listener;
mutex_lock(&l->lock);
int ret = g_hash_table_remove(l->streams, s);
mutex_unlock(&l->lock);
poller_del_item(s->listener->poller, s->sock.fd);
if (ret)
obj_put(s);
}
static void streambuf_stream_timer(int fd, void *p, uintptr_t u) {
struct streambuf_stream *s = p;
if (s->cb->timer_func)
s->cb->timer_func(s);
}
static void streambuf_stream_readable(int fd, void *p, uintptr_t u) {
struct streambuf_stream *s = p;
int ret = streambuf_readable(s->inbuf);
if (ret == -1)
goto close;
s->cb->newdata_func(s);
if (ret == -2)
goto close;
return;
close:
streambuf_stream_closed(fd, s, 0);
}
static void streambuf_stream_writeable(int fd, void *p, uintptr_t u) {
struct streambuf_stream *s = p;
if (streambuf_writeable(s->outbuf))
streambuf_stream_closed(fd, s, 0);
}
static void streambuf_listener_newconn(struct obj *p, socket_t *newsock, char *addr, socket_t *listener_sock) {
struct streambuf_callback *cb = (void *) p;
struct streambuf_stream *s;
struct streambuf_listener *listener;
struct poller_item i;
listener = cb->listener;
s = obj_alloc0("streambuf_stream", sizeof(*s), streambuf_stream_free);
s->sock = *newsock;
s->inbuf = streambuf_new(listener->poller, newsock->fd);
s->outbuf = streambuf_new(listener->poller, newsock->fd);
s->listener = listener;
s->cb = obj_get(cb);
s->parent = obj_get_o(cb->parent);
s->addr = strdup(addr);
ZERO(i);
i.fd = newsock->fd;
i.closed = streambuf_stream_closed;
i.readable = streambuf_stream_readable;
i.writeable = streambuf_stream_writeable;
i.timer = streambuf_stream_timer;
i.obj = &s->obj;
if (poller_add_item(listener->poller, &i))
goto fail;
if (cb->newconn_func)
cb->newconn_func(s);
mutex_lock(&listener->lock);
g_hash_table_insert(listener->streams, s, s); // hand over ref
mutex_unlock(&listener->lock);
return;
fail:
obj_put(s);
}
int streambuf_listener_init(struct streambuf_listener *listener, struct poller *p, const endpoint_t *ep,
streambuf_callback_t newconn_func,
streambuf_callback_t newdata_func,
streambuf_callback_t closed_func,
streambuf_callback_t timer_func,
struct obj *obj)
{
struct streambuf_callback *cb;
ZERO(*listener);
listener->poller = p;
mutex_init(&listener->lock);
listener->streams = g_hash_table_new(g_direct_hash, g_direct_equal);
cb = obj_alloc("streambuf_callback", sizeof(*cb), NULL);
cb->newconn_func = newconn_func;
cb->newdata_func = newdata_func;
cb->closed_func = closed_func;
cb->timer_func = timer_func;
cb->parent = obj_get_o(obj);
cb->listener = listener;
if (tcp_listener_init(&listener->listener, p, ep, streambuf_listener_newconn, &cb->obj))
goto fail;
return 0;
fail:
obj_put(cb);
return -1;
}
void streambuf_stream_close(struct streambuf_stream *s) {
streambuf_stream_closed(s->sock.fd, s, 0);
}
void streambuf_stream_shutdown(struct streambuf_stream *s) {
shutdown(s->sock.fd, SHUT_WR);
}

@ -0,0 +1,47 @@
#ifndef _TCP_LISTENER_H_
#define _TCP_LISTENER_H_
#include "socket.h"
#include "obj.h"
struct poller;
struct obj;
struct streambuf_callback;
struct streambuf_stream;
typedef void (*tcp_listener_callback_t)(struct obj *p, socket_t *sock, char *addr, socket_t *);
typedef void (*streambuf_callback_t)(struct streambuf_stream *);
struct streambuf_listener {
socket_t listener;
struct poller *poller;
mutex_t lock;
GHashTable *streams;
};
struct streambuf_stream {
struct obj obj;
socket_t sock;
struct streambuf_listener *listener;
struct streambuf_callback *cb;
struct obj *parent;
char *addr;
struct streambuf *inbuf,
*outbuf;
};
int tcp_listener_init(socket_t *, struct poller *p, const endpoint_t *, tcp_listener_callback_t, struct obj *);
int streambuf_listener_init(struct streambuf_listener *listener, struct poller *p, const endpoint_t *ep,
streambuf_callback_t newconn_func,
streambuf_callback_t newdata_func,
streambuf_callback_t closed_func,
streambuf_callback_t timer_func,
struct obj *obj);
void streambuf_stream_close(struct streambuf_stream *);
void streambuf_stream_shutdown(struct streambuf_stream *);
#endif

@ -17,7 +17,7 @@
struct udp_listener_callback {
struct obj obj;
udp_listener_callback_t func;
struct udp_listener *ul;
socket_t *ul;
struct obj *p;
};
@ -31,13 +31,11 @@ static void udp_listener_incoming(int fd, void *p, uintptr_t x) {
char buf[0x10000];
char addr[64];
str str;
struct udp_listener *ul;
socket_t *listener;
endpoint_t sin;
str.s = buf;
ul = cb->ul;
listener = &ul->sock;
listener = cb->ul;
for (;;) {
len = socket_recvfrom(listener, buf, sizeof(buf)-1, &sin);
@ -53,11 +51,11 @@ static void udp_listener_incoming(int fd, void *p, uintptr_t x) {
endpoint_print(&sin, addr, sizeof(addr));
str.len = len;
cb->func(cb->p, &str, &sin, addr, ul);
cb->func(cb->p, &str, &sin, addr, listener);
}
}
int udp_listener_init(struct udp_listener *u, struct poller *p, const endpoint_t *ep,
int udp_listener_init(socket_t *sock, struct poller *p, const endpoint_t *ep,
udp_listener_callback_t func, struct obj *obj)
{
struct poller_item i;
@ -66,15 +64,13 @@ int udp_listener_init(struct udp_listener *u, struct poller *p, const endpoint_t
cb = obj_alloc("udp_listener_callback", sizeof(*cb), NULL);
cb->func = func;
cb->p = obj_get_o(obj);
cb->ul = u;
cb->ul = sock;
if (open_socket(&u->sock, SOCK_DGRAM, ep->port, &ep->address))
if (open_socket(sock, SOCK_DGRAM, ep->port, &ep->address))
goto fail;
ipv6only(u->sock.fd, 1);
ZERO(i);
i.fd = u->sock.fd;
i.fd = sock->fd;
i.closed = udp_listener_closed;
i.readable = udp_listener_incoming;
i.obj = &cb->obj;
@ -84,7 +80,7 @@ int udp_listener_init(struct udp_listener *u, struct poller *p, const endpoint_t
return 0;
fail:
close_socket(&u->sock);
close_socket(sock);
obj_put_o(obj);
obj_put(cb);
return -1;

@ -9,16 +9,9 @@
struct poller;
struct obj;
struct udp_listener;
typedef void (*udp_listener_callback_t)(struct obj *p, str *buf, const endpoint_t *ep, char *addr,
struct udp_listener *);
typedef void (*udp_listener_callback_t)(struct obj *p, str *buf, const endpoint_t *ep, char *addr, socket_t *);
struct udp_listener {
socket_t sock;
struct poller *poller;
};
int udp_listener_init(struct udp_listener *, struct poller *p, const endpoint_t *, udp_listener_callback_t, struct obj *);
int udp_listener_init(socket_t *, struct poller *p, const endpoint_t *, udp_listener_callback_t, struct obj *);
#endif

13
debian/.gitignore vendored

@ -0,0 +1,13 @@
*.debhelper
*.log
*.substvars
/.debhelper/
/debhelper-build-stamp
/files
/ngcp-rtpengine-daemon/
/ngcp-rtpengine-iptables/
/ngcp-rtpengine-kernel-dkms/
/ngcp-rtpengine-kernel-source/
/ngcp-rtpengine-recording-daemon/
/ngcp-rtpengine-utils/
/ngcp-rtpengine/

52
debian/changelog vendored

@ -1,3 +1,55 @@
ngcp-rtpengine (6.1.0.0+0~mr6.1.0.0) unstable; urgency=medium
[ Richard Fuchs ]
* [5bcbf27] fix metadata DB insert without trailing pipe character
* [13d51f0] add extra thread allowance for blocking CLI code
* [cc056c1] add rtcp-mux-require option to force rtcp-mux usage for WebRTC
* [23eebfc] Fix frame PTS when decoder returns multiple frames in a row
* [ba46768] small fixes and improvements for packet forwarding feature #411
* [031921c] purge old entries from SSRC hash table if it gets too full
[ Claudiu Boriga ]
* [ca622b4] set TOS for redis streams
* [d5fea12] check call.recording structure before using it
* [3dcddf3] recording-daemon: add option to forward calls
* [2250ab1] make METADATA section appear in the metafile at intialization when recording using the proc method
* [ab0cb0c] don't use g_hash_table_foreach for call_timer_iterator
[ Pawel Kuzak ]
* [f81fdf4] Deleted outdated graphics
[ Guillem Jover ]
* [d5f7624] TT#24097 Fix perl filehandle usage
* [4f006a5] TT#24097 Use block form for map and grep
* [aa2eaee] TT#24097 Use a regex for split
* [bf514fb] TT#24097 Do not directly return result from sort
* [d5ad4a9] TT#24097 Do not use magic variable names
* [301af4a] TT#24097 Declare variable as my
* [0c728b2] TT#24097 Localize %ENV variable assignments
* [b4d0ff7] TT#24097 Use upper-case HERE-doc markers
* [0012037] TT#24097 Allow perl builtin homonyms for method names
* [acf84a7] TT#24097 Use semicolon instead of colons for end of statement
* [cff9d29] TT#24097 Reorder hash assignment to make perlcritic life easier
* [0bdc116] TT#24097 Rework perl code flow
* [b2636dc] TT#24097 Enable strict and warnings everywhere
* [19b4df7] TT#24097 Do not use unportable test operators
* [3351821] TT#24097 Remove unused shell variables
* [747661f] TT#24097 Do not use bash builtin names in sh scripts
* [04f5790] TT#24097 Switch for loop to use the i variable
* [33bafcf] TT#24097 Use $() instead of deprecated ``
* [376df64] TT#24097 Use «grep -E» instead of deprecated egrep
* [53b3c36] TT#24097 Fix shell quoting
* [465f3fe] TT#24097 Remove $ from variables inside arithmetic evaluation
* [677c9a5] TT#22072 Update packaging
* [6721f3c] TT#22072 Update debian/.gitignore
[ Dmitry Poroh ]
* [cc09f4d] Crash stream_packet in case of out_srtp is NULL is fixed
[ Sipwise Jenkins Builder ]
-- Sipwise Jenkins Builder <jenkins@sipwise.com> Wed, 06 Dec 2017 00:14:35 +0100
ngcp-rtpengine (6.0.0.0+0~mr6.0.0.0) unstable; urgency=medium
[ Richard Fuchs ]

2
debian/compat vendored

@ -1 +1 @@
9
10

29
debian/control vendored

@ -1,9 +1,11 @@
Source: ngcp-rtpengine
Section: net
Priority: extra
Priority: optional
Maintainer: Sipwise Development Team <support@sipwise.com>
Homepage: https://www.sipwise.com/
Standards-Version: 3.9.8
Build-Depends:
debhelper (>= 9~),
debhelper (>= 10~),
default-libmysqlclient-dev | libmysqlclient-dev,
iptables-dev (>= 1.4),
libavcodec-dev (>= 6:10),
@ -23,8 +25,6 @@ Build-Depends:
libxmlrpc-c3-dev (>= 1.16.07) | libxmlrpc-core-c3-dev (>= 1.16.07),
markdown,
zlib1g-dev,
Standards-Version: 3.9.7
Homepage: http://sipwise.com/
Package: ngcp-rtpengine-daemon
Architecture: any
@ -122,27 +122,6 @@ Description: IPtables kernel module for the NGCP media proxy - DKMS.
performance packet forwarding.
This package contains the source to be built with dkms.
Package: ngcp-rtpengine-dbg
Architecture: any
Section: debug
Depends:
ngcp-rtpengine-daemon (= ${binary:Version}),
ngcp-rtpengine-iptables (= ${binary:Version}),
${misc:Depends},
Conflicts:
ngcp-mediaproxy-ng-dbg,
Replaces:
ngcp-mediaproxy-ng-dbg,
Description: debugging symbols for ngcp-rtpengine
The ngcp-rtpengine daemon handles the first stages of proxying media streams and talks to
the kernel part of the proxy for eventual high-performance packet forwarding.
.
ngcp-rtpengine-iptables provides the IPtables extension needed
to configure the mediaproxy rule.
.
This package contains the debugging symbols for ngcp-rtpengine-daemon
and ngcp-rtpengine-iptables
Package: ngcp-rtpengine-utils
Architecture: all
Depends:

@ -10,6 +10,7 @@
# Description: Proxy for RTP and other media streams
### END INIT INFO
set -e
PATH=/sbin:/bin:/usr/sbin:/usr/bin
NAME=ngcp-rtpengine-daemon
@ -22,21 +23,19 @@ DEFAULTS=/etc/default/${NAME}
test -f "$DAEMON" || exit 0
. /lib/lsb/init-functions
# Load startup options if available
if [ -f "$DEFAULTS" ]; then
. "$DEFAULTS" || true
fi
if [ "$RUN_RTPENGINE" != "yes" ]; then
echo "rtpengine not yet configured. Edit $DEFAULTS first."
log_action_msg "rtpengine not yet configured. Edit $DEFAULTS first."
exit 0
fi
[ -z "$PIDFILE" ] && PIDFILE="/var/run/rtpengine.pid"
set -e
. /lib/lsb/init-functions
OPTIONS=""
START_OPTIONS=""
MODPROBE_OPTIONS=""
@ -71,16 +70,16 @@ fi
[ -z "$TOS" ] || OPTIONS="$OPTIONS --tos=$TOS"
[ -z "$PORT_MIN" ] || OPTIONS="$OPTIONS --port-min=$PORT_MIN"
[ -z "$PORT_MAX" ] || OPTIONS="$OPTIONS --port-max=$PORT_MAX"
[ -z "$REDIS" -o -z "$REDIS_DB" ] || OPTIONS="$OPTIONS --redis=$REDIS/$REDIS_DB"
[ -z "$REDIS" ] || [ -z "$REDIS_DB" ] || OPTIONS="$OPTIONS --redis=$REDIS/$REDIS_DB"
[ -z "$REDIS_AUTH_PW" ] || export RTPENGINE_REDIS_AUTH_PW="$REDIS_AUTH_PW"
[ -z "$REDIS_WRITE" -o -z "$REDIS_WRITE_DB" ] || OPTIONS="$OPTIONS --redis-write=$REDIS_WRITE/$REDIS_WRITE_DB"
[ -z "$REDIS_WRITE" ] || [ -z "$REDIS_WRITE_DB" ] || OPTIONS="$OPTIONS --redis-write=$REDIS_WRITE/$REDIS_WRITE_DB"
[ -z "$REDIS_WRITE_AUTH_PW" ] || export RTPENGINE_REDIS_WRITE_AUTH_PW="$REDIS_WRITE_AUTH_PW"
[ -z "$REDIS_NUM_THREADS" ] || OPTIONS="$OPTIONS --redis-num-threads=$REDIS_NUM_THREADS"
[ -z "$REDIS_EXPIRES" ] || OPTIONS="$OPTIONS --redis-expires=$REDIS_EXPIRES"
[ -z "$REDIS_MULTIKEY" ] || OPTIONS="$OPTIONS --redis-multikey=$REDIS_MULTIKEY"
[ -z "$NO_REDIS_REQUIRED" -o \( "$NO_REDIS_REQUIRED" != "1" -a "$NO_REDIS_REQUIRED" != "yes" \) ] || OPTIONS="$OPTIONS --no-redis-required"
[ -z "$NO_REDIS_REQUIRED" ] || ( [ "$NO_REDIS_REQUIRED" != "1" ] && [ "$NO_REDIS_REQUIRED" != "yes" ] ) || OPTIONS="$OPTIONS --no-redis-required"
[ -z "$B2B_URL" ] || OPTIONS="$OPTIONS --b2b-url=$B2B_URL"
[ -z "$NO_FALLBACK" -o \( "$NO_FALLBACK" != "1" -a "$NO_FALLBACK" != "yes" \) ] || OPTIONS="$OPTIONS --no-fallback"
[ -z "$NO_FALLBACK" ] || ( [ "$NO_FALLBACK" != "1" ] && [ "$NO_FALLBACK" != "yes" ] ) || OPTIONS="$OPTIONS --no-fallback"
OPTIONS="$OPTIONS --table=$TABLE"
[ -z "$LOG_LEVEL" ] || OPTIONS="$OPTIONS --log-level=$LOG_LEVEL"
[ -z "$LOG_FACILITY" ] || OPTIONS="$OPTIONS --log-facility=$LOG_FACILITY"
@ -104,7 +103,7 @@ if [ ! -z "$RECORDING_DIR" ]; then
fi
[ -z "$RECORDING_METHOD" ] || OPTIONS="$OPTIONS --recording-method=$RECORDING_METHOD"
[ -z "$RECORDING_FORMAT" ] || OPTIONS="$OPTIONS --recording-format=$RECORDING_FORMAT"
[ -z "$DTLS_PASSIVE" -o \( "$DTLS_PASSIVE" != "yes" -a "$DTLS_PASSIVE" != "1" \) ] || OPTIONS="$OPTIONS --dtls-passive"
[ -z "$DTLS_PASSIVE" ] || ( [ "$DTLS_PASSIVE" != "yes" ] && [ "$DTLS_PASSIVE" != "1" ] ) || OPTIONS="$OPTIONS --dtls-passive"
if test "$FORK" = "no" ; then
OPTIONS="$OPTIONS --foreground"
@ -127,19 +126,19 @@ fi
if ! test -z "$SET_USER"; then
START_OPTIONS="$START_OPTIONS --chuid $SET_USER"
UID=$(id -u "$SET_USER" 2> /dev/null)
test -z "$UID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_uid=$UID"
PUID=$(id -u "$SET_USER" 2> /dev/null)
test -z "$PUID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_uid=$PUID"
if test -z "$SET_GROUP"; then
GID=$(id -g "$SET_USER" 2> /dev/null)
test -z "$GID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_gid=$GID"
PGID=$(id -g "$SET_USER" 2> /dev/null)
test -z "$PGID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_gid=$PGID"
fi
test "$DO_DIR_CHOWN" = 1 && chown "$SET_USER": "$PIDDIR"
fi
if ! test -z "$SET_GROUP"; then
START_OPTIONS="$START_OPTIONS --group $SET_GROUP"
GID=$(grep "^$SET_GROUP:" /etc/group | cut -d: -f3 2> /dev/null)
test -z "$GID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_gid=$GID"
PGID=$(grep "^$SET_GROUP:" /etc/group | cut -d: -f3 2> /dev/null)
test -z "$PGID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_gid=$PGID"
test "$DO_DIR_CHOWN" = 1 && chgrp "$SET_GROUP" "$PIDDIR"
fi
@ -159,17 +158,17 @@ case "$1" in
status=$?
case "${status}" in
0|3)
echo "Active node or transition."
log_action_msg "Active node or transition."
;;
*)
echo "Ignored start action in inactive node ($status)"
log_action_msg "Ignored start action in inactive node ($status)"
exit 0
;;
esac
fi
log_daemon_msg "Starting $DESC: $NAME"
if [ "$TABLE" -ge 0 ] && [ "$VIRT" != "yes" ]; then
if [ "$MANAGE_IPTABLES" = "yes" ]; then
# shellcheck disable=SC2086
modprobe xt_RTPENGINE $MODPROBE_OPTIONS
iptables -N rtpengine 2> /dev/null
@ -183,19 +182,18 @@ case "$1" in
ip6tables -D rtpengine -p udp -j RTPENGINE --id "$TABLE" 2>/dev/null
ip6tables -I rtpengine -p udp -j RTPENGINE --id "$TABLE"
fi
if [ -e /proc/rtpengine/control ]; then
echo "del $TABLE" > /proc/rtpengine/control 2>/dev/null
fi
fi
set -e
log_daemon_msg "Starting $DESC" "$NAME"
# shellcheck disable=SC2086
start-stop-daemon --start --quiet --pidfile "$PIDFILE" \
--exec "$DAEMON" $START_OPTIONS -- $OPTIONS || log_progress_msg " already running"
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping $DESC: $NAME"
log_daemon_msg "Stopping $DESC" "$NAME"
start-stop-daemon --oknodo --stop --quiet --pidfile $PIDFILE \
--exec "$DAEMON"
--retry 5 --exec "$DAEMON"
if [ "$?" -ne 0 ]; then
return $?
fi
@ -217,7 +215,6 @@ case "$1" in
;;
restart|force-reload)
$0 stop
sleep 1
$0 start
;;
status)

@ -10,33 +10,31 @@
# Description: Recording daemon for RTP and other media streams
### END INIT INFO
set -e
PATH=/sbin:/bin:/usr/sbin:/usr/bin
NAME=ngcp-rtpengine-recording-daemon
DESC="RTP/media recording daemon"
TABLE=0
DAEMON=$(which rtpengine-recording)
DEFAULTS=/etc/default/${NAME}
test -f "$DAEMON" || exit 0
. /lib/lsb/init-functions
# Load startup options if available
if [ -f "$DEFAULTS" ]; then
. "$DEFAULTS" || true
fi
if [ "$RUN_RTPENGINE_RECORDING" != "yes" ]; then
echo "rtpengine-recording not yet configured. Edit $DEFAULTS first."
log_action_msg "rtpengine-recording not yet configured. Edit $DEFAULTS first."
exit 0
fi
[ -z "$PIDFILE" ] && PIDFILE="/var/run/rtpengine-recording.pid"
[ -z "$NFS_OPTIONS" ] && NFS_OPTIONS="hard,tcp,intr"
set -e
. /lib/lsb/init-functions
OPTIONS=""
START_OPTIONS=""
@ -58,30 +56,16 @@ fi
if ! test -z "$SET_USER"; then
START_OPTIONS="$START_OPTIONS --chuid $SET_USER"
UID=$(id -u "$SET_USER" 2> /dev/null)
test -z "$UID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_uid=$UID"
if test -z "$SET_GROUP"; then
GID=$(id -g "$SET_USER" 2> /dev/null)
test -z "$GID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_gid=$GID"
fi
test "$DO_DIR_CHOWN" = 1 && chown "$SET_USER": "$PIDDIR"
fi
if ! test -z "$SET_GROUP"; then
START_OPTIONS="$START_OPTIONS --group $SET_GROUP"
GID=$(grep "^$SET_GROUP:" /etc/group | cut -d: -f3 2> /dev/null)
test -z "$GID" || MODPROBE_OPTIONS="$MODPROBE_OPTIONS proc_gid=$GID"
test "$DO_DIR_CHOWN" = 1 && chgrp "$SET_GROUP" "$PIDDIR"
fi
###
if [ -x /usr/sbin/ngcp-virt-identify ]; then
if /usr/sbin/ngcp-virt-identify --type container; then
VIRT="yes"
fi
fi
case "$1" in
start)
set +e
@ -90,34 +74,35 @@ case "$1" in
status=$?
case "${status}" in
0|3)
echo "Active node or transition."
log_action_msg "Active node or transition."
;;
*)
echo "Ignored start action in inactive node ($status)"
log_action_msg "Ignored start action in inactive node ($status)"
exit 0
;;
esac
fi
set -e
log_daemon_msg "Starting $DESC: $NAME"
log_daemon_msg "Starting $DESC" "$NAME"
if [ "$MUST_NFS" = yes ]; then
if ! egrep -q '^[^ :]+:[^ :]+ '"$NFS_LOCAL_MOUNT"' nfs.? ' /proc/mounts; then
if ! grep -E -q "^[^ :]+:[^ :]+ $NFS_LOCAL_MOUNT nfs.? " /proc/mounts; then
log_progress_msg "Mounting NFS share"
test -d "$NFS_LOCAL_MOUNT" || mkdir -p "$NFS_LOCAL_MOUNT"
mount -t nfs -o "$NFS_OPTIONS" "$NFS_HOST":"$NFS_REMOTE_PATH" "$NFS_LOCAL_MOUNT"
mount -t nfs -o "$NFS_OPTIONS" "$NFS_HOST:$NFS_REMOTE_PATH" "$NFS_LOCAL_MOUNT"
fi
fi
# shellcheck disable=SC2086
start-stop-daemon --start --quiet --pidfile "$PIDFILE" \
--exec "$DAEMON" $START_OPTIONS -- $OPTIONS || log_progress_msg " already running"
log_end_msg $?
;;
stop)
log_daemon_msg "Stopping $DESC: $NAME"
log_daemon_msg "Stopping $DESC" "$NAME"
start-stop-daemon --oknodo --stop --quiet --pidfile $PIDFILE \
--exec "$DAEMON"
--retry 5 --exec "$DAEMON"
if [ "$?" -ne 0 ]; then
return $?
fi

4
debian/rules vendored

@ -12,7 +12,7 @@ pdkms:=ngcp-rtpengine-kernel-dkms
# short upstream name, used for module source directory
sname:=ngcp-rtpengine
# Source version
sversion:=$(shell dpkg-parsechangelog|grep "^Version:"|cut -d" " -f2|rev|cut -d- -f2-|rev|cut -d':' -f2)
sversion:=$(shell dpkg-parsechangelog -SVersion | sed -e 's/^[^:]\+://;s/-[^-]\+$$//')
PACKAGE=ngcp-rtpengine-kernel
## end of kernel package specific stuff
@ -68,6 +68,6 @@ override_dh_install:
.PHONY: override_dh_strip
override_dh_strip:
dh_strip --dbg-package=ngcp-rtpengine-dbg
dh_strip --dbgsym-migration='ngcp-rtpengine-dbg (<= 6.0.0.0+0~mr6.0.0.0)'
override_dh_auto_test:

@ -39,7 +39,7 @@ MODULE=0
build_opts() {
shopt -s nocasematch
RPMS=`rpm -qa | grep rtpengine-kernel`
RPMS=$(rpm -qa | grep rtpengine-kernel)
if [[ "$KERNEL" == "yes" && -n "$TABLE" && -n "$RPMS" ]]
then
MODULE=1
@ -179,15 +179,15 @@ start() {
then
if [[ -n "$RE_GROUP" ]]
then
proc_gid=$(grep ^$RE_GROUP: /etc/group | cut -f3 -d:)
proc_gid="$(grep "^$RE_GROUP:" /etc/group | cut -f3 -d:)"
else
proc_gid=$(id $RE_USER -g)
proc_gid="$(id "$RE_USER" -g)"
fi
modprobe xt_RTPENGINE proc_uid=$(id $RE_USER -u) proc_gid=$proc_gid
modprobe xt_RTPENGINE proc_uid="$(id "$RE_USER" -u)" proc_gid="$proc_gid"
else
modprobe xt_RTPENGINE
fi
temp=`firewall-cmd --state 2>/dev/null`
firewall-cmd --state 2>/dev/null
if [[ $? == 0 ]]
then
# Using firewalld
@ -202,15 +202,15 @@ start() {
firewall-cmd --direct --add-chain ipv4 filter rtpengine
firewall-cmd --direct --add-rule ipv4 filter INPUT_prefilter 0 -j rtpengine
firewall-cmd --direct --add-rule ipv4 filter rtpengine 0 -p udp -j RTPENGINE --id $TABLE
firewall-cmd --direct --add-rule ipv6 filter rtpengine 0 -p udp -j RTPENGINE --id $TABLE
firewall-cmd --direct --add-rule ipv4 filter rtpengine 0 -p udp -j RTPENGINE --id "$TABLE"
firewall-cmd --direct --add-rule ipv6 filter rtpengine 0 -p udp -j RTPENGINE --id "$TABLE"
firewall-cmd --reload
else
iptables -N rtpengine
# We insert the rtpengine rule at the top of the input chain
iptables -t filter -I INPUT -j rtpengine
iptables -I rtpengine -p udp -j RTPENGINE --id $TABLE
ip6tables -I rtpengine -p udp -j RTPENGINE --id $TABLE
iptables -I rtpengine -p udp -j RTPENGINE --id "$TABLE"
ip6tables -I rtpengine -p udp -j RTPENGINE --id "$TABLE"
fi
cat <<EOF > "$cachefile"
@ -220,19 +220,21 @@ EOF
echo -n $"Starting $prog: "
if [[ -n "$RE_USER" ]]
then
daemon --user $RE_USER --pidfile=${pidfile} $rtpengine $OPTS
# shellcheck disable=SC2086
daemon --user "$RE_USER" --pidfile="${pidfile}" "$rtpengine" $OPTS
else
daemon --pidfile=${pidfile} $rtpengine $OPTS
# shellcheck disable=SC2086
daemon --pidfile="${pidfile}" "$rtpengine" $OPTS
fi
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch ${lockfile}
[ $RETVAL = 0 ] && touch "${lockfile}"
return $RETVAL
}
stop() {
echo -n $"Stopping $prog: "
killproc -p ${pidfile} $rtpengine
killproc -p "${pidfile}" "$rtpengine"
RETVAL=$?
echo
if [ -f "$cachefile" ]
@ -240,7 +242,7 @@ stop() {
. "$cachefile"
echo "Unloading module for in-kernel packet forwarding"
echo "del $TABLE" > /proc/rtpengine/control
temp=`firewall-cmd --state 2>/dev/null`
firewall-cmd --state 2>/dev/null
if [[ $? == 0 ]]
then
firewall-cmd --direct --remove-rules ipv4 filter rtpengine
@ -249,16 +251,16 @@ stop() {
firewall-cmd --direct --remove-chain ipv4 filter rtpengine
firewall-cmd --reload
else
iptables -D rtpengine -p udp -j RTPENGINE --id $CUR_TABLE
ip6tables -D rtpengine -p udp -j RTPENGINE --id $CUR_TABLE
iptables -D rtpengine -p udp -j RTPENGINE --id "$CUR_TABLE"
ip6tables -D rtpengine -p udp -j RTPENGINE --id "$CUR_TABLE"
iptables -t filter -D INPUT -j rtpengine
iptables -X rtpengine
fi
rmmod xt_RTPENGINE
rm -f $cachefile
rm -f "$cachefile"
fi
[ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile}
[ $RETVAL = 0 ] && rm -f "${lockfile}" "${pidfile}"
}
# See how we were called.
@ -270,7 +272,7 @@ case "$1" in
stop
;;
status)
status -p ${pidfile} $rtpengine
status -p "${pidfile}" "$rtpengine"
RETVAL=$?
;;
restart)
@ -278,7 +280,7 @@ case "$1" in
start
;;
condrestart|try-restart)
if status -p ${pidfile} $rtpengine >&/dev/null; then
if status -p "${pidfile}" "$rtpengine" >&/dev/null; then
stop
start
fi

@ -21,6 +21,7 @@ listen-ng = 127.0.0.1:2223
timeout = 60
silent-timeout = 3600
tos = 184
#control-tos = 184
# delete-delay = 30
# final-timeout = 10800
@ -41,6 +42,10 @@ port-max = 50000
# redis-num-threads = 8
# no-redis-required = false
# redis-expires = 86400
# redis-allowed-errors = -1
# redis-disable-time = 10
# redis-cmd-timeout = 0
# redis-connect-timeout = 1000
# b2b-url = http://127.0.0.1:8090/
# xmlrpc-format = 0

@ -7,18 +7,15 @@
#include <string.h>
#include <errno.h>
#include "log.h"
#include "loglib.h"
static const char *config_file;
static const char *config_section;
static const char *pid_file;
static const char *log_facility;
static int foreground;
static int version;
struct rtpengine_common_config *rtpe_common_config_ptr;
void daemonize(void) {
if (foreground)
if (rtpe_common_config_ptr->foreground)
return;
if (fork())
_exit(0);
@ -32,10 +29,10 @@ void daemonize(void) {
void wpidfile() {
FILE *fp;
if (!pid_file)
if (!rtpe_common_config_ptr->pidfile)
return;
fp = fopen(pid_file, "w");
fp = fopen(rtpe_common_config_ptr->pidfile, "w");
if (!fp) {
ilog(LOG_CRIT, "Failed to create PID file (%s), aborting startup", strerror(errno));
exit(-1);
@ -54,22 +51,10 @@ static unsigned int options_length(const GOptionEntry *arr) {
}
static const GOptionEntry shared_options[] = {
{ "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Print build time and exit", NULL },
{ "config-file", 0, 0, G_OPTION_ARG_STRING, &config_file, "Load config from this file", "FILE" },
{ "config-section", 0, 0, G_OPTION_ARG_STRING, &config_section,"Config file section to use", "STRING" },
{ "log-facility", 0, 0, G_OPTION_ARG_STRING, &log_facility, "Syslog facility to use for logging", "daemon|local0|...|local7"},
{ "log-level", 'L', 0, G_OPTION_ARG_INT, (void *)&log_level,"Mask log priorities above this level","INT" },
{ "log-stderr", 'E', 0, G_OPTION_ARG_NONE, &ilog_stderr, "Log on stderr instead of syslog", NULL },
{ "pidfile", 'p', 0, G_OPTION_ARG_FILENAME, &pid_file, "Write PID to file", "FILE" },
{ "foreground", 'f', 0, G_OPTION_ARG_NONE, &foreground, "Don't fork to background", NULL },
{ NULL, }
};
#define CONF_OPTION_GLUE(get_func, data_type, ...) \
{ \
data_type *varptr = e->arg_data; \
data_type var = g_key_file_get_ ## get_func(kf, config_section, e->long_name, \
data_type var = g_key_file_get_ ## get_func(kf, rtpe_common_config_ptr->config_section, e->long_name, \
##__VA_ARGS__, &er); \
if (er && g_error_matches(er, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) { \
g_error_free(er); \
@ -83,7 +68,8 @@ static const GOptionEntry shared_options[] = {
}
void config_load(int *argc, char ***argv, GOptionEntry *app_entries, const char *description,
const char *default_config, const char *default_section)
char *default_config, char *default_section,
struct rtpengine_common_config *cconfig)
{
GOptionContext *c;
GError *er = NULL;
@ -92,6 +78,28 @@ void config_load(int *argc, char ***argv, GOptionEntry *app_entries, const char
int saved_argc = *argc;
char **saved_argv = g_strdupv(*argv);
rtpe_common_config_ptr = cconfig;
// defaults
#ifndef __DEBUG
rtpe_common_config_ptr->log_level = LOG_INFO;
#else
rtpe_common_config_ptr->log_level = LOG_DEBUG;
#endif
GOptionEntry shared_options[] = {
{ "version", 'v', 0, G_OPTION_ARG_NONE, &version, "Print build time and exit", NULL },
{ "config-file", 0, 0, G_OPTION_ARG_STRING, &rtpe_common_config_ptr->config_file, "Load config from this file", "FILE" },
{ "config-section", 0, 0, G_OPTION_ARG_STRING, &rtpe_common_config_ptr->config_section,"Config file section to use", "STRING" },
{ "log-facility", 0, 0, G_OPTION_ARG_STRING, &rtpe_common_config_ptr->log_facility, "Syslog facility to use for logging", "daemon|local0|...|local7"},
{ "log-level", 'L', 0, G_OPTION_ARG_INT, (void *)&rtpe_common_config_ptr->log_level,"Mask log priorities above this level","INT" },
{ "log-stderr", 'E', 0, G_OPTION_ARG_NONE, &rtpe_common_config_ptr->log_stderr, "Log on stderr instead of syslog", NULL },
{ "pidfile", 'p', 0, G_OPTION_ARG_FILENAME, &rtpe_common_config_ptr->pidfile, "Write PID to file", "FILE" },
{ "foreground", 'f', 0, G_OPTION_ARG_NONE, &rtpe_common_config_ptr->foreground, "Don't fork to background", NULL },
{ NULL, }
};
// prepend shared CLI options
unsigned int shared_len = options_length(shared_options);
unsigned int app_len = options_length(app_entries);
@ -99,8 +107,8 @@ void config_load(int *argc, char ***argv, GOptionEntry *app_entries, const char
memcpy(entries, shared_options, sizeof(*entries) * shared_len);
memcpy(&entries[shared_len], app_entries, sizeof(*entries) * (app_len + 1));
if (!config_section)
config_section = default_section;
if (!rtpe_common_config_ptr->config_section)
rtpe_common_config_ptr->config_section = default_section;
c = g_option_context_new(description);
g_option_context_add_main_entries(c, entries, NULL);
@ -109,8 +117,8 @@ void config_load(int *argc, char ***argv, GOptionEntry *app_entries, const char
// is there a config file to load?
use_config = default_config;
if (config_file) {
use_config = config_file;
if (rtpe_common_config_ptr->config_file) {
use_config = rtpe_common_config_ptr->config_file;
fatal = 1;
}
@ -153,14 +161,17 @@ out:
}
if (log_facility) {
if (!parse_log_facility(log_facility, &ilog_facility)) {
if (rtpe_common_config_ptr->log_facility) {
if (!parse_log_facility(rtpe_common_config_ptr->log_facility, &ilog_facility)) {
print_available_log_facilities();
die ("Invalid log facility '%s' (--log-facility)", log_facility);
die ("Invalid log facility '%s' (--log-facility)", rtpe_common_config_ptr->log_facility);
}
}
if (ilog_stderr) {
if ((rtpe_common_config_ptr->log_level < LOG_EMERG) || (rtpe_common_config_ptr->log_level > LOG_DEBUG))
die("Invalid log level (--log_level)");
if (rtpe_common_config_ptr->log_stderr) {
write_log = log_to_stderr;
max_log_line_length = 0;
}

@ -3,10 +3,23 @@
#include <glib.h>
struct rtpengine_common_config {
char *config_file;
char *config_section;
char *log_facility;
volatile int log_level;
int log_stderr;
char *pidfile;
int foreground;
};
extern struct rtpengine_common_config *rtpe_common_config_ptr;
void daemonize(void);
void wpidfile(void);
void config_load(int *argc, char ***argv, GOptionEntry *entries, const char *description,
const char *default_config, const char *default_section);
char *default_config, char *default_section,
struct rtpengine_common_config *);
#endif

@ -8,6 +8,7 @@
#include <pthread.h>
#include <sys/time.h>
#include <string.h>
#include "auxlib.h"
struct log_limiter_entry {
@ -22,14 +23,6 @@ typedef struct _fac_code {
#ifndef __DEBUG
volatile gint log_level = LOG_INFO;
#else
volatile gint log_level = LOG_DEBUG;
#endif
static write_log_t log_both;
unsigned int max_log_line_length = 500;
@ -73,7 +66,6 @@ static const char* const prio_str[] = {
"DEBUG"
};
gboolean ilog_stderr = 0;
int ilog_facility = LOG_DAEMON;
@ -230,7 +222,7 @@ void log_init(const char *handle) {
__log_limiter = g_hash_table_new(log_limiter_entry_hash, log_limiter_entry_equal);
__log_limiter_strings = g_string_chunk_new(1024);
if (!ilog_stderr)
if (!rtpe_common_config_ptr->log_stderr)
openlog(handle, LOG_PID | LOG_NDELAY, ilog_facility);
}

@ -6,13 +6,11 @@
#include <syslog.h>
#include <stdarg.h>
#include "compat.h"
#include "auxlib.h"
extern gboolean ilog_stderr;
extern int ilog_facility;
extern volatile gint log_level;
extern unsigned int max_log_line_length;
@ -46,7 +44,7 @@ void __ilog_np(int prio, const char *format, ...) __attribute__ ((format (printf
INLINE int get_log_level(void) {
return g_atomic_int_get(&log_level);
return g_atomic_int_get(&rtpe_common_config_ptr->log_level);
}

@ -21,6 +21,7 @@ struct rtp_payload_type {
str encoding;
unsigned int clock_rate;
str encoding_parameters;
str format_parameters;
};

@ -35,7 +35,7 @@ INLINE char *str_end(const str *s);
/* returns pointer to first occurrence of "c" in s */
INLINE char *str_chr(const str *s, int c);
/* sets "out" to point to first occurrence of c in s. adjusts len also */
INLINE str *str_chr_str(str *out, const str *s, int c);
INLINE char *str_chr_str(str *out, const str *s, int c);
/* compares a str to a regular string */
INLINE int str_cmp(const str *a, const char *b);
/* compares a str to a non-null-terminated string */
@ -70,7 +70,7 @@ INLINE void str_swap(str *a, str *b);
INLINE int str_to_i(str *s, int def);
/* parses a string uinto an int, returns default if conversion fails */
INLINE uint str_to_ui(str *s, int def);
/* extracts the first/next token into "new_token" and modifies "ori_and_remainer" in place */
/* extracts the first/next token into "new_token" and modifies "ori_and_remaidner" in place */
INLINE int str_token(str *new_token, str *ori_and_remainder, int sep);
/* same as str_token but allows for a trailing non-empty token (e.g. "foo,bar" -> "foo", "bar" ) */
INLINE int str_token_sep(str *new_token, str *ori_and_remainder, int sep);
@ -132,16 +132,16 @@ INLINE int str_shift_cmp(str *s, const char *t) {
INLINE char *str_chr(const str *s, int c) {
return memchr(s->s, c, s->len);
}
INLINE str *str_chr_str(str *out, const str *s, int c) {
INLINE char *str_chr_str(str *out, const str *s, int c) {
char *p;
p = str_chr(s, c);
if (!p) {
*out = STR_NULL;
return out;
return NULL;
}
*out = *s;
str_shift(out, p - out->s);
return out;
return out->s;
}
INLINE int str_cmp_len(const str *a, const char *b, int l) {
if (a->len < l)

@ -67,7 +67,7 @@ sub set_cert {
}
# XXX unify these two
sub connect {
sub connect { ## no critic: Subroutines::ProhibitBuiltinHomonyms
my ($self) = @_;
$self->{_connected} and return;
@ -95,7 +95,7 @@ sub connect {
$self->{_mux}->add($near);
$self->{_mux}->add($openssl_out);
}
sub accept {
sub accept { ## no critic: Subroutines::ProhibitBuiltinHomonyms
my ($self) = @_;
$self->{_connected} and return;
@ -303,13 +303,13 @@ sub encode {
my ($self, @rest) = @_;
return $self->[0]->encode(@rest);
}
sub connect {
sub connect { ## no critic: Subroutines::ProhibitBuiltinHomonyms
my ($self, @rest) = @_;
for my $cl (@$self) {
$cl->accept(@rest);
}
}
sub accept {
sub accept { ## no critic: Subroutines::ProhibitBuiltinHomonyms
my ($self, @rest) = @_;
for my $cl (@$self) {
$cl->accept(@rest);

@ -596,17 +596,17 @@ sub stun_handler_binding_success {
sub check_to_nominate {
my ($self) = @_;
$self->{controlling} or return;
$self->{start_nominating} && time() < $self->{start_nominating} and return;
$self->{nominate} and return;
@{$self->{triggered_checks}} and return;
return unless $self->{controlling};
return if $self->{start_nominating} && time() < $self->{start_nominating};
return if $self->{nominate};
return if @{$self->{triggered_checks}};
my @succeeded;
for my $pair (values(%{$self->{candidate_pairs}})) {
my @comps = @{$pair->{components}};
my @succeeded_comps = grep {$_->{state} eq 'succeeded'} @comps;
@succeeded_comps < $self->{components} and next;
next if @succeeded_comps < $self->{components};
$self->debug("got fully succeeded pair $pair->{foundation}\n");
push(@succeeded, $pair);
}
@ -753,7 +753,8 @@ sub timer {
# run checks
defined($self->{other_ufrag}) && defined($self->{other_pwd}) or return; # not enough info
# not enough info
return if !defined($self->{other_ufrag}) || !defined($self->{other_pwd});
if (my $pair = shift(@{$self->{triggered_checks}})) {
$pair->debug("running triggered check\n");
@ -795,7 +796,10 @@ sub keepalives {
sub sort_pairs {
my ($pair_list) = @_;
return sort {$a->priority() <=> $b->priority()} @$pair_list;
my @sorted_list = sort {
$a->priority() <=> $b->priority()
} @{$pair_list};
return @sorted_list;
}
sub get_send_component {

@ -48,11 +48,11 @@ sub req {
sub offer {
my ($self, $packet) = @_;
return $self->req( { %$packet, command => 'offer' } );
return $self->req( { command => 'offer', %$packet } );
}
sub answer {
my ($self, $packet) = @_;
return $self->req( { %$packet, command => 'answer' } );
return $self->req( { command => 'answer', %$packet } );
}
1;

@ -50,7 +50,7 @@ sub new {
$self->{mux} = IO::Multiplex->new();
$self->{mux}->set_callback_object($self);
$self->{media_port} = 2000;
$self->{media_port} = $args{media_port} // 2000;
$self->{timers} = [];
$self->{clients} = [];
@ -149,11 +149,13 @@ sub _new {
($args{sockdomain} && $args{sockdomain} != $address->{sockdomain}) and next;
my $rtp = IO::Socket::IP->new(Type => &SOCK_DGRAM, Proto => 'udp',
LocalHost => $address->{address}, LocalPort => $parent->{media_port}++)
or die($address->{address});
LocalHost => $address->{address}, LocalPort => $parent->{media_port})
or die("$address->{address}:$parent->{media_port}");
$parent->{media_port}++;
my $rtcp = IO::Socket::IP->new(Type => &SOCK_DGRAM, Proto => 'udp',
LocalHost => $address->{address}, LocalPort => $parent->{media_port}++)
or die($address->{address});
LocalHost => $address->{address}, LocalPort => $parent->{media_port})
or die("$address->{address}:$parent->{media_port}");
$parent->{media_port}++;
push(@sockets, [$rtp, $rtcp]); # component 0 and 1
push(@rtp, $rtp);
@ -271,7 +273,7 @@ sub _default_req_args {
my $req = { command => $cmd, 'call-id' => $self->{parent}->{callid} };
for my $cp (qw(sdp from-tag to-tag ICE transport-protocol address-family label)) {
for my $cp (qw(sdp from-tag to-tag ICE transport-protocol address-family label direction)) {
$args{$cp} and $req->{$cp} = $args{$cp};
}
for my $cp (@{$args{flags}}) {
@ -298,6 +300,7 @@ sub _offered {
my ($self, $req) = @_;
my $sdp_body = $req->{sdp} or die;
$self->{remote_sdp_raw} = $sdp_body;
$self->{remote_sdp} = NGCP::Rtpclient::SDP->decode($sdp_body);
# XXX validate SDP
@{$self->{remote_sdp}->{medias}} == 1 or die;
@ -323,6 +326,7 @@ sub _answered {
my ($self, $req) = @_;
my $sdp_body = $req->{sdp} or die;
$self->{remote_sdp_raw} = $sdp_body;
$self->{remote_sdp} = NGCP::Rtpclient::SDP->decode($sdp_body);
# XXX validate SDP
@{$self->{remote_sdp}->{medias}} == 1 or die;

@ -45,6 +45,9 @@ static GQueue threads = G_QUEUE_INIT; // only accessed from main thread
volatile int shutdown_flag;
struct rtpengine_common_config rtpe_common_config;
static void signals(void) {
sigset_t ss;
@ -186,7 +189,7 @@ static void options(int *argc, char ***argv) {
};
config_load(argc, argv, e, " - rtpengine recording daemon",
"/etc/rtpengine/rtpengine-recording.conf", "rtpengine-recording");
"/etc/rtpengine/rtpengine-recording.conf", "rtpengine-recording", &rtpe_common_config);
if (!strcmp(output_format, "none")) {
output_enabled = 0;

@ -2,6 +2,9 @@
#define _MAIN_H_
#include "auxlib.h"
extern int ktable;
extern int num_threads;
extern const char *spool_dir;
@ -19,4 +22,7 @@ extern const char *forward_to;
extern volatile int shutdown_flag;
extern struct rtpengine_common_config rtpe_common_config;
#endif

@ -0,0 +1,55 @@
#!/usr/bin/perl
use strict;
use warnings;
use NGCP::Rtpengine::Test;
use IO::Socket;
my $iterations = 10;
my @interfaces = qw(int ext);
my @domains = (&Socket::AF_INET);
my $r = NGCP::Rtpengine::Test->new(media_port => 50000);
for my $a_domain (@domains) {
for my $b_domain (@domains) {
if (!@interfaces) {
for (1 .. $iterations) {
run_test([], $a_domain, $b_domain);
}
}
else {
for my $a_interface (@interfaces) {
for my $b_interface (@interfaces) {
for (1 .. $iterations) {
run_test([$a_interface, $b_interface], $a_domain, $b_domain);
}
}
}
}
}
}
sub run_test {
my ($directions, $a_domain, $b_domain) = @_;
print("Testing directions @{$directions} between $a_domain and $b_domain\n");
my ($a, $b) = $r->client_pair(
{sockdomain => $a_domain},
{sockdomain => $b_domain}
);
print("Offering with address: " . $a->{sockets}->[0]->[0]->sockhost . "\n");
my %dir_arg = ();
$dir_arg{direction} = $directions if @{$directions};
$a->offer($b, ICE => 'remove', label => "caller", %dir_arg);
print("Offer out with address: " . $b->{remote_media}->connection->{address} . "\n");
print("Answering with address: " . $b->{sockets}->[0]->[0]->sockhost . "\n");
$b->answer($a, ICE => 'remove', label => "callee");
print("Answer out with address: " . $a->{remote_media}->connection->{address} . "\n");
$a->teardown();
print("\n");
}

@ -4,7 +4,7 @@ use warnings;
use strict;
use Socket;
$| = 1;
STDOUT->autoflush(1);
@ -28,11 +28,9 @@ sub mp_msg {
my $fd;
socket($fd, AF_INET, SOCK_STREAM, 0) or die;
connect($fd, sockaddr_in(25060, inet_aton('127.0.0.1'))) or die;
my $old = select($fd);
$| = 1;
print("$cmd\n");
$fd->autoflush(1);
print { $fd } ("$cmd\n");
my $ret = <$fd>;
select($old);
close($fd);
chomp($ret);
return $ret;
@ -53,7 +51,7 @@ sub udp_sock {
sub send_rcv {
my ($sendfd, $sendtoip, $sendtoport, $recvfd) = @_;
print("sending to $sendtoip:$sendtoport... ");
my $pkt = join('',map(rand,1..10));
my $pkt = join('', map { rand } 1..10);
send($sendfd, $pkt, 0, sockaddr_in($sendtoport, inet_aton($sendtoip))) or die;
my $inc;
{
@ -64,7 +62,10 @@ sub send_rcv {
recv($recvfd, $inc, length($pkt), 0);
alarm(0);
}
$inc eq $pkt or print("NOT received packed ok\n"), return;
if ($inc ne $pkt) {
print("NOT received packed ok\n");
return;
}
print("received packet ok\n");
}
@ -99,12 +100,12 @@ sub sim_lk {
my $callid1 = join('',map(rand,1..2));
my $fromtag1 = join('',map(rand,1..4));
my $totag1 = join('',map(rand,1..4));
my $callid2 = join('',map(rand,1..2));
my $fromtag2 = join('',map(rand,1..4));
my $totag2 = join('',map(rand,1..4));
my $callid1 = join('', map { rand } 1..2);
my $fromtag1 = join('', map { rand } 1..4);
my $totag1 = join('', map { rand } 1..4);
my $callid2 = join('', map { rand } 1..2);
my $fromtag2 = join('', map { rand } 1..4);
my $totag2 = join('', map { rand } 1..4);
my ($client1, $lp1) = udp_sock();
my ($client2, $lp2) = udp_sock();

@ -10,23 +10,24 @@ my $t = $ARGV[0] || "0";
my $format = 'SS ia16SS ia16SS ia16SS CCCC LLLLLL';
my $len = length(pack($format, (0) x 100));
open(X, "<", "/proc/rtpengine/$t/blist") or die;
open(my $fh, "<", "/proc/rtpengine/$t/blist") or die;
my $buf;
while (sysread(X, $buf, $len)) {
my @b = unpack($format, $buf);
while (sysread($fh, $buf, $len)) {
my @buf = unpack($format, $buf);
for (2,6,10) {
if ($b[$_] == AF_INET) {
$b[$_ + 1] = inet_ntoa($b[$_ + 1]);
if ($buf[$_] == AF_INET) {
$buf[$_ + 1] = inet_ntoa($buf[$_ + 1]);
}
elsif ($b[$_] == AF_INET6) {
$b[$_ + 1] = inet_ntop(AF_INET6, $b[$_ + 1]);
elsif ($buf[$_] == AF_INET6) {
$buf[$_ + 1] = inet_ntop(AF_INET6, $buf[$_ + 1]);
}
elsif ($b[$_] == 0) {
$b[$_ + 1] = '---';
elsif ($buf[$_] == 0) {
$buf[$_ + 1] = '---';
}
}
for (18, 20, 22) {
$b[$_] += $b[$_ + 1] * 2**32;
$buf[$_] += $buf[$_ + 1] * 2**32;
}
printf("%5u %15s:%-5u -> %15s:%-5u (-> %15s:%-5u) [%u] [%llu %llu %llu]\n", @b[0,3,4,7,8,11,12,14,18,20,22]);
printf("%5u %15s:%-5u -> %15s:%-5u (-> %15s:%-5u) [%u] [%llu %llu %llu]\n", @buf[0,3,4,7,8,11,12,14,18,20,22]);
}
close($fh);

@ -8,14 +8,10 @@ use Socket6;
my %cmds = (noop => 1, add => 2, delete => 3, update => 4, add_call => 5, del_call => 6, add_stream => 7, del_stream => 8, packet => 9);
my %ciphers = ('null' => 1, 'aes-cm' => 2, 'aes-f8' => 3);
my %hmacs = ('null' => 1, 'hmac-sha1' => 2);
$| = 1;
STDOUT->autoflush(1);
open(F, "+> /proc/rtpengine/0/control") or die;
{
my $x = select(F);
$| = 1;
select($x);
}
open(my $fh, '+>', '/proc/rtpengine/0/control') or die;
$fh->autoflush(1);
sub re_address {
my ($fam, $addr, $port) = @_;
@ -38,10 +34,14 @@ sub re_address {
}
sub re_srtp {
my ($h) = @_;
no warnings;
return pack('VV a16 a16 a256 Q VV', $ciphers{$$h{cipher}}, $hmacs{$$h{hmac}},
@$h{qw(master_key master_salt mki last_index auth_tag_len mki_len)});
use warnings;
my %opts = %{$h};
# Explicitly initialize the hash entries.
$opts{$_} //= q{} foreach (qw(master_key master_salt mki));
$opts{$_} //= 0 foreach (qw(last_index auth_tag_len mki_len));
return pack('VV a16 a16 a256 Q VV', $ciphers{$opts{cipher}}, $hmacs{$opts{hmac}},
@opts{qw(master_key master_salt mki last_index auth_tag_len mki_len)});
}
sub rtpengine_message {
my ($cmd, %args) = @_;
@ -140,47 +140,47 @@ my $ret;
my $msg;
# print("add 9876 -> 1234/6543\n");
# $ret = syswrite(F, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# $ret = syswrite($fh, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# print("ret = $ret, code = $!\n");
# sleep($sleep);
# print("add fail\n");
# $ret = syswrite(F, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, mirror_addr => \@dst, mirror_port => 6789, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# $ret = syswrite($fh, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, mirror_addr => \@dst, mirror_port => 6789, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# print("ret = $ret, code = $!\n");
# sleep($sleep);
# print("update 9876 -> 1234/6543 & 6789\n");
# $ret = syswrite(F, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, mirror_addr => \@dst, mirror_port => 6789, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# $ret = syswrite($fh, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, mirror_addr => \@dst, mirror_port => 6789, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# print("ret = $ret, code = $!\n");
# sleep($sleep);
# print("update 9876 -> 2345/7890 & 4321\n");
# $ret = syswrite(F, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 2345, dst_addr => \@dst, dst_port => 7890, mirror_addr => \@dst, mirror_port => 4321, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# $ret = syswrite($fh, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 2345, dst_addr => \@dst, dst_port => 7890, mirror_addr => \@dst, mirror_port => 4321, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# print("ret = $ret, code = $!\n");
# sleep($sleep);
# print("add fail\n");
# $ret = syswrite(F, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, mirror_addr => \@dst, mirror_port => 6789, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# $ret = syswrite($fh, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, mirror_addr => \@dst, mirror_port => 6789, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# print("ret = $ret, code = $!\n");
# sleep($sleep);
# print("update 9876 -> 1234/6543\n");
# $ret = syswrite(F, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# $ret = syswrite($fh, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# print("ret = $ret, code = $!\n");
# sleep($sleep);
# print("delete\n");
# $ret = syswrite(F, rtpengine_message('delete', local_addr => \@local, local_port => 9876, decrypt => $dec, encrypt => $enc)) // '-';
# $ret = syswrite($fh, rtpengine_message('delete', local_addr => \@local, local_port => 9876, decrypt => $dec, encrypt => $enc)) // '-';
# print("ret = $ret, code = $!\n");
# sleep($sleep);
# print("delete fail\n");
# $ret = syswrite(F, rtpengine_message('delete', local_addr => \@local, local_port => 9876, decrypt => $dec, encrypt => $enc)) // '-';
# $ret = syswrite($fh, rtpengine_message('delete', local_addr => \@local, local_port => 9876, decrypt => $dec, encrypt => $enc)) // '-';
# print("ret = $ret, code = $!\n");
# sleep($sleep);
# print("update fail\n");
# $ret = syswrite(F, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# $ret = syswrite($fh, rtpengine_message('update', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc)) // '-';
# print("ret = $ret, code = $!\n");
# sleep($sleep);
@ -215,7 +215,7 @@ if (0) {
print("creating call $name\n");
$msg = rtpengine_message_call('add_call', 0, $name);
$ret = sysread(F, $msg, length($msg)) // '-';
$ret = sysread($fh, $msg, length($msg)) // '-';
#print("reply: " . unpack("H*", $msg) . "\n");
print("ret = $ret, code = $!\n");
@ -232,7 +232,7 @@ if (0) {
print("creating stream $name under call idx $call\n");
$msg = rtpengine_message_stream('add_stream', $call, 0, $name);
$ret = sysread(F, $msg, length($msg)) // '-';
$ret = sysread($fh, $msg, length($msg)) // '-';
#print("reply: " . unpack("H*", $msg) . "\n");
print("ret = $ret, code = $!\n");
@ -249,7 +249,7 @@ if (0) {
print("deleting call idx $call\n");
$msg = rtpengine_message_call('del_call', $call);
$ret = syswrite(F, $msg) // '-';
$ret = syswrite($fh, $msg) // '-';
#print("ret = $ret, code = $!, reply: " . unpack("H*", $msg) . "\n");
print("ret = $ret, code = $!\n");
@ -278,7 +278,7 @@ if (0) {
print("deleting stream idx $stream->[1] (call $stream->[0])\n");
$msg = rtpengine_message_stream('del_stream', $stream->[0], $stream->[1]);
$ret = syswrite(F, $msg) // '-';
$ret = syswrite($fh, $msg) // '-';
#print("ret = $ret, code = $!, reply: " . unpack("H*", $msg) . "\n");
print("ret = $ret, code = $!\n");
@ -295,7 +295,7 @@ if (0) {
print("delivering a packet to $idx\n");
$msg = rtpengine_message_packet('packet', 0, $idx, 'packet data bla bla ' . rand() . "\n");
$ret = syswrite(F, $msg) // '-';
$ret = syswrite($fh, $msg) // '-';
print("ret = $ret, code = $!\n");
sleep($sleep);
@ -316,7 +316,7 @@ if (0) {
print("creating call\n");
$msg = rtpengine_message_call('add_call', 0, 'test call');
$ret = sysread(F, $msg, length($msg)) // '-';
$ret = sysread($fh, $msg, length($msg)) // '-';
#print("reply: " . unpack("H*", $msg) . "\n");
print("ret = $ret, code = $!\n");
@ -330,7 +330,7 @@ sleep($sleep);
# print("creating identical call\n");
#
# $msg = rtpengine_message_call('add_call', 0, 'test call');
# $ret = sysread(F, $msg, length($msg)) // '-';
# $ret = sysread($fh, $msg, length($msg)) // '-';
# #print("reply: " . unpack("H*", $msg) . "\n");
# print("ret = $ret, code = $!\n");
#
@ -344,7 +344,7 @@ sleep($sleep);
# print("creating other call\n");
#
# $msg = rtpengine_message_call('add_call', 0, 'another test call');
# $ret = sysread(F, $msg, length($msg)) // '-';
# $ret = sysread($fh, $msg, length($msg)) // '-';
# #print("reply: " . unpack("H*", $msg) . "\n");
# print("ret = $ret, code = $!\n");
#
@ -359,7 +359,7 @@ for my $exp (0 .. 1000) {
print("creating a stream\n");
$msg = rtpengine_message_stream('add_stream', $idx1, 0, 'test stream ' . rand());
$ret = sysread(F, $msg, length($msg)) // '-';
$ret = sysread($fh, $msg, length($msg)) // '-';
#print("reply: " . unpack("H*", $msg) . "\n");
print("ret = $ret, code = $!\n");
@ -373,7 +373,7 @@ for my $exp (0 .. 1000) {
# print("creating a stream\n");
#
# $msg = rtpengine_message_stream('add_stream', $idx1, 0, 'test stream');
# $ret = sysread(F, $msg, length($msg)) // '-';
# $ret = sysread($fh, $msg, length($msg)) // '-';
# #print("reply: " . unpack("H*", $msg) . "\n");
# print("ret = $ret, code = $!\n");
#
@ -387,7 +387,7 @@ for my $exp (0 .. 1000) {
# print("creating identical stream\n");
#
# $msg = rtpengine_message_stream('add_stream', $idx1, 0, 'test stream');
# $ret = sysread(F, $msg, length($msg)) // '-';
# $ret = sysread($fh, $msg, length($msg)) // '-';
# #print("reply: " . unpack("H*", $msg) . "\n");
# print("ret = $ret, code = $!\n");
#
@ -401,7 +401,7 @@ for my $exp (0 .. 1000) {
# print("creating different stream\n");
#
# $msg = rtpengine_message_stream('add_stream', $idx3, 0, 'test stream');
# $ret = sysread(F, $msg, length($msg)) // '-';
# $ret = sysread($fh, $msg, length($msg)) // '-';
# #print("reply: " . unpack("H*", $msg) . "\n");
# print("ret = $ret, code = $!\n");
#
@ -413,7 +413,7 @@ for my $exp (0 .. 1000) {
# print("add 9876 -> 1234/6543\n");
# $ret = syswrite(F, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc, stream_idx => $sidx1, flags => 0x20)) // '-';
# $ret = syswrite($fh, rtpengine_message('add', local_addr => \@local, local_port => 9876, src_addr => \@src, src_port => 1234, dst_addr => \@dst, dst_port => 6543, tos => 184, decrypt => $dec, encrypt => $enc, stream_idx => $sidx1, flags => 0x20)) // '-';
# print("ret = $ret, code = $!\n");
# sleep($sleep);
@ -423,7 +423,7 @@ for my $exp (0 .. 1000) {
# print("delivering a packet\n");
#
# $msg = rtpengine_message_packet('packet', $idx1, $sidx1, 'packet data bla bla ' . rand() . "\n");
# $ret = syswrite(F, $msg) // '-';
# $ret = syswrite($fh, $msg) // '-';
# #print("reply: " . unpack("H*", $msg) . "\n");
# print("ret = $ret, code = $!\n");
#
@ -436,7 +436,7 @@ for my $exp (0 .. 1000) {
# print("deleting stream\n");
#
# $msg = rtpengine_message_stream('del_stream', $idx1, $sidx1, '');
# $ret = syswrite(F, $msg) // '-';
# $ret = syswrite($fh, $msg) // '-';
# #print("ret = $ret, code = $!, reply: " . unpack("H*", $msg) . "\n");
# print("ret = $ret, code = $!\n");
#
@ -447,7 +447,7 @@ for my $exp (0 .. 1000) {
# print("deleting call\n");
#
# $msg = rtpengine_message_call('del_call', $idx1, '');
# $ret = syswrite(F, $msg) // '-';
# $ret = syswrite($fh, $msg) // '-';
# #print("ret = $ret, code = $!, reply: " . unpack("H*", $msg) . "\n");
# print("ret = $ret, code = $!\n");
#
@ -456,4 +456,4 @@ for my $exp (0 .. 1000) {
close(F);
close($fh);

@ -4,7 +4,7 @@ use warnings;
use strict;
use Socket;
$| = 1;
STDOUT->autoflush(1);
@ -28,11 +28,9 @@ sub mp_msg {
my $fd;
socket($fd, AF_INET, SOCK_STREAM, 0) or die;
connect($fd, sockaddr_in(25060, inet_aton('127.0.0.1'))) or die;
my $old = select($fd);
$| = 1;
print("$cmd\n");
$fd->autoflush(1);
print { $fd } ("$cmd\n");
my $ret = <$fd>;
select($old);
close($fd);
chomp($ret);
return $ret;
@ -55,7 +53,7 @@ sub send_rcv {
my $laddr = getsockname($sendfd);
my ($lport, $lip) = sockaddr_in($laddr);
print("local port $lport sending to $sendtoip:$sendtoport... ");
my $pkt = join('',map(rand,1..10));
my $pkt = join('', map { rand } 1..10);
send($sendfd, $pkt, 0, sockaddr_in($sendtoport, inet_aton($sendtoip))) or die;
my ($inc, $addr);
{
@ -67,7 +65,10 @@ sub send_rcv {
$addr = recv($recvfd, $inc, length($pkt), 0);
alarm(0);
}
$inc eq $pkt or print("did NOT receive packet\n"), return;
if ($inc ne $pkt) {
print("did NOT receive packet\n");
return;
}
my ($port, $ip) = sockaddr_in($addr);
$laddr = getsockname($recvfd);
($lport, $lip) = sockaddr_in($laddr);
@ -110,9 +111,9 @@ sub sim_lk {
my $callid = join('',map(rand,1..2));
my $fromtag = join('',map(rand,1..4));
my $totag = join('',map(rand,1..4));
my $callid = join('', map { rand } 1..2);
my $fromtag = join('', map { rand } 1..4);
my $totag = join('', map { rand } 1..4);
my ($client1, $lp1) = udp_sock();
my ($client2, $lp2) = udp_sock();

@ -46,11 +46,11 @@ GetOptions(
($IP || $IPV6) or die("at least one of --local-ip or --local-ipv6 must be given");
$SIG{ALRM} = sub { print "alarm!\n"; };
local $SIG{ALRM} = sub { print "alarm!\n"; };
setrlimit(RLIMIT_NOFILE, 8000, 8000);
$PROTOS and $PROTOS = [split(/\s*[,;:]+\s*/, $PROTOS)];
$PROTOS && @$PROTOS == 1 and $$PROTOS[1] = $$PROTOS[0];
$$PROTOS[1] = $$PROTOS[0] if $PROTOS && @$PROTOS == 1;
$DEST and $DEST = [$DEST =~ /^(?:([a-z.-]+)(?::(\d+))?|([\d.]+)(?::(\d+))?|([\da-f:]+)|\[([\da-f:]+)\]:(\d+))$/si];
my $dest_host = $$DEST[0] || $$DEST[2] || $$DEST[4] || $$DEST[5] || 'localhost';
my $dest_port = $$DEST[1] || $$DEST[3] || $$DEST[6] || 2223;
@ -86,8 +86,14 @@ sub msg {
my @dests = getaddrinfo($dest_host, $dest_port, AF_UNSPEC, SOCK_DGRAM);
while (@dests >= 5) {
my ($fam, $type, $prot, $addr, $canon, @dests) = @dests;
socket($fd, $fam, $type, $prot) or undef($fd), next;
connect($fd, $addr) or undef($fd), next;
if (!socket($fd, $fam, $type, $prot)) {
undef($fd);
next;
}
if (!connect($fd, $addr)) {
undef($fd);
next;
}
last;
}
$fd or die($!);
@ -106,7 +112,7 @@ sub send_receive {
alarm(1);
recv($receive_fd, $x, 0xffff, 0) or $err = "$!";
alarm(0);
$err && $err !~ /interrupt/i and die $err;
die $err if $err && $err !~ /interrupt/i;
return $x;
}
@ -334,24 +340,24 @@ sub rtp_savp {
sub savp_crypto {
my ($sdp, $ctx, $ctx_o) = @_;
my @a = $sdp =~ /[\r\n]a=crypto:(\d+) (\w+) inline:([\w\/+=]{40,})(?:\|(?:2\^(\d+)|(\d+)))?(?:\|(\d+):(\d+))?(?: (.*?))?[\r\n]/sig;
@a or die;
my @aa = $sdp =~ /[\r\n]a=crypto:(\d+) (\w+) inline:([\w\/+=]{40,})(?:\|(?:2\^(\d+)|(\d+)))?(?:\|(\d+):(\d+))?(?: (.*?))?[\r\n]/sig;
@aa or die;
my $i = 0;
while (@a >= 8) {
$$ctx[$i]{in}{crypto_suite} = $NGCP::Rtpclient::SRTP::crypto_suites{$a[1]} or die;
$$ctx[$i]{in}{crypto_tag} = $a[0];
while (@aa >= 8) {
$$ctx[$i]{in}{crypto_suite} = $NGCP::Rtpclient::SRTP::crypto_suites{$aa[1]} or die;
$$ctx[$i]{in}{crypto_tag} = $aa[0];
($$ctx[$i]{in}{rtp_master_key}, $$ctx[$i]{in}{rtp_master_salt})
= NGCP::Rtpclient::SRTP::decode_inline_base64($a[2], $$ctx[$i]{in}{crypto_suite});
$$ctx[$i]{in}{rtp_mki} = $a[5];
$$ctx[$i]{in}{rtp_mki_len} = $a[6];
= NGCP::Rtpclient::SRTP::decode_inline_base64($aa[2], $$ctx[$i]{in}{crypto_suite});
$$ctx[$i]{in}{rtp_mki} = $aa[5];
$$ctx[$i]{in}{rtp_mki_len} = $aa[6];
undef($$ctx[$i]{in}{rtp_session_key});
undef($$ctx[$i]{in}{rtcp_session_key});
($a[7] || '') =~ /UNENCRYPTED_SRTP/ and $$ctx[$i]{in}{unenc_srtp} = 1;
($a[7] || '') =~ /UNENCRYPTED_SRTCP/ and $$ctx[$i]{in}{unenc_srtcp} = 1;
($a[7] || '') =~ /UNAUTHENTICATED_SRTP/ and $$ctx[$i]{in}{unauth_srtp} = 1;
($aa[7] || '') =~ /UNENCRYPTED_SRTP/ and $$ctx[$i]{in}{unenc_srtp} = 1;
($aa[7] || '') =~ /UNENCRYPTED_SRTCP/ and $$ctx[$i]{in}{unenc_srtcp} = 1;
($aa[7] || '') =~ /UNAUTHENTICATED_SRTP/ and $$ctx[$i]{in}{unauth_srtp} = 1;
$i++;
@a = @a[8 .. $#a];
@aa = @aa[8 .. $#aa];
}
}
@ -403,7 +409,7 @@ sub do_rtp {
$KEEPGOING or undef($c);
}
$NOENC and $repl = $expect;
!$repl && $KEEPGOING and next;
next if !$repl && $KEEPGOING;
$repl eq $expect or die hexdump($repl, $expect) . " $$trans{name} > $$trans_o{name}, $$c{callid}, RTP port $$outputs[$j][0]";
$rtcp or next;
@ -421,7 +427,7 @@ sub do_rtp {
$dst = $$pr{sockaddr}($dstport, $addr);
$repl = send_receive($sendfd, $expfd, $payload, $dst);
$NOENC and $repl = $expect;
!$repl && $KEEPGOING and next;
next if !$repl && $KEEPGOING;
$repl eq $expect or die hexdump($repl, $expect) . " $$trans{name} > $$trans_o{name}, $$c{callid}, RTCP";
}
}
@ -606,20 +612,20 @@ sub offer_answer {
my $tcx = $$A{trans_contexts};
my $tcx_o = $$B{trans_contexts};
my $sdp = <<"!";
my $sdp = <<"SDP";
v=0
o=blah 123 123 IN $$pr{family_str} $$ips_t[0]
s=session
c=IN $$pr{family_str} $$ips_t[0]
t=0 0
!
SDP
my $ul = $$A{num_streams};
$op eq 'answer' && $$A{streams_seen} < $$A{num_streams}
and $ul = $$A{streams_seen};
$ul = $$A{streams_seen} if $op eq 'answer' && $$A{streams_seen} < $$A{num_streams};
$$A{want_bundle} && $op eq 'offer' and
$$A{bundle} = 1,
if ($$A{want_bundle} && $op eq 'offer') {
$$A{bundle} = 1;
$sdp .= "a=group:BUNDLE " . join(' ', (0 .. $ul)) . "\n";
}
for my $i (0 .. $ul) {
my $bi = $i;
@ -628,14 +634,13 @@ t=0 0
my $p = $$ports_t[$bi];
my $cp = $p + 1;
$$A{bundle} && $$A{want_rtcpmux} && $op eq 'offer'
and $cp = $p;
$cp = $p if $$A{bundle} && $$A{want_rtcpmux} && $op eq 'offer';
$sdp .= <<"!";
$sdp .= <<"SDP";
m=audio $p $$tr{name} 0 8 111
a=rtpmap:8 PCMA/8000
a=rtpmap:111 opus/48000/2
!
SDP
if ($$A{want_rtcpmux} && $op eq 'offer') {
$sdp .= "a=rtcp-mux\n";
$sdp .= "a=rtcp:$cp\n";
@ -676,7 +681,7 @@ a=rtpmap:111 opus/48000/2
rand() > .5 and $$dict{'to-tag'} = $$B{tag};
}
elsif ($op eq 'answer') {
$dict->{'from-tag'} = $$B{tag},
$dict->{'from-tag'} = $$B{tag};
$dict->{'to-tag'} = $$A{tag};
}
if (!$LAZY
@ -749,7 +754,10 @@ sub answer {
}
for my $iter (1 .. $NUM) {
($iter % 10 == 0) and print("$iter calls established\n"), do_rtp();
if ($iter % 10 == 0) {
print("$iter calls established\n");
do_rtp();
}
my $c = {};
offer($c, 0, 1);
@ -792,7 +800,7 @@ while (time() < $end) {
do_rtp($rtcp);
@calls = sort {rand() < .5} grep(defined, @calls);
@calls = sort { rand() < .5 } grep { defined } @calls;
if ($REINVITES && $now >= $last_reinv + 15) {
$last_reinv = $now;

@ -9,25 +9,25 @@ pipe() {
pipe_o > /dev/null
}
ip() {
echo $(($RANDOM % 254 + 1)).$(($RANDOM % 254 + 1)).$(($RANDOM % 254 + 1)).$(($RANDOM % 254 + 1))
echo $((RANDOM % 254 + 1)).$((RANDOM % 254 + 1)).$((RANDOM % 254 + 1)).$((RANDOM % 254 + 1))
}
port() {
echo $(($RANDOM % 64000 + 1024))
echo $((RANDOM % 64000 + 1024))
}
ids=""
ports=""
for i in $(seq 1 1000); do
callid=`uuid`
for (( i = 0 ; i < 1000 ; i++ )); do
callid=$(uuid)
test -z "$callid" && exit 1
src=`ip`:`port`
dst=`ip`:`port`
gw=`ip`
fromtag=`uuid`
totag=`uuid`
src=$(ip):$(port)
dst=$(ip):$(port)
gw=$(ip)
fromtag=$(uuid)
totag=$(uuid)
src_rel=`echo "request $callid $src:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe_o`
dst_rel=`echo "lookup $callid $dst:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:$totag,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe_o`
src_rel=$(echo "request $callid $src:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe_o)
dst_rel=$(echo "lookup $callid $dst:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:$totag,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe_o)
echo "lookup $callid $dst:audio $gw voip.inode.at local unknown unknown unknown-agent info=domain:voip.sipwise.local,from:number@voip.inode.at,totag:$totag,to:othernumber@voip.inode.at,fromtag:$fromtag" | pipe
echo version | pipe
(echo status | pipe) &
@ -36,7 +36,7 @@ for i in $(seq 1 1000); do
dst_path=${dst_rel/ //}
ports="$ports $src_path $dst_path"
for port in $ports; do
echo foobar > /dev/udp/$port
echo foobar > "/dev/udp/$port"
done
ids="$ids $callid"

@ -23,7 +23,7 @@ GetOptions(
($IP || $IPV6) or die("at least one of --local-ip or --local-ipv6 must be given");
$SIG{ALRM} = sub { print "alarm!\n"; };
local $SIG{ALRM} = sub { print "alarm!\n"; };
setrlimit(RLIMIT_NOFILE, 8000, 8000);
my @chrs = ('a' .. 'z', 'A' .. 'Z', '0' .. '9');
@ -75,7 +75,7 @@ sub do_rtp {
alarm(1);
recv($$fds[$b], $x, 0xffff, 0) or $err = "$!";
alarm(0);
$err && $err !~ /interrupt/i and die $err;
die $err if $err && $err !~ /interrupt/i;
if (($x || '') ne $payload) {
warn("no rtp reply received, ports $$outputs[$b][0] and $$outputs[$a][0]");
$KEEPGOING or undef($c);
@ -162,7 +162,10 @@ sub update_lookup {
}
for my $iter (1 .. $NUM) {
($iter % 10 == 0) and print("$iter\n"), do_rtp();
if ($iter % 10 == 0) {
print("$iter\n");
do_rtp();
}
my $c = [];
update_lookup($c, 0);
@ -175,7 +178,7 @@ while (time() < $end) {
sleep(1);
do_rtp();
@calls = sort {rand() < .5} grep(defined, @calls);
@calls = sort { rand() < .5 } grep { defined } @calls;
if ($REINVITES) {
my $c = $calls[rand(@calls)];
@ -200,7 +203,7 @@ if (!$NODEL) {
for my $c (@calls) {
$c or next;
my ($tags, $callid) = @$c[3,5];
$BRANCHES && rand() < .3 and $callid =~ s/;.*//;
$callid =~ s/;.*// if $BRANCHES && rand() < .3;
msg("D $callid $$tags[0] $$tags[1]");
}
}

@ -13,8 +13,14 @@ my $fd;
my @dests = getaddrinfo($ip, $port, AF_UNSPEC, SOCK_DGRAM);
while (@dests >= 5) {
my ($fam, $type, $prot, $addr, $canon, @dests) = @dests;
socket($fd, $fam, $type, $prot) or undef($fd), next;
connect($fd, $addr) or undef($fd), next;
if (!socket($fd, $fam, $type, $prot)) {
undef($fd);
next;
}
if (!connect($fd, $addr)) {
undef($fd);
next;
}
last;
}
$fd or die($!);

@ -54,14 +54,29 @@ while (1) {
}
next;
}
$cmd == 1 or print("not stun request\n"), next;
length($attrs) == $len or print("length mismatch\n"), next;
if ($cmd != 1) {
print("not stun request\n")
next;
}
if (length($attrs) != $len) {
print("length mismatch\n")
next;
}
my ($list, $hash) = unpack_attrs($attrs);
$$list[$#$list]{name} eq 'fingerprint' or print("last attr not fingerprint\n"), next;
$$list[$#$list-1]{name} eq 'message-integrity' or print("last but one attr not MI\n"), next;
$$hash{username} or print("no username\n"), next;
if ($$list[$#$list]{name} ne 'fingerprint') {
print("last attr not fingerprint\n");
next;
}
if ($$list[$#$list-1]{name} ne 'message-integrity') {
print("last but one attr not MI\n")
next;
}
unless ($$hash{username}) {
print("no username\n")
next;
}
$$hash{controlling} and print("is controlling\n");
$$hash{controlled} and print("is controlled\n");

@ -11,12 +11,12 @@ if ! [ -f "${CONTROL_FILE}" ]; then
exit 1
fi
BUILD_DEPS=$(/usr/bin/gdebi --quiet --non-interactive \
BUILD_DEPS=($(/usr/bin/gdebi --quiet --non-interactive \
--option=APT::Install-Recommends=false \
--apt-line ${CONTROL_FILE})
if [ -z "${BUILD_DEPS}" ]; then
--apt-line "${CONTROL_FILE}"))
if [ ${#BUILD_DEPS[@]} -eq 0 ]; then
echo "Error: no build deps packages resolved"
exit 2
fi
apt-get install -y $BUILD_DEPS
apt-get install -y "${BUILD_DEPS[@]}"

@ -187,12 +187,12 @@ sub setup {
if ($COMBINE == 0) {
$callbacks{stream_setup} = \&stream_pcap;
$callbacks{stream_close} = \&stream_pcap_close;
$callbacks{packet} = \&stream_packet,
$callbacks{packet} = \&stream_packet;
}
elsif ($COMBINE == 1) {
$callbacks{call_setup} = \&call_pcap;
$callbacks{call_close} = \&call_pcap_close;
$callbacks{packet} = \&call_packet,
$callbacks{packet} = \&call_packet;
}
}
sub cb {

@ -34,7 +34,7 @@ if ! grep -q CONFIG_NETFILTER_XT_TARGET_RTPENGINE "$KERN"/net/netfilter/Makefile
(
echo
echo "EXTRA_CFLAGS += -DRTPENGINE_VERSION=\"\\\"$4\\\"\""
echo 'obj-$(CONFIG_NETFILTER_XT_TARGET_RTPENGINE) += xt_RTPENGINE.o'
echo "obj-\$(CONFIG_NETFILTER_XT_TARGET_RTPENGINE) += xt_RTPENGINE.o"
) >> "$KERN"/net/netfilter/Makefile
fi

@ -1,8 +1,11 @@
#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::INET;
$num_args = $#ARGV + 1;
my $num_args = $#ARGV + 1;
if ( ($num_args == 0) or
(($num_args == 1) && (($ARGV[0] eq "--help") or ($ARGV[0] eq "-h"))) )
{
@ -10,9 +13,6 @@ if ( ($num_args == 0) or
exit;
}
# auto-flush on socket
$| = 1;
my $argumentstring = "";
my $ip = "127.0.0.1";
my $port = "9900";
@ -22,6 +22,9 @@ for (my $argnum=0; $argnum <= $#ARGV; $argnum++) {
die "No argument after -ip\n" unless $argnum+1<=$#ARGV;
$argnum = $argnum+1;
$ip = $ARGV[$argnum];
if ($ip =~ s/:(\d)$//) {
$port = $1;
}
} elsif ($ARGV[$argnum] eq "-port") {
die "No argument after -port\n" unless $argnum+1<=$#ARGV;
$argnum = $argnum+1;
@ -39,6 +42,8 @@ my $socket = new IO::Socket::INET (
);
die "Cannot connect to the rtpengine $!\n" unless $socket;
$socket->autoflush(1);
#set send/recv timeout so script doesn't hang when rtpengine doesn't interact
setsockopt($socket, SOL_SOCKET, SO_SNDTIMEO, pack('L!L!', 3, 0) ) or die $!;
setsockopt($socket, SOL_SOCKET, SO_RCVTIMEO, pack('L!L!', 3, 0) ) or die $!;
@ -62,11 +67,12 @@ $socket->close();
sub showusage {
print "\n";
print " rtpengine-ctl [ -ip <ipaddress> -port <port> ] <command>\n";
print " rtpengine-ctl [ -ip <ipaddress>[:<port>] -port <port> ] <command>\n";
print "\n";
print " Supported commands are:\n";
print "\n";
print " list [ numsessions | maxsessions | maxopenfiles | sessions [ <callid> | all | own | foreign ] | totals ]\n";
print " list [ numsessions | maxsessions | maxopenfiles\n";
print " | sessions [ <callid> | all | own | foreign ] | totals | loglevel ]\n";
print " numsessions : print the number of sessions\n";
print " maxsessions : print the number of allowed sessions\n";
print " maxopenfiles : print the number of allowed open files\n";
@ -76,6 +82,7 @@ sub showusage {
print " sessions foreign : print one-liner foreign sessions information\n";
print " totals : print total statistics\n";
print " timeout : print timeout parameters\n";
print " loglevel : print current log level\n";
print "\n";
print " terminate [ <callid> | all | own | foreign ]\n";
print " <callid> : session is immediately terminated\n";
@ -83,12 +90,14 @@ sub showusage {
print " own : terminates own current sessions\n";
print " foreign : terminates foreign current sessions\n";
print "\n";
print " set [ maxsessions <int> | maxopenfiles <uint> | timeout <uint> | silent_timeout <uint> | final_timeout <uint>]\n";
print " set [ maxsessions <int> | maxopenfiles <uint> | timeout <uint>\n";
print " | silent_timeout <uint> | final_timeout <uint> | loglevel <uint> ]\n";
print " maxsessions <int> : set the max nr of allowed sessions\n";
print " maxopenfiles <uint> : set the max nr of allowed open files\n";
print " timeout <uint> : set the --timeout parameter \n";
print " silenttimeout <uint> : set the --silent-timeout parameter \n";
print " finaltimeout <uint> : set the --final-timeout parameter \n";
print " loglevel <uint> : set the log level to new value (1-7)\n";
print "\n";
print " ksadd [ keyspace <uint>]\n";
print " keyspace <uint> : subscribe to 'keyspace' database\n";

@ -21,6 +21,7 @@ GetOptions(
'protocol=s' => \$options{'transport protocol'},
'trust-address' => \$options{'trust address'},
'sip-source-address' => \$options{'sip source address'},
'no-rtcp-attribute' => \$options{'no rtcp attribute'},
'symmetric' => \$options{'symmetric'},
'asymmetric' => \$options{'asymmetric'},
'replace-origin' => \$options{'replace-origin'},
@ -43,40 +44,52 @@ GetOptions(
'reset' => \$options{'reset'},
'port-latching' => \$options{'port latching'},
'media-address=s' => \$options{'media address'},
'codec-strip=s@' => \$options{'codec-strip'},
'codec-offer=s@' => \$options{'codec-offer'},
'flags=s@' => \$options{'flags'},
) or die;
my $cmd = shift(@ARGV) or die;
my %packet = (command => $cmd);
for my $x (split(',', 'from-tag,to-tag,call-id,transport protocol,media address,ICE,address family,DTLS,via-branch,media address')) {
for my $x (split(/,/, 'from-tag,to-tag,call-id,transport protocol,media address,ICE,address family,DTLS,via-branch,media address')) {
defined($options{$x}) and $packet{$x} = \$options{$x};
}
for my $x (split(',', 'TOS,delete-delay')) {
for my $x (split(/,/, 'TOS,delete-delay')) {
defined($options{$x}) and $packet{$x} = $options{$x};
}
for my $x (split(',', 'trust address,symmetric,asymmetric,force,strict source,media handover,sip source address,reset,port latching')) {
for my $x (split(/,/, 'trust address,symmetric,asymmetric,force,strict source,media handover,sip source address,reset,port latching,no rtcp attribute')) {
defined($options{$x}) and push(@{$packet{flags}}, $x);
}
for my $x (split(',', 'origin,session connection')) {
for my $x (split(/,/, 'origin,session connection')) {
defined($options{'replace-' . $x}) and push(@{$packet{replace}}, $x);
}
for my $x (split(',', 'rtcp-mux,SDES')) {
defined($options{$x}) && ref($options{$x}) eq 'ARRAY'
and $packet{$x} = $options{$x};
for my $x (split(/,/, 'rtcp-mux,SDES')) {
$packet{$x} = $options{$x}
if defined($options{$x}) && ref($options{$x}) eq 'ARRAY';
}
if (defined($options{direction})) {
$options{direction} =~ /(.*),(.*)/ or die;
$packet{direction} = [$1,$2];
}
if ($options{'codec-strip'} && @{$options{'codec-strip'}}) {
$packet{codec}{strip} = $options{'codec-strip'};
}
if ($options{'codec-offer'} && @{$options{'codec-offer'}}) {
$packet{codec}{offer} = $options{'codec-offer'};
}
if ($options{'flags'} && @{$options{'flags'}}) {
push(@{$packet{flags}}, @{$options{'flags'}});
}
if (defined($options{sdp})) {
$packet{sdp} = $options{sdp};
}
elsif (defined($options{'sdp-file'})) {
open(F, '<', $options{'sdp-file'}) or die $!;
my @sdp = <F> or die $!;
close(F);
open(my $fh, '<', $options{'sdp-file'}) or die $!;
my @sdp = <$fh> or die $!;
close($fh);
$packet{sdp} = join('', @sdp);
}
#elsif (@ARGV && $ARGV[0] eq 'sdp') {

Loading…
Cancel
Save