Copyright © 2006-2026 Maksym Sobolyev <sobomax@sippysoft.com>
Copyright © 2019 Donat Zenichev
Copyright © 2006 Jan Janak <janakj@users.berlios.de>
30 May 2019
The RTPProxy is a high-performance software proxy for RTP streams that can work together with Sippy B2BUA, Kamailio, OpenSIPS and SER (SIP Express Router).
The latest stable version is RTPProxy 3.2
RTPProxy was originally developed by in 2003 with a purpose of VoIP calls facilitation to/from SIP User Agents located behind NAT or firewalls. That time, there were a few cases where direct end-to-end communication between users behind the NAT was not possible. RTPProxy, in common with a SIP proxy, overcomes those obstacles by acting as an intermediary point for the RTP streams.
Afterwards, the RTPProxy has become widely used by VoIP service providers that are in a demand for optimizing a traffic flow in their networks.
Later on it became apparent that there are many other possible uses of this software. It can be used in common with a signaling element (SIP Proxy or SIP B2BUA) to build complex VoIP networks, to optimize a traffic flow, to collect voice quality information and so on.
RTPProxy is now actively developed and maintained by the Sippy Software, Inc. and is available on GitHub of Sippy Labs - https://github.com/sippy/rtpproxy.
Over the years, many other individuals and organizations have contributed features and fixes into the code base, including Vonage, SipPulse, Voicenter, RateTel, either directly or by sponsoring respective development efforts (often both).
Originally it was designed for handling of NAT scenarios. It can also act as a generic media relay, as well as a gateway for RTP sessions between IPv4 and IPv6 networks.
It can perform a number of additional functions on RTP streams, including call recording, playing pre-recorded announcements, real-time stream copying and RTP payload re-framing.
The RTPProxy supports some advanced features, such as remote control mode, allowing a building of scalable distributed SIP VoIP networks. The rtpproxy modules of OpenSIPS and Kamailio (both descendants of the SIP Express Router, whose nathelper module originally provided this functionality), as well as the Sippy B2BUA, allow using multiple RTPProxy instances running on remote machines for fault-tolerance and load-balancing purposes.
An advanced high-capacity clustering and a load balancing is available through the use of RTP Cluster middleware.
The software also supports MOH/pre-recorded prompts injection, video relaying and RTP session recording to a local file or remote UDP listener(s).
Firstly, when a SIP proxy receives an INVITE request, it extracts a call-id from it and hands it to the proxy via the control socket. Then, in its turn, a proxy looks for an existing session with the same id. If the session exists, it returns a UDP port for that session. In case it doesn't exist, it creates a new session and binds it to a free UDP port, randomly selected from the range specified at the start time, and returns the number of that port to a SIP proxy called it. Upon receiving a reply from the proxy, SIP Proxy (Sippy B2BUA, OpenSIPS or Kamailio) in its turn replaces media IP:port in the SDP body, in order to point to the media proxy and then it forwards a request as usually.
When it comes to receiving of non-negative SIP reply with the SDP body, then SIP proxy again extracts call-id from it and hands it to the rtpproxy. In this case the rtpproxy does not allocate a new session, if it doesn't exist. But simply performs a lookup among existing sessions and returns either a port number if the session is found, or an error code indicating that there is no session with such id. Upon receiving a positive reply from the rtpproxy, SIP Proxy (Sippy B2BUA, OpenSIPS or Kamailio) replaces media IP:port in the SDP body of that SIP reply, in order to point to the rtpproxy and then forwards this reply as usually.
After the session has been created, the rtpproxy listens on the ports it has allocated for that session and relays UDP packets between the call parties. Initially, packets are sent to the addresses provided by the SIP proxy, i.e. taken from the SDP. Once a packet is received from a party, the proxy "latches" to the source ip:port of that packet and uses it as the address of that party from then on, which allows reaching parties located behind NAT. Latching is not done for parties set up as asymmetric.
A good advantage of the RTPProxy is, that it tracks idle time for each existing session (the period of time within which no packets were relayed) and automatically cleans up a session, idle time of which exceeds the value specified at the command line (60 seconds by default).
The RTPProxy package by default provides the following components:
rtpproxy - main RTP proxy server binary (production build)
rtpproxy_debug - Main RTP proxy server binary (debug and profiling build)
makeann - Utility to pre-encode prompts and announcements (production build)
makeann_debug - Utility to pre-encode prompts and announcements (debug and profiling build, built but not installed)
extractaudio - Utility to convert recorded sessions into plain audio files (production build, only built if libsndfile is available)
extractaudio_debug - Utility to convert recorded sessions into plain audio files (debug and profiling build, built but not installed)
In case you want to use a pre-built RTPProxy package, then use your system's package manager to set this up:
dpkg - for Debian™ based systems (including Ubuntu™), when installing .deb packages.
apt-get / aptitude - for Debian™ based systems (including Ubuntu™), when installing from a repository.
rpm - for RPM based systems, when installing .rpm packages.
pkg - for FreeBSD™ when installing from a repository or packages.
Starting with version 3.2.0, packages for several Debian and Ubuntu releases are attached to each release on the GitHub releases page.
Official Docker images are published to Docker Hub
(sippylabs/rtpproxy) and GitHub Container Registry
(ghcr.io/sippy/rtpproxy) with every release and every
commit to the master branch.
In case you want to compile RTPProxy from sources, use the following section "Compilation from sources".
The RTPProxy package has several optional dependencies that are detected at the configure stage and an appropriate functionality is enabled.
Those dependencies and their impact on the RTPProxy build are listed below:
bcg729 - Enables G.729 support in several tools.
https://github.com/BelledonneCommunications/bcg729
Affected binaries: extractaudio makeann
libsndfile - Required to build extractaudio, which uses it to save decoded audio in a number of popular audio formats.
https://libsndfile.github.io/libsndfile/
Affected binaries: extractaudio
libg722 - Enables G.722 support in several tools.
https://github.com/sippy/libg722
Affected binaries: extractaudio makeann
libsrtp2 (preferred) or
libsrtp - Enables decoding recordings of SRTP
streams. Together with OpenSSL, also enables
building the dtls_gw and
ice_lite modules.
https://github.com/cisco/libsrtp.git
Affected binaries:
extractaudio,
dtls_gw, ice_lite
libgsm - Enables GSM 06.10 support in several tools.
Affected binaries: extractaudio makeann
libsystemd-dev - allows for a tighter systemd integration.[1]
Affected binaries: rtpproxy
libelperiodic - Required by several internal tools that are used by an automated testing framework. If it is not installed on the system, the copy bundled as a git submodule is built and used instead.
https://github.com/sobomax/libelperiodic.git
Affected tools: rtpp_notify_client.py rtpp_query.py getmonotime.py
tcpdump - Test framework requires it to capture traffic and verify PCAP decoding features. make check will fail if it's not installed.
sudo - Test framework requires it to run the tcpdump at elevated privileges. make check will fail if it's not installed.
Do not forget about your compiler! To build this version of the RTPProxy you would need either:
GCC compiler, version 11 or higher, or
Clang compiler, version 11 or higher
Those are the oldest versions tested by the continuous integration.
Clone the source code (into a directory where you want to store compilation sources, usually it's /usr/src/). The master branch contains the latest development version, use a release tag (e.g. v3.2.0) instead to build a stable release:
cd /usr/src
git clone -b master https://github.com/sippy/rtpproxy.gitMake sure you bring in the RTPProxy git submodules:
git -C rtpproxy submodule update --init --recursive
Now compile and install it using the following commands:
A sequence for users:
cd rtpproxy
./configure
make clean all
make installDevelopers might also want to include make check in the sequence. Make sure you have libelperiodic, sudo and tcpdump installed for that step to complete:
cd rtpproxy
./configure
make clean all
make check
make installAfter a successful compilation, you have the following binaries
Main RTP proxy server binary - production build.
After the compilation is done, you have a binary file stored in the /usr/local/bin/ folder. You can test this out, checking the version of the rtpproxy: /usr/local/bin/rtpproxy -V
Main RTP proxy server binary - debug and profiling build.
You will also have a second binary file for debug and profiling: /usr/local/bin/rtpproxy_debug
A good way to maintain your RTPProxy is to start it using a certain Service Manager, such as systemd.
systemd is the default service manager on most Linux distributions:
Ubuntu 15.04 is the first version (of Ubuntu) that uses systemd by default;
Debian 8 (Jessie) is the first version (of Debian) that uses systemd by default;
It has been the default system and service manager in Red Hat Enterprise Linux and CentOS since version 7, and in Fedora since Fedora 15;
There is a huge list of other distributions, that use the systemd as a System Manager by default now as well (OpenSUSE, Mint, Mageia etc.) .
In case you have a system, that doesn't support the systemd, you can use any other Service Managers as you want to (e.g. init.d start scripts in Debian systems older than 8th one).
That said, systemd is the recommended solution, because it provides the following capabilities:
Define a list of options in a separate environment file, and use those options as a variable in the start row 'ExecStart='
You're able to react on a failure of an application using a special parameter 'OnFailure=' (and to send notification for e.g.)
You can specify a list of pre-actions to be performed, before systemd launches binary file 'ExecStartPre=' (e.g. creation of /var/run/rtpproxy directory and adding user/group rights to it)
As well as you can add pre-start actions, you can perform post-stop actions for cases when application stops running 'ExecStopPost=' (e.g. deletion of the /var/run/rtpproxy directory)
You can set a policy for cases when application stops improperly (not a manual stop of the application), e.g. restarting always with 'Restart=always'
You may also want to add a time delay between restart attempts with 'RestartSec=5'
You can deal with different things related to logging or syslogging (e.g. changing of a name identifier when writing to a syslog 'SyslogIdentifier=' or specifying a different logfacility level with 'StandardOutput=' and 'StandardError=' )
User/Group to be used for running an application
and many many other things..
The link to an instance of the systemd service file (for the RTPProxy): systemd configuration file instance -> Instance of the systemd service file
After you downloaded your systemd service file, adjust OPTIONS parameters.
Set your own IP address for listening, or leave it with 0.0.0.0 (manual way)
-l 0.0.0.0
Set upper bound on number of file descriptors the rtpproxy service instance might be using. Rule of thumb is that 4 file descriptors are needed for a fully established session (two RTP endpoints and two RTCP ones).
Make the value reasonably high to match your load expectations in terms of number of concurrent sessions but not excessively high, as it may cause rtpproxy to fail initialization because operating system usually also enforces upper bound on this value that varies from system to system:
-L 4096
You may also want to change a port range for opening UDP ports:
-m <min number> -M <max number> e.g.: -m 10000 -M 40000
Change your log facility level if you want to:
-d INFO:<LOG_LOCAL_NUMBER>
e.g.: -d INFO:LOG_LOCAL5
-d DBUG:LOG_LOCAL5Another great reason to use systemd, is a possibility of systemd to create control sockets for rtpproxy.
The benefit of this is that the control socket stays in place while rtpproxy is down for some reason, e.g. being restarted. Clients (such as OpenSIPS or Kamailio) connecting to it during that time are not refused: their connections are queued and get served once rtpproxy is back (they can still get timeout errors if that takes too long). Connections that were already open when rtpproxy went down are lost.
To let this be working, follow this guide: .
Other than that, for better view of how to configure your startup line, you can use this .
After you're done with customizing of your systemd service file for RTPProxy, follow this sequence to finish it:
copy it to the /etc/systemd/system/ directory
reload systemd configuration with:
bash> sudo systemctl daemon-reload
enable the service:
bash> sudo systemctl enable rtpproxy.service
Now you can start your RTPProxy using the commands given below
Working with a systemd, you may need to know a list of general commands to manage with your RTPProxy service, here they are:
Start of the service:
bash> sudo systemctl start rtpproxy.service
Stop of the service:
bash> sudo systemctl stop rtpproxy.service
Restart of the service:
bash> sudo systemctl restart rtpproxy.service
Check status of the service:
bash> sudo systemctl status rtpproxy.service
Show systemd configuration file of the service:
bash> sudo systemctl show rtpproxy.service
To see an output after you started your RTPProxy, or even just to keep a track on how it's working, you can use the following commands:
Look into a journal:
bash> sudo journalctl -xe -u rtpproxy.service
You can configure logging to a separate logfile, to do this, follow the manual (or you can use manual logging to a syslog file) listed in the:
You can see an output of the systemctl tool:
bash> sudo systemctl status rtpproxy.service
Interaction of a SIP proxy server with RTPProxy application is supported via
rtpproxy.sock - control socket created by the
RTPProxy
rtpproxy_timeout.sock - a socket for timeout
notifications created by the SIP proxy
Historically, primary configuration method for the RTPProxy was done via command-line parameters. Starting with version 2.2 certain advanced parameters (particularly loadable modules configuration) have been moved into configuration file.
rtpproxy [-?] [-2] [-f] [-v] [-V] [-R] [-l
addr1[/addr2]...] [-l
[label@]address[=advertized_address]...] [-6
addr1[/addr2]...] [-6
[label@]address[=advertized_address]...] [-s
ctrl_socket] [-t
tos] [-p
pidfile] [-T
max_ttl] [--maxpps
pps[,rtcp_pps]] [--maxpsize
size[,rtcp_size]] [-r rdir
[-S
sdir]] [-L
nofile_limit] [-A
advaddr1[/advaddr2]] [-m
min_port] [-M
max_port] [-u
uname[:gname]] [-w
sock_mode] [-F] [-i] [-n
timeout_socket] [-P] [-a] [-d
log_level[:log_facility]] [-W
setup_ttl] [--force_asymmetric] [-b] [-c
fifo|rr] [--nice
level] [-D] [--no_redirect] [--config
config_file] [--dso
module...] [--bridge_symmetric] [--no_resolve]
The following command-line parameters are supported:
-?Show summary of options.
-2Send every RTP packet twice in sessions that use low-bitrate codecs. Only packets that are smaller than 128 bytes will be sent twice. This option can improve audio quality on lossy links.
-frtpproxy will stay in foreground mode if this option is set.
-VShow version of program.
-vShow supported rtpp command protocol revisions.
-l
addr1[/addr2]IPv4 listen IP address(es). This option can be specified
multiple times. Bridging mode can be enabled when the first IPv4 (or
first IPv6) listen argument is specified as
addr1/addr2.
Additional labeled listen addresses use the separate format
[label@]address[=advertized_address]. The
/addr2 form is not valid for labeled
entries.
When multiple listen addresses are configured, all addresses
after the first must have unique labels. Such labels are accepted by
the l{label} argument in U and
L commands.
-6
addr1[/addr2]IPv6 listen IP address(es). This option can be specified
multiple times. Two argument formats are supported:
addr1[/addr2] and
[label@]address[=advertized_address].
-s
ctrl_socketThis parameter configures rtpproxy control socket. The control
socket is used by the call controller for the purpose of creating,
modifying, and deleting RTP sessions. The control socket can also be
used to fetch stats from the rtpproxy process, or about specific
media sessions. Format of ctrl_socket is
<type>:<socket>. Following types are supported:
udp: Create UDP control socket. In this mode rtpproxy will listen on a UDP socket for control messages from the call controlle.
Example: -s udp:127.0.0.1:9000
IP address can be '*' in which case rtpproxy will listen on all local interfaces. If port is omitted then port 22222 will be used.
rtpproxy control protocol has no built-in security mechanisms. Make sure that you protect the listening IP and port properly when using rtpproxy with UDP control socket.
udp6: Create IPv6 UDP control socket. In this mode rtpproxy will listen on UDP/IPv6 for control messages from the SIP Controller.
Example: -s udp6:::1:9000
tcp: Create IPv4 TCP control socket. In this mode rtpproxy will listen on TCP/IPv4 for control messages from the SIP Controller.
Example: -s tcp:192.168.0.1:9001
tcp6: Create IPv6 TCP control socket. In this mode rtpproxy will listen on TCP/IPv6 for control messages from the SIP Controller.
Example: -s tcp6:::1:9002
unix: Create UNIX domain socket in a datagram-like mode for control interface. In this mode the SIP Controller and rtpproxy must be running on the same host. This is traditional mode when rtpproxy would close connection after processing each command and sending reply.
Example: -s unix:/var/run/rtpproxy.sock
cunix: Create UNIX domain socket for control interface. In this mode the SIP Controller and rtpproxy must be running on the same host. Similar to unix: above, but the rtpproxy would keep control socket open and accept multiple commands via the same connection.
Example: -s cunix:/var/run/rtpproxy.sock
fd: Use socket
N, inherited from the parent
process, instead of creating one. The type of the control
socket is determined from the socket: a datagram IPv4/IPv6
socket is used as udp: /
udp6:, a listening IPv4/IPv6 or UNIX
domain stream socket as tcp: /
tcp6: / cunix:,
and a connected stream socket as a single control channel to
the SIP Controller. The rtpproxy does not change the ownership
or permissions of such a socket, and does not remove its path
on exit.
Example: -s fd:3
Default value is
unix:/var/run/rtpproxy.sock.
-t tosSet ToS (Type of Service) in the outgoing IP header. Default value is 0xB8. Setting this parameter to -1 disables setting ToS resulting in operating system default ToS being used instead.
-r rec_dirDirectory to write recorded RTP sessions.
-S spool_dirSpool directory for recording of RTP streams. When the session
is stopped, the recording will be moved from the spool directory to
the rec_dir directory as specified by the -r
option.
-RPrevent rtpproxy from recording RTCP when recording RTP. rtpproxy records RTCP by default when RTP recording is enabled.
-p pid_fileThis parameter configures the name of the file where PID of
running rtpproxy will be stored. Default is
/var/run/rtpproxy.pid.
-T max_ttlSpecify the RTP inactivity timer. Defaults to 60 seconds.
If the rtpproxy does not receive any RTP packets for more than
max_ttl it will then delete the
session.
--maxpps
pps[,rtcp_pps]Set the default per-stream packet-per-second limit for newly
created RTP/RTCP streams. The first value applies to RTP streams.
The optional second value applies to RTCP streams; if omitted, the
RTP value is used for RTCP as well. A value of 0 disables the
default limit, which is the default behavior. The limit can be
changed for an existing session using the S pps=
control sub-command.
--maxpsize
size[,rtcp_size]Set the default maximum allowed incoming packet size in bytes
for newly created RTP/RTCP streams. The first value applies to RTP
streams. The optional second value applies to RTCP streams; if
omitted, the RTP value is used for RTCP as well. Packets larger than
the configured size are dropped. A value of 0 disables the default
limit, which is the default behavior. The limit can be changed for
an existing session using the S mps= control
sub-command.
-L
nofile_limitSet the maximum number of open file descriptors per process.
The default maximum is set by the operating system, and can be
overridden using the -L flag.
The rtpproxy requires four file descriptors per session to ensure that it can reliably identify where each stream is coming from in a NAT firewall scenario.
-A
advaddr1[/advaddr2]Set advertised address of rtpproxy. Useful if the rtpproxy is
behind a NAT firewall. (Amazon EC2) When the rtpproxy receives a
session request from a SIP controller it will return the IP
address(es) specified by the -A option.
This option configures the legacy bridge
internal/external address pair. For
per-address advertised overrides with multiple
-l/-6 entries, use
=advertized_address inside each listen
address argument.
-m min_portSet lower limit on UDP ports range that the rtpproxy uses for
RTP/RTCP sessions to min_port. Default is
35000.
-M max_portSet upper limit on UDP ports range that the rtpproxy uses for
RTP/RTCP sessions to max_port. Default is
65000.
-u
uname[:gname]Switch rtpproxy to UID identified by the
uname and optional GID identified by
gname when proxy is up and
running.
-w sock_modeSet access mode for the controlling UNIX-socket (if used).
Only applies if rtpproxy runs under a different GID using
-u option.
-FBy default the rtpproxy will warn user if running as superuser (UID 0) in local control mode and refuse to run in remote control mode at all. This switch removes the check.
-iEnable independent RTP activity timeout mode. By default, a timeout (which results in automatic destruction of the session) can only occur if no RTP packets are received on any of the session's ports. This option, if set, varies that behaviour, such that a timeout will occur if packets are still being received on one port but not the other. The option should be used with caution since in some cases it's perfectly fine to have packets coming from only one side of conversation (i.e. when the second party has muted its audio).
-n
timeout_socketThis parameter specifies permitted notification sockets only. The listening socket must be created by another application, preferably before starting rtpproxy.
Timeout notifications must be enabled by the SIP controller when setting up the session. The SIP Controller must specify the timeout_socket, and a notify_tag, which is expected to be an arbitrary string that can be used by the SIP controller to identify which session a received time out notification relates to.
If a SIP Controller specifies a notification socket for a
session, and that socket is not specified using the
-n flag, the rtpproxy will not send a notification,
and will not produce an error. It will ignore the notification
request.
Format of timeout_socket is
<type>:<socket>. Following types are supported:
unix: Connect to UNIX domain socket for sending timeout notifications. In this mode B2BUA and rtpproxy must be running on the same host.
Example: -n unix:/var/run/rtpproxy_timeout.sock
tcp: Connect to a remote host using
TCP/IP for sending timeout notifications. Format of the
socket parameter in this case is
<host>:<port>.
Example: -n tcp:10.20.30:12345
There is no default value, notifications are not sent and not
permitted unless a value is specified explicitly. Multiple
notification sockets can be provided by specifying the
-n flag more than once.
-PRecord sessions using libpcap file format instead of non-standard ad-hoc format. The libpcap format, which is the de-facto standard for packet capturing software, has the advantage of being compatible with numerous third-party tools and utilities, such as tcpdump or Wireshark. The drawback of libpcap is slightly larger overhead (extra 12 bytes for every saved RTP packet for IPv4).
-aRecord all sessions going through the rtpproxy unconditionally. By default rtpproxy expects the SIP controller to enable recording on a per-session basis.
-d
log_level[:log_facility]Configures the verbosity level of the log output. Possible
log_level values in the order from the
most verbose to the least verbose are: DBUG, INFO, WARN, ERR and
CRIT.
The optional log_facility parameter
sets syslog(3) facility assigned to log messages.
Example: -d WARN:LOG_LOCAL5
The default level in foreground mode is DBUG, in background - WARN and facility is LOG_DAEMON.
--force_asymmetricTreat all RTP/RTCP sessions as "asymmetric", i.e. disable any NAT traversal features unconditionally.
-W setup_ttlSpecify the inactivity timer for sessions that have not been
fully set up yet, i.e. before the rtpproxy has received the matching
L command. Defaults to the value of
-T.
-bAllocate UDP ports for new sessions sequentially instead of randomly. Mostly intended for debugging, to make the behaviour more predictable.
-c fifo|rrSwitch the rtpproxy process to the
SCHED_FIFO (fifo) or
SCHED_RR (rr)
real-time scheduling policy, at the maximum priority for that
policy.
--nice levelSet the scheduling priority (nice value) of the rtpproxy
process to level.
-DDo not change the working directory to /
when switching into the background mode.
--no_redirectDo not redirect the standard output and the standard error to
/dev/null when switching into the background
mode.
-CPrint the name of the monotonic clock used by the rtpproxy and exit.
--config config_fileRead the configuration of the loadable modules from
config_file, in the UCL format. An
example is provided in doc/rtpproxy.conf.sample
in the source tree.
--dso moduleLoad module, specified as a path to
the shared object of a dynamically loadable module, or as a name of
a module compiled in statically. This option can be specified
multiple times. Modules that require configuration cannot be loaded
this way, use --config instead.
--bridge_symmetricIn the bridging mode, do not treat all sessions as asymmetric, which is the default in that mode for historical reasons.
--no_resolveOnly accept numeric IP addresses, in the command line arguments and in the control commands, instead of resolving host names.
Configuration of the loadable modules is read from a
configuration file in the UCL format, specified with the
--config command-line option. See Modules for the available modules and their
parameters. An example is provided in
doc/rtpproxy.conf.sample.
The way you integrate RTPProxy into your system startup sequence is highly OS-version dependent. If you are using a pre-built RTPProxy package then most likely such method will be provided as part of that package and the best course of actions is to use that facility.
However, if you are a package maintainer or system builder you might need to create such configuration from scratch. In the next sections we would provide some basic guidelines on how to launch RTPProxy in some common cases.
On most modern Linux-based systems systemd is the recommended method to start the RTPProxy.
[Unit] Description=RTPProxy media server After=network.target Requires=network.target [Service] Type=simple PIDFile=/var/run/rtpproxy/rtpproxy.pid Environment='OPTIONS= -f -L 4096 -l 0.0.0.0 -m 10000 -M 20000 -d INFO:LOG_LOCAL5' Restart=always RestartSec=5 ExecStartPre=-/bin/mkdir /var/run/rtpproxy ExecStartPre=-/bin/chown rtpproxy:rtpproxy /var/run/rtpproxy ExecStart=/usr/local/bin/rtpproxy -p /var/run/rtpproxy/rtpproxy.pid -s unix:/var/run/rtpproxy/rtpproxy.sock \ -u rtpproxy:rtpproxy -n unix:/var/run/rtpproxy/rtpproxy_timeout.sock $OPTIONS ExecStop=/usr/bin/pkill -F /var/run/rtpproxy/rtpproxy.pid ExecStopPost=-/bin/rm -R /var/run/rtpproxy SyslogIdentifier=rtpproxy SyslogFacility=local5 TimeoutStartSec=10 TimeoutStopSec=10 [Install] WantedBy=multi-user.target
The source tree also contains the unit files used for
packaging, rpm/rtpproxy.service and
rpm/rtpproxy.socket, which are installed by the
Debian packages. They use systemd socket activation, which requires
building with --enable-systemd, see below.
Logging is a very important aspect of the RTPProxy operation that should not be overlooked. In our experience, properly configured and tested RTPProxy instance can keep running unattended literally for many years non-stop. Yet, various issues with a particular session and/or endpoint would inevitably pop-up over time, so it's of utter importance to setup logging and log management in a manner that is durable and forward-looking. Allowing someone to perform a post-mortem investigation of particular issue that happened days, if not weeks, ago with a relative ease.
Some of the considerations one should have in mind while configuring logging is:
How much storage my system has for the RTPProxy to use?
What is the typical amount of time it takes for my user to report an issue and get it escalated to the DevOps team?
What is the typical amount of time it takes for the DevOps team to act on issue and look at the logs in question?
How much detail do I need? The answer to this might depend on whether this is a development/QA or a production system.
Are there any other non-technical considerations that might influence those decisions? For example: data protection laws, law enforcement requirements etc.
First of all you need to create a file you will use for logging and give it proper permissions (according to a user/group starting RTPProxy):
touch /var/log/rtpproxy.log chown rtpproxy:rtpproxy /var/log/rtpproxy.log
Then define a log facility for it in rsyslog configuration file (add a row at the bottom of the file) and restart rsyslog server:
vim /etc/rsyslog.d/50-default.conf local5.* /var/log/rtpproxy.log systemctl restart rsyslog.service
next step add a log rotation configuration for this log file (that's a good way of handling overfilled log files):
vim /etc/logrotate.d/rtpproxy
/var/log/rtpproxy.log {
missingok
compress
delaycompress
notifempty
copytruncate
rotate 5
daily
}Now add a log facility level to your start row of the RTPProxy:
-d INFO:LOG_LOCAL5
Reload systemd configuration (to apply previous change) and restart your RTPProxy server. Now logging should be done using new output file:
systemctl daemon-reload systemctl restart rtpproxy.service
As it was said earlier, keeping of the control socket using systemd, is a good reason to start using systemd.
A configuration process is pretty simple, there are just a few steps you need to implement to start using of it.
1. Firstly, you need to install package requirements: libsystemd-dev
2. Secondly, you need to compile rtpproxy with a systemd flag (a usual compilation process, just add an option to a configure execution):
./configure --enable-systemd
3. A third step, you need to create rtpproxy.socket systemd unit file, that will be responsible for keeping your control socket:
An example:
/etc/systemd/system/rtpproxy.socket (enabling it with systemctl enable creates the link in sockets.target.wants)
[Socket] ListenStream=/var/run/rtpproxy/rtpproxy.sock SocketUser=rtpproxy SocketGroup=rtpproxy SocketMode=0660 ExecStartPost=-/bin/chown rtpproxy:rtpproxy /var/run/rtpproxy [Install] WantedBy=sockets.target
The SIP proxy has to be able to write to the socket, so make sure it runs as a member of the rtpproxy group.
4. Set a parameter in startup row of rtpproxy.service unit (you don't need to provide a path to socket file):
-s systemd:
an example:
ExecStart=/usr/local/bin/rtpproxy -p /var/run/rtpproxy/rtpproxy.pid -s systemd: -u rtpproxy:rtpproxy -n unix:/var/run/rtpproxy/rtpproxy_timeout.sock -f -l 0.0.0.0 -m 10000 -M 20000 -d INFO:LOG_DAEMON
When you're done with all above, do not forget to reload systemd configuration and enable socket unit:
bash> sudo systemctl daemon-reload bash> sudo systemctl enable rtpproxy.socket
(btw here is a nice article of how to configure systemd socket unit files - http://0pointer.de/blog/projects/socket-activation.html)
Now start rtpproxy.socket and rtpproxy.service just after that:
bash> sudo systemctl start rtpproxy.socket bash> sudo systemctl start rtpproxy.service
At this stage, your rtpproxy.service should catch up a socket created by systemd and start working properly. If you see some kind of errors, then you're missing one of the steps defined above.
Some of the RTPProxy functionality is implemented as loadable
modules. A module can be loaded either via the configuration file, which also allows setting
its parameters, or via the --dso command-line option,
which only works for modules that take no parameters.
Each module is configured in its own block of the
modules section of the configuration file. The name
of the block is the name of the module. Unless the
load parameter specifies the path to the module's
shared object, the module is loaded from
(libdir/rtpproxy/rtpp_name.sortpp_ for
the debug build). Modules that take no parameters can have an empty
block:name_debug.so
modules {
acct_rtcp_hep {
capt_host = 192.0.2.10
}
catch_dtmf {
}
}The same module can be loaded more than once, each loaded copy being a separate instance, numbered from 1 in the order of loading.
Modules that are controlled on a per-session basis accept
sub-commands appended to the U or
L command after &&, in the
form M. The module IDs are
listed below, and module_id:instance
argumentsinstance is 1 unless the
module is loaded more than once. For example, the following command
sends S to the first instance of the
dtls_gw module:
U call-id 192.0.2.1 12345 from-tag && M4:1 S
Several sub-commands can be chained in the same command, each
with its own &&. The output of each of them
follows the reply to the main command, separated by
&& too. A failed sub-command is reported as
a negative number, while one with no output is reported as
0, or omitted when no sub-command after it has any
output.
Table 1. Modules
| Module | ID | Purpose |
|---|---|---|
| acct_csv | 1 | Per-session accounting into a CSV file |
| acct_rtcp_hep | 2 | Reporting RTCP to a HEP (Homer) collector |
| catch_dtmf | 3 | Notifying the call controller of DTMF events |
| dtls_gw | 4 | Gateway between plain RTP and DTLS-SRTP |
| ice_lite | 5 | ICE-Lite and RTCP multiplexing |
Writes one record with the session's statistics into a CSV file for each session, when the session ends. A header line with the column names is written when the file is created. The columns include the Call-ID, the from-tag, the session setup and teardown times, and the packet, loss, jitter and SSRC statistics for each direction. The file is re-opened if it is moved or removed, e.g. by log rotation.
The module takes no sub-commands. The parameters are:
logfilePath of the CSV file. A relative path is relative to the
directory the rtpproxy was started from. Default:
rtpproxy_acct.csv.
Since the parameter is optional, the module can also be loaded
with the --dso command-line option.
modules {
acct_csv {
logfile = /var/log/rtpproxy/rtpproxy_acct.csv
}
}Reports every RTCP packet received, converted to JSON, to a HEP (Homer) collector, using HEPv3.
The module takes no sub-commands. The parameters are:
capt_hostAddress of the collector. Default:
10.0.0.1.
capt_portPort of the collector. Default:
9060.
capt_ptypeTransport, udp or
tcp. Default: udp.
capt_idCapture agent ID, from 0 to 4294967295. Default:
101.
modules {
acct_rtcp_hep {
capt_host = 192.0.2.10
capt_port = 9060
capt_ptype = udp
capt_id = 101
}
}Detects RFC 2833 (RFC 4733) telephone events in the stream and notifies the call controller of each DTMF digit received. Developed in collaboration with the OpenSIPS project. The module takes no parameters.
Detection is enabled per stream with the sub-command:
M3:1tag[payload_type[h]]
tagURL-encoded string that is sent back with each notification.
payload_typeRTP payload type of the telephone events. Default: 101.
hDrop the telephone event packets instead of relaying them.
Notifications are sent the same way as the session timeout
notifications, so notifications have to be enabled for the session:
the U/L command has to specify
the notification socket (permitted with the -n
command-line option) and the notification tag. For each digit, when
its last packet is received, the following line is sent:
tagdigitvolumedurationside
where duration is in RTP timestamp
units, and side is 0 for the caller and 1
for the callee. For example:
U call-id 192.0.2.1 12345 from-tag tcp:192.0.2.100:2222 ntag && M3:1 caller%20dtmf 101
would result in notifications like:
caller dtmf 1 0 960 0
Gateways between plain RTP (RTP/AVP) and DTLS-SRTP (UDP/TLS/RTP/SAVP) streams, taking care of certificate validation, key negotiation and SRTP encryption/decryption. It can also re-encrypt already encrypted streams back to back, e.g. for recording. Requires the RTPProxy to be built with libsrtp2 (or libsrtp) and OpenSSL. A self-signed certificate is generated when the module starts. The module takes no parameters.
The sub-commands are:
M4:1 SAllocate a DTLS endpoint on the side facing the encrypted party if needed, and return the session mode and the certificate fingerprint to be put into the SDP.
M4:1 A digest_alg
fingerprint
[ssrc]Set up the DTLS endpoint for the remote end in the
active or actpass mode, using
the fingerprint from its SDP. SSRC verification is enabled if
ssrc is given.
M4:1 P digest_alg
fingerprint
[ssrc]The same, for the remote end in the
passive mode.
M4:1 DDestroy the DTLS context created by A or
P.
M4:1 UDestroy the DTLS context created by
S.
For example, an incoming INVITE with plain RTP answered with DTLS-SRTP by a passive party:
U call-id 192.0.2.1 12345 from-tag && M4:1 S 35000 && actpass SHA-256 4A:AD:B9:... L call-id 192.0.2.2 23456 from-tag to-tag && M4:1 P SHA-256 2C:7F:01:... 35002
See modules/dtls_gw/README.md
for more scenarios.
Implements the lite variant of ICE (RFC 5245), allowing
ICE-enabled user agents to establish media sessions through the
RTPProxy, and RTCP multiplexing and de-multiplexing. Together with
dtls_gw, it allows the RTPProxy to act as a WebRTC
endpoint. Requires the same libraries as dtls_gw.
The module takes no parameters.
The sub-commands are:
M5:1 SAllocate an ICE endpoint if needed, and return the local
ice-ufrag, ice-pwd and the
ICE candidates (URL-encoded) to offer to the user agent.
M5:1 A ice_ufrag
ice_pwdProcess an ICE offer received from the user agent, allocating an ICE endpoint if needed.
M5:1 C candidateAdd a single ICE candidate received from the user agent, as
in the a=candidate SDP attribute. Repeat for
each candidate.
M5:1 DDestroy the ICE context in the forward direction.
M5:1 UDestroy the ICE context in the reverse direction.
For example, an incoming INVITE with an ICE offer:
U call-id 192.0.2.1 12345 from-tag && M5:1 A ufrag pwd && M5:1 C 1 1 UDP 2130706431 192.0.2.1 12345 typ host
See modules/ice_lite/README.md
for more scenarios.
This chapter is intended for developers who embed the RTPProxy into their own applications, or who work on the RTPProxy itself and need to test their changes.
The whole RTPProxy engine is also available as a library,
librtpproxy, which allows running it inside
another process, e.g. a test harness, a fuzzer or an application that
needs a media relay of its own without managing a separate
daemon.
The library is always built, as it is used by the rtpproxy
itself, but it is only installed when the
--enable-librtpproxy option is given to the
configure script:
./configure --enable-librtpproxy make make install
This installs a static librtpproxy.a,
which already includes the bundled libraries and the modules, and
the headers: librtpproxy.h with the main
interface and librtpproxy/packetport.h,
librtpproxy/packet_ext.h with the packet port
interface described below. On Debian and Ubuntu the same files are
provided by the rtpproxy-dev package.
Since the library is static, an application has to link with
the libraries that the rtpproxy itself has been configured with as
well, such as -lsiplog,
-lelperiodic, codec and SRTP libraries,
-lssl -lcrypto, -lm and
-lpthread. If the RTPProxy has been built with
LTO, the application has to be linked using the same
compiler.
The main interface consists of two functions:
#include <librtpproxy.h> struct rtpp_cfg *rtpp_main(int argc, const char * const *argv); void rtpp_shutdown(struct rtpp_cfg *);
rtpp_main() takes the same arguments as
the rtpproxy command line (see Configuration), with
argv[0] being the program name, initializes the
engine and starts its threads, then returns right away with a handle
to the running instance. The library never daemonizes, does not
write a PID file, does not change the current directory and does
not install any signal handlers, so the -f option
is not needed. Instead of exiting the process, an error in the
arguments or during initialization makes
rtpp_main() return
NULL. The global state of the
getopt() is saved and restored, so the
application is free to use it as well.
rtpp_shutdown() stops the instance,
destroys all of its sessions and releases all resources associated
with it.
An embedded instance is controlled using the same protocol as
a standalone one, via any of the control sockets described in Configuration. The most convenient
one is usually the fd:N
socket, which makes the instance use a socket created by the
application: one end of a connected socket pair becomes a private
control channel, with no need to pick an address or a path. Over
such a channel commands are written one per line and the replies are
read back the same way, without the cookies used by the network
control sockets:
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <librtpproxy.h>
int
main(void)
{
int fds[2];
char fdarg[16], buf[256];
struct rtpp_cfg *cfsp;
ssize_t len;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0)
return (1);
snprintf(fdarg, sizeof(fdarg), "fd:%d", fds[1]);
const char *argv[] = {"rtpproxy", "-s", fdarg, "-l", "127.0.0.1",
"-d", "info"};
cfsp = rtpp_main(sizeof(argv) / sizeof(argv[0]), argv);
if (cfsp == NULL)
return (1);
const char *cmd = "U call1 127.0.0.1 12000 ftag1\n";
write(fds[0], cmd, strlen(cmd));
len = read(fds[0], buf, sizeof(buf) - 1);
if (len > 0) {
buf[len] = '\0';
printf("allocated: %s", buf);
}
rtpp_shutdown(cfsp);
close(fds[0]);
return (0);
}The fuzzing harnesses in scripts/fuzz
are another example of using the library.
Besides sending and receiving packets via sockets, an embedded
instance can exchange them with the application directly, through
shared memory packet rings called packet ports. A port is created
with rtpp_packetport_ctor(), which takes the
capacity of the rings and the policy of flushing the outbound ring
(explicitly with rtpp_packetport_flush() or
on a timer). The application pushes packets into the instance with
rtpp_packetport_push() and friends, and pops
the packets sent by the instance with
rtpp_packetport_try_pop() and
friends.
A port is referenced in the commands by its address, in the
RTQ:0xpointer
format: as the remote address of a stream, followed by a port tag in
place of the port number, to have the instance send the packets of
that stream into the port, or as a local address
(l modifier) to have the stream receive packets
from the port instead of a socket. Packet ports are only available
in the library. This interface is a work in progress and may change
in the future releases, see src/librtpproxy/packetport.h
for the details.
The RTPProxy comes with an extensive suite of integration
tests, located in the tests
directory. Each test starts one or more rtpproxy instances, drives
them via their control sockets, generates and captures media, and
compares the results with the reference ones. The tests are also run
by the CI on every change, across several operating systems,
compilers and architectures.
All tests are run with:
make check
A subset of the tests can be selected with the
TESTS variable, and several tests can be run in
parallel, since none of them depends on pre-determined ports:
cd tests make -j4 check TESTS="forwarding/forwarding1 playback/playback1"
A test can also be run directly as sh
tests/group/test,
which prints the result of each of its steps. Besides the build
dependencies of the RTPProxy, the tests need Python 3 with the
sippy and parsimonious
modules (see python/tools/requirements.txt),
ffmpeg and tcpdump. When the tests are run as root, the
rtpproxy and the tools are run as the rtpproxy
user, via setpriv.
New tests are best written using the RTPProxy Testing Language
(RPTL), a small scripting language interpreted by python/RPTL/rptl_run.py.
An RPTL script starts the rtpproxy instances it needs, sends them
commands, checks the replies and runs external tools, passing the
ports allocated by one step on to the next ones, so that the tests
need neither pre-determined ports nor root privileges and can safely
run in parallel.
A script is a sequence of statements, one per line. A
socket statement defines a control connection,
which with an address of stdio: (or
udp:, tcp:,
unix: and so on, with nothing after the type)
starts a managed rtpproxy instance with the given command line
arguments. Every other statement either sends a command to the
named socket or runs a built-in, such as .eval:,
.sleep:, .include:,
.foreach: or .exec:, and
can capture the result into a variable, which is then referenced
as %%NAME%%, or pass it to a checking function.
Any failed check stops the script with an error:
# Expects RTPPROXY_BIN to be defined .eval: 0 -> RES_OK .eval: E50 -> SESS_NOTFOUND socket r: stdio: -d info -p /dev/null -> example.rout r: U call1 127.0.0.1 12000 ftag1 -> validate_port(PORT_O) r: L call1 127.0.0.1 12002 ftag1 ttag1 -> validate_port(PORT_A) .echo: allocated ports %%PORT_O%% and %%PORT_A%% r: D call1 ftag1 -> str_compare(RES_OK) r: D call1 ftag1 -> str_compare(SESS_NOTFOUND) .close: r
A test itself is a Bourne shell script, which sets up the
environment using the common functions from tests/functions,
runs the RPTL script with the RPTL_INT
interpreter, passing it the parameters with -D,
and then examines the results, e.g. compares the replies logged by
the instances with the reference output, or decodes the recorded
media with extractaudio and checks its
checksum:
#!/bin/sh
BASEDIR="`dirname "${0}"`/.."
. "${BASEDIR}/functions"
${RPTL_INT} -s "${BASEDIR}/example/example.rptl" \
-D RTPPROXY_BIN="${RTPPROXY}"
report "running example.rptl"
${DIFF} "${BASEDIR}/example/example.output" example.rout
report "checking rtpproxy replies"When the replies contain dynamically allocated ports, the
reference output can be a template that matches any port (see
scripts/revtempl.py),
or the ports can be replaced with a placeholder before the
comparison. The test, together with the files it uses, goes into a
directory of its group, and is registered in the
Makefile.ami of that group, after which the
tests/Makefile.in has to be regenerated with
automake.
The complete description of the language is available in
python/RPTL/README.md,
and the tests in tests/recording,
tests/session_timeouts,
tests/forwarding and
tests/playback are written in RPTL and can
be used as examples of more complex scenarios, such as running
several instances at once, replaying traffic or capturing streams
with external tools.