rtpbench 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rtpbench/.dockerignore +4 -0
- rtpbench/Dockerfile +12 -0
- rtpbench/__init__.py +6 -0
- rtpbench/__main__.py +3 -0
- rtpbench/agent.py +746 -0
- rtpbench/analyze.py +520 -0
- rtpbench/bargein.py +272 -0
- rtpbench/bench.py +1069 -0
- rtpbench/caller.py +781 -0
- rtpbench/capture.py +33 -0
- rtpbench/carrier/compose.yaml +68 -0
- rtpbench/carrier/opensips.cfg +226 -0
- rtpbench/carrier/tls.cfg +25 -0
- rtpbench/carrier/tls.yaml +9 -0
- rtpbench/cases.py +1989 -0
- rtpbench/check.py +369 -0
- rtpbench/cli.py +616 -0
- rtpbench/clock.py +174 -0
- rtpbench/compose.yaml +55 -0
- rtpbench/corpus/README.md +55 -0
- rtpbench/corpus/cough-c1-0.wav +0 -0
- rtpbench/corpus/cough-c1-1.wav +0 -0
- rtpbench/corpus/cough-c2-0.wav +0 -0
- rtpbench/corpus/cough-c2-1.wav +0 -0
- rtpbench/corpus/cough-man-0.wav +0 -0
- rtpbench/corpus/cough-man-1.wav +0 -0
- rtpbench/corpus/cough-short-0.wav +0 -0
- rtpbench/corpus/cough-short-1.wav +0 -0
- rtpbench/corpus/cough-woman_three_times-0.wav +0 -0
- rtpbench/corpus/human-en-dr-0.wav +0 -0
- rtpbench/corpus/human-en-dr-1.wav +0 -0
- rtpbench/corpus/human-en-sc-0.wav +0 -0
- rtpbench/corpus/human-en-sc-1.wav +0 -0
- rtpbench/corpus/human-en-sc-2.wav +0 -0
- rtpbench/corpus/human-es-f012-0.wav +0 -0
- rtpbench/corpus/human-es-f012-1.wav +0 -0
- rtpbench/corpus/human-es-f012-2.wav +0 -0
- rtpbench/corpus/human-es-f016-0.wav +0 -0
- rtpbench/corpus/human-es-f016-1.wav +0 -0
- rtpbench/corpus/human-es-f016-2.wav +0 -0
- rtpbench/corpus/lines/en/interrupt-00.wav +0 -0
- rtpbench/corpus/lines/en/interrupt-01.wav +0 -0
- rtpbench/corpus/lines/en/interrupt-02.wav +0 -0
- rtpbench/corpus/lines/en/interrupt-03.wav +0 -0
- rtpbench/corpus/lines/en/prompt-00.wav +0 -0
- rtpbench/corpus/lines/en/prompt-01.wav +0 -0
- rtpbench/corpus/lines/en/prompt-02.wav +0 -0
- rtpbench/corpus/lines/en/prompt-03.wav +0 -0
- rtpbench/corpus/lines/en/prompt-04.wav +0 -0
- rtpbench/corpus/lines/en/prompt-05.wav +0 -0
- rtpbench/corpus/lines/en/prompt-06.wav +0 -0
- rtpbench/corpus/lines/en/prompt-07.wav +0 -0
- rtpbench/corpus/lines/en/prompt-08.wav +0 -0
- rtpbench/corpus/lines/en/prompt-09.wav +0 -0
- rtpbench/corpus/lines/en/prompt.wav +0 -0
- rtpbench/corpus/lines/en/transfer.wav +0 -0
- rtpbench/corpus/lines/en/turn-00.wav +0 -0
- rtpbench/corpus/lines/en/turn-01.wav +0 -0
- rtpbench/corpus/lines/en/turn-02.wav +0 -0
- rtpbench/corpus/lines/en/turn-03.wav +0 -0
- rtpbench/corpus/lines/en/turn-04.wav +0 -0
- rtpbench/corpus/lines/en/turn-05.wav +0 -0
- rtpbench/corpus/lines/en/turn-06.wav +0 -0
- rtpbench/corpus/lines/en/turn-07.wav +0 -0
- rtpbench/corpus/lines/en/turn-08.wav +0 -0
- rtpbench/corpus/lines/en/turn-09.wav +0 -0
- rtpbench/corpus/lines/en/turn-10.wav +0 -0
- rtpbench/corpus/lines/en/turn-11.wav +0 -0
- rtpbench/corpus/tts-ald-bc.wav +0 -0
- rtpbench/corpus/tts-ald-long.wav +0 -0
- rtpbench/corpus/tts-ald-s03.wav +0 -0
- rtpbench/corpus/tts-ald-s07.wav +0 -0
- rtpbench/corpus/tts-ald-s11.wav +0 -0
- rtpbench/corpus/tts-cori-bc.wav +0 -0
- rtpbench/corpus/tts-cori-long.wav +0 -0
- rtpbench/corpus/tts-cori-s01.wav +0 -0
- rtpbench/corpus/tts-cori-s05.wav +0 -0
- rtpbench/corpus/tts-cori-s09.wav +0 -0
- rtpbench/corpus/tts-davefx-bc.wav +0 -0
- rtpbench/corpus/tts-davefx-long.wav +0 -0
- rtpbench/corpus/tts-davefx-s00.wav +0 -0
- rtpbench/corpus/tts-davefx-s04.wav +0 -0
- rtpbench/corpus/tts-davefx-s08.wav +0 -0
- rtpbench/corpus/tts-libritts0-bc.wav +0 -0
- rtpbench/corpus/tts-libritts0-long.wav +0 -0
- rtpbench/corpus/tts-libritts0-s02.wav +0 -0
- rtpbench/corpus/tts-libritts0-s06.wav +0 -0
- rtpbench/corpus/tts-libritts0-s10.wav +0 -0
- rtpbench/corpus/tts-libritts3-bc.wav +0 -0
- rtpbench/corpus/tts-libritts3-long.wav +0 -0
- rtpbench/corpus/tts-libritts3-s03.wav +0 -0
- rtpbench/corpus/tts-libritts3-s07.wav +0 -0
- rtpbench/corpus/tts-libritts3-s11.wav +0 -0
- rtpbench/corpus/tts-ljspeech-bc.wav +0 -0
- rtpbench/corpus/tts-ljspeech-long.wav +0 -0
- rtpbench/corpus/tts-ljspeech-s00.wav +0 -0
- rtpbench/corpus/tts-ljspeech-s04.wav +0 -0
- rtpbench/corpus/tts-ljspeech-s08.wav +0 -0
- rtpbench/corpus/tts-sharvardf-bc.wav +0 -0
- rtpbench/corpus/tts-sharvardf-long.wav +0 -0
- rtpbench/corpus/tts-sharvardf-s02.wav +0 -0
- rtpbench/corpus/tts-sharvardf-s06.wav +0 -0
- rtpbench/corpus/tts-sharvardf-s10.wav +0 -0
- rtpbench/corpus/tts-sharvardm-bc.wav +0 -0
- rtpbench/corpus/tts-sharvardm-long.wav +0 -0
- rtpbench/corpus/tts-sharvardm-s01.wav +0 -0
- rtpbench/corpus/tts-sharvardm-s05.wav +0 -0
- rtpbench/corpus/tts-sharvardm-s09.wav +0 -0
- rtpbench/detect.py +145 -0
- rtpbench/g711.py +82 -0
- rtpbench/mock/compose.yaml +18 -0
- rtpbench/pcap.py +106 -0
- rtpbench/progress.py +184 -0
- rtpbench/realtime.py +1015 -0
- rtpbench/report.py +450 -0
- rtpbench/rtp.py +166 -0
- rtpbench/sip.py +653 -0
- rtpbench/speech.py +100 -0
- rtpbench/speechcal.py +367 -0
- rtpbench/srtp.py +191 -0
- rtpbench/targets/aivc/aivc.ini +25 -0
- rtpbench/targets/aivc/compose.yaml +72 -0
- rtpbench/targets/aivc/constraints.txt +52 -0
- rtpbench/targets/aivc/opensips.cfg +45 -0
- rtpbench/targets/ava/ai-agent.yaml +275 -0
- rtpbench/targets/ava/ari.conf +7 -0
- rtpbench/targets/ava/compose.yaml +128 -0
- rtpbench/targets/ava/constraints.txt +75 -0
- rtpbench/targets/ava/extensions.conf +10 -0
- rtpbench/targets/ava/http.conf +4 -0
- rtpbench/targets/ava/pjsip.conf +19 -0
- rtpbench/targets/ava/websocket_client.conf +15 -0
- rtpbench/targets/livekit/agent.py +111 -0
- rtpbench/targets/livekit/compose.yaml +86 -0
- rtpbench/targets/livekit/constraints.txt +72 -0
- rtpbench/tls.py +284 -0
- rtpbench/uri/advertise.yaml +7 -0
- rtpbench/uri/network.yaml +14 -0
- rtpbench/voice.py +18 -0
- rtpbench-0.3.0.dist-info/METADATA +123 -0
- rtpbench-0.3.0.dist-info/RECORD +144 -0
- rtpbench-0.3.0.dist-info/WHEEL +4 -0
- rtpbench-0.3.0.dist-info/entry_points.txt +2 -0
- rtpbench-0.3.0.dist-info/licenses/LICENSE +202 -0
rtpbench/capture.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""tcpdump as a context manager: the pcap is the timing source of truth."""
|
|
2
|
+
|
|
3
|
+
import signal
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
from collections.abc import Iterator
|
|
8
|
+
from contextlib import contextmanager
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@contextmanager
|
|
13
|
+
def capture(path: Path, iface: str = "eth0", bpf: str = "udp or tcp") -> Iterator[None]:
|
|
14
|
+
# Immediate mode: without it libpcap hands packets over in blocks of up to a second, and
|
|
15
|
+
# stopping tcpdump right after a BYE loses the end of the call. 32 MiB of kernel buffer
|
|
16
|
+
# (default 2): a busy host once starved a capture of four streams into a drop.
|
|
17
|
+
cmd = ["tcpdump", "-i", iface, "-n", "-Z", "root", "--immediate-mode", "-B", "32768"]
|
|
18
|
+
cmd += ["--time-stamp-precision=nano", "-w", str(path), bpf]
|
|
19
|
+
proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, text=True)
|
|
20
|
+
assert proc.stderr is not None
|
|
21
|
+
line = proc.stderr.readline()
|
|
22
|
+
if "listening on" not in line:
|
|
23
|
+
proc.kill()
|
|
24
|
+
raise RuntimeError(f"tcpdump did not start: {line.strip()}")
|
|
25
|
+
try:
|
|
26
|
+
yield
|
|
27
|
+
finally:
|
|
28
|
+
time.sleep(0.2)
|
|
29
|
+
proc.send_signal(signal.SIGINT)
|
|
30
|
+
_, err = proc.communicate(timeout=10)
|
|
31
|
+
print(f"{path.name}: " + "; ".join(err.split("\n")[:3]).strip("; "), file=sys.stderr)
|
|
32
|
+
if "\n0 packets dropped by kernel" not in err:
|
|
33
|
+
raise RuntimeError(f"tcpdump dropped packets writing {path}:\n{err}")
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# A carrier between the router and the agent (the reference agent or a target), merged over
|
|
2
|
+
# ../compose.yaml: OpenSIPS as an SBC (stateful proxy with topology hiding) and rtpengine
|
|
3
|
+
# anchoring the media, as real trunks put in front of a PBX. `carrier` owns the network namespace
|
|
4
|
+
# on agentnet, so caller <-> carrier crosses the netem router and carrier <-> agent does not.
|
|
5
|
+
# rtpbench sets RTPBENCH_CARRIER_NEXT to the agent's host (and RTPBENCH_CARRIER_PORT, its SIP
|
|
6
|
+
# port, for an agent outside the stack, which the carrier reaches through Docker's gateway).
|
|
7
|
+
# Paths are relative to the first compose file's directory.
|
|
8
|
+
services:
|
|
9
|
+
carrier:
|
|
10
|
+
image: ${RTPBENCH_IMAGE:?}
|
|
11
|
+
cap_add: [NET_ADMIN]
|
|
12
|
+
init: true
|
|
13
|
+
depends_on: [router]
|
|
14
|
+
networks: [agentnet]
|
|
15
|
+
volumes: ["${RTPBENCH_OUT:?}:/out"]
|
|
16
|
+
command: sh -c 'ip route replace default via $$(getent hosts router | cut -d" " -f1) && exec sleep infinity'
|
|
17
|
+
|
|
18
|
+
carrier-media:
|
|
19
|
+
build:
|
|
20
|
+
# neither build copies a file, but the tag hashes the context's: not the whole repository
|
|
21
|
+
context: ./carrier
|
|
22
|
+
dockerfile_inline: |
|
|
23
|
+
FROM debian:trixie@sha256:913f6706df59a68922d1dd08f78c2476560a8d367897200a6005b00e5f67c2d5
|
|
24
|
+
RUN apt-get update \
|
|
25
|
+
&& apt-get install -y --no-install-recommends rtpengine-daemon=12.5.1.31-1 \
|
|
26
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
27
|
+
image: ${RTPBENCH_REGISTRY:?}/rtpbench-rtpengine:12.5.1.31${RTPBENCH_BUILD_CARRIER_MEDIA:-}
|
|
28
|
+
network_mode: service:carrier
|
|
29
|
+
depends_on: [carrier]
|
|
30
|
+
# userspace forwarding (no kernel module in a container), media on the agentnet address
|
|
31
|
+
command: >-
|
|
32
|
+
rtpengine --foreground --log-stderr --table=-1 --interface=eth0
|
|
33
|
+
--listen-ng=127.0.0.1:2223 --port-min=30000 --port-max=30999
|
|
34
|
+
healthcheck: # its control socket (127.0.0.1:2223) bound; OpenSIPS disables a relay that
|
|
35
|
+
# does not answer its first ping, for a minute, and calls then bypass it
|
|
36
|
+
test: grep -q ' 0100007F:08AF 00000000:0000 07 ' /proc/net/udp
|
|
37
|
+
interval: 1s
|
|
38
|
+
retries: 20
|
|
39
|
+
|
|
40
|
+
carrier-sip:
|
|
41
|
+
build: # the same OpenSIPS, with its digest authentication and TLS modules (not in the image)
|
|
42
|
+
context: ./carrier
|
|
43
|
+
dockerfile_inline: |
|
|
44
|
+
FROM opensips/opensips:3.6@sha256:81a7fa3c7bdbceaadb7cb869ff7ac3f34efb2d95cd7fecb3b689d7b6b1eb0672
|
|
45
|
+
RUN apt-get update \
|
|
46
|
+
&& apt-get install -y --no-install-recommends opensips-auth-modules=3.6.9-1 \
|
|
47
|
+
opensips-tls-module=3.6.9-1 opensips-tls-openssl-module=3.6.9-1 \
|
|
48
|
+
opensips-tlsmgm-module=3.6.9-1 \
|
|
49
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
50
|
+
image: ${RTPBENCH_REGISTRY:?}/rtpbench-opensips:3.6.9-1-tls${RTPBENCH_BUILD_CARRIER_SIP:-}
|
|
51
|
+
network_mode: service:carrier
|
|
52
|
+
depends_on:
|
|
53
|
+
carrier-media: {condition: service_healthy}
|
|
54
|
+
volumes: ["./carrier/opensips.cfg:/etc/opensips/opensips.cfg:ro"]
|
|
55
|
+
environment:
|
|
56
|
+
CARRIER_NEXT: ${RTPBENCH_CARRIER_NEXT:?}
|
|
57
|
+
CARRIER_NEXT_PORT: ${RTPBENCH_CARRIER_PORT:-5060}
|
|
58
|
+
CARRIER_NEXT_TRANSPORT: ${RTPBENCH_CARRIER_TRANSPORT:-udp}
|
|
59
|
+
# set, the carrier demands these credentials of the caller, as a trunk may
|
|
60
|
+
CARRIER_AUTH_USER: ${RTPBENCH_CARRIER_AUTH_USER:-}
|
|
61
|
+
CARRIER_AUTH_PASSWORD: ${RTPBENCH_CARRIER_AUTH_PASSWORD:-}
|
|
62
|
+
# set, it answers the agent's challenge with these
|
|
63
|
+
CARRIER_UAC_USER: ${RTPBENCH_CARRIER_UAC_USER:-}
|
|
64
|
+
CARRIER_UAC_PASSWORD: ${RTPBENCH_CARRIER_UAC_PASSWORD:-}
|
|
65
|
+
healthcheck:
|
|
66
|
+
test: opensips-cli -x mi sr_get_status core | grep -q running
|
|
67
|
+
interval: 1s
|
|
68
|
+
retries: 20
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# rtpbench carrier: an SBC in front of the agent, as trunks put one. A stateful proxy that keeps
|
|
2
|
+
# a dialog per call, hides the topology (the agent sees the carrier as the far end: its Via,
|
|
3
|
+
# Contact and Call-ID side only) and anchors the media of both legs in rtpengine. Every request
|
|
4
|
+
# goes to the host in CARRIER_NEXT, port CARRIER_NEXT_PORT, with the caller's Request-URI user,
|
|
5
|
+
# over TCP if CARRIER_NEXT_TRANSPORT is tcp, except calls to the user `operator` (a transfer's
|
|
6
|
+
# target), which go to the host of that name.
|
|
7
|
+
#
|
|
8
|
+
# A call can ask for one mutation in an X-Mutation header (removed here), applied to what goes
|
|
9
|
+
# to the agent:
|
|
10
|
+
# compact-headers From, To, Call-ID, Content-Type in compact form (RFC 3261 7.3.3)
|
|
11
|
+
# sdp-ssrc the offer carries `a=ssrc:<id>` without an attribute (AIVC CE #27)
|
|
12
|
+
# late-offer the INVITE carries no SDP: the agent offers in its 2xx, the ACK answers
|
|
13
|
+
# (RFC 3261 13.2.1); the caller's leg stays early offer
|
|
14
|
+
# ptime-30 rtpengine repacketises the caller's audio to 30 ms (a=ptime:30)
|
|
15
|
+
#
|
|
16
|
+
# With CARRIER_AUTH_USER set, the caller must authenticate (digest, CARRIER_AUTH_PASSWORD) every
|
|
17
|
+
# request it sends but ACK and CANCEL (RFC 3261 22.1): the initial INVITE is challenged with
|
|
18
|
+
# MD5, requests in the dialog with SHA-256 (RFC 8760), the algorithms agents ask for. With
|
|
19
|
+
# CARRIER_UAC_USER set instead, the carrier answers the agent's challenge to the INVITE, once,
|
|
20
|
+
# with CARRIER_UAC_PASSWORD, as a trunk does.
|
|
21
|
+
#
|
|
22
|
+
# The caller's leg may be secure, as trunks offer it: SIP over TLS (tls.cfg loads it) and SRTP
|
|
23
|
+
# keyed in the SDP, which rtpengine terminates. The agent's leg is plain SIP (UDP or TCP) and RTP.
|
|
24
|
+
|
|
25
|
+
log_level=2
|
|
26
|
+
xlog_level=2
|
|
27
|
+
stderror_enabled=yes
|
|
28
|
+
syslog_enabled=no
|
|
29
|
+
|
|
30
|
+
socket=udp:*:5060
|
|
31
|
+
socket=tcp:*:5060
|
|
32
|
+
|
|
33
|
+
mpath="/usr/lib/x86_64-linux-gnu/opensips/modules/"
|
|
34
|
+
|
|
35
|
+
loadmodule "proto_udp.so"
|
|
36
|
+
loadmodule "proto_tcp.so"
|
|
37
|
+
loadmodule "tm.so"
|
|
38
|
+
loadmodule "sl.so"
|
|
39
|
+
loadmodule "rr.so"
|
|
40
|
+
loadmodule "maxfwd.so"
|
|
41
|
+
loadmodule "sipmsgops.so"
|
|
42
|
+
loadmodule "textops.so"
|
|
43
|
+
loadmodule "signaling.so"
|
|
44
|
+
loadmodule "cfgutils.so"
|
|
45
|
+
loadmodule "mi_fifo.so"
|
|
46
|
+
loadmodule "dialog.so"
|
|
47
|
+
loadmodule "topology_hiding.so"
|
|
48
|
+
loadmodule "rtpengine.so"
|
|
49
|
+
loadmodule "auth.so"
|
|
50
|
+
loadmodule "uac_auth.so"
|
|
51
|
+
loadmodule "uac.so"
|
|
52
|
+
|
|
53
|
+
modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")
|
|
54
|
+
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
|
|
55
|
+
modparam("auth", "calculate_ha1", 1)
|
|
56
|
+
modparam("auth", "username_spec", "$var(username)")
|
|
57
|
+
modparam("auth", "password_spec", "$var(password)")
|
|
58
|
+
modparam("uac_auth", "auth_username_avp", "$avp(uac_user)")
|
|
59
|
+
modparam("uac_auth", "auth_password_avp", "$avp(uac_password)")
|
|
60
|
+
modparam("uac_auth", "auth_realm_avp", "$avp(uac_realm)")
|
|
61
|
+
|
|
62
|
+
route {
|
|
63
|
+
if (!mf_process_maxfwd_header(10)) {
|
|
64
|
+
send_reply(483, "Too Many Hops");
|
|
65
|
+
exit;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (has_totag()) {
|
|
69
|
+
# before matching: the dialog module ends a dialog when it matches its BYE, challenged
|
|
70
|
+
# or not. Requests from the agent's side (the next hop, the transfer target) pass.
|
|
71
|
+
# Each address copied before comparing: $si and {ip.resolve} print into one buffer.
|
|
72
|
+
$var(next) = $env(CARRIER_NEXT);
|
|
73
|
+
$var(next) = $(var(next){ip.resolve});
|
|
74
|
+
$var(operator) = "operator";
|
|
75
|
+
$var(operator) = $(var(operator){ip.resolve});
|
|
76
|
+
$var(si) = $si;
|
|
77
|
+
if ($var(si) != $var(next) && $var(si) != $var(operator))
|
|
78
|
+
route(auth, "sha-256");
|
|
79
|
+
if (topology_hiding_match()) {
|
|
80
|
+
if ($DLG_dir == "downstream")
|
|
81
|
+
route(mutate);
|
|
82
|
+
if (is_method("INVITE") && !has_body("application/sdp"))
|
|
83
|
+
$dlg_val(reoffer) = "1"; # a re-INVITE without SDP: the 2xx offers
|
|
84
|
+
if (is_method("BYE")) {
|
|
85
|
+
rtpengine_delete();
|
|
86
|
+
} else if (is_method("ACK") && $dlg_val(offer) != NULL) {
|
|
87
|
+
# late offer: the caller's offer, as rtpengine gave it, answers the agent's
|
|
88
|
+
add_body_part($dlg_val(offer), "application/sdp");
|
|
89
|
+
$dlg_val(offer) = NULL;
|
|
90
|
+
} else if (is_method("ACK") && has_body("application/sdp")) {
|
|
91
|
+
rtpengine_answer("replace-origin");
|
|
92
|
+
$dlg_val(reoffer) = NULL;
|
|
93
|
+
} else {
|
|
94
|
+
route(media_offer);
|
|
95
|
+
}
|
|
96
|
+
t_on_reply("media_answer");
|
|
97
|
+
t_relay();
|
|
98
|
+
} else if (is_method("ACK") && t_check_trans()) {
|
|
99
|
+
t_relay();
|
|
100
|
+
} else if (!is_method("ACK")) {
|
|
101
|
+
send_reply(481, "Call/Transaction Does Not Exist");
|
|
102
|
+
}
|
|
103
|
+
exit;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (is_method("CANCEL")) {
|
|
107
|
+
if (t_check_trans())
|
|
108
|
+
t_relay();
|
|
109
|
+
exit;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
t_check_trans();
|
|
113
|
+
|
|
114
|
+
if (!is_method("INVITE")) {
|
|
115
|
+
send_reply(405, "Method Not Allowed");
|
|
116
|
+
exit;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
route(auth, "md5");
|
|
120
|
+
create_dialog();
|
|
121
|
+
if (has_body("application/sdp") && search_body("RTP/SAVP"))
|
|
122
|
+
$dlg_val(srtp) = "1";
|
|
123
|
+
if ($hdr(X-Mutation) != NULL) {
|
|
124
|
+
$dlg_val(mutation) = $hdr(X-Mutation);
|
|
125
|
+
remove_hf("X-Mutation");
|
|
126
|
+
xlog("L_NOTICE", "carrier: $ci gets $dlg_val(mutation)\n");
|
|
127
|
+
}
|
|
128
|
+
$var(host) = $env(CARRIER_NEXT) + ":" + $env(CARRIER_NEXT_PORT);
|
|
129
|
+
if ($rU == "operator") # a transferee calling the transfer target
|
|
130
|
+
$var(host) = "operator:5060";
|
|
131
|
+
# whatever the caller's leg was (TLS: sips: or transport=tls), without its params
|
|
132
|
+
if ($rU == NULL)
|
|
133
|
+
$ru = "sip:" + $var(host);
|
|
134
|
+
else
|
|
135
|
+
$ru = "sip:" + $rU + "@" + $var(host);
|
|
136
|
+
if ($rU != "operator" && $env(CARRIER_NEXT_TRANSPORT) == "tcp")
|
|
137
|
+
$ru = $ru + ";transport=tcp";
|
|
138
|
+
topology_hiding("C");
|
|
139
|
+
route(mutate);
|
|
140
|
+
route(media_offer);
|
|
141
|
+
t_on_reply("media_answer");
|
|
142
|
+
t_on_failure("media_end");
|
|
143
|
+
t_relay();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
route[auth] { # $param(1): the algorithm to challenge with
|
|
147
|
+
$var(username) = $env(CARRIER_AUTH_USER); # compose passes it empty when unset
|
|
148
|
+
if ($(var(username){s.len}) == 0 || is_method("ACK|CANCEL"))
|
|
149
|
+
return;
|
|
150
|
+
$var(password) = $env(CARRIER_AUTH_PASSWORD);
|
|
151
|
+
if (!pv_proxy_authorize("carrier")) {
|
|
152
|
+
proxy_challenge("carrier", "auth", $param(1));
|
|
153
|
+
exit;
|
|
154
|
+
}
|
|
155
|
+
consume_credentials();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
route[mutate] {
|
|
159
|
+
if ($dlg_val(mutation) == "compact-headers") {
|
|
160
|
+
subst('/^From:/f:/');
|
|
161
|
+
subst('/^To:/t:/');
|
|
162
|
+
subst('/^Call-ID:/i:/');
|
|
163
|
+
subst('/^Content-Type:/c:/');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
route[media_offer] {
|
|
168
|
+
if (!has_body("application/sdp"))
|
|
169
|
+
return;
|
|
170
|
+
$var(flags) = "replace-origin";
|
|
171
|
+
if ($dlg_val(mutation) == "ptime-30")
|
|
172
|
+
$var(flags) = $var(flags) + " ptime=30";
|
|
173
|
+
if ($dlg_val(mutation) == "sdp-ssrc")
|
|
174
|
+
$var(flags) = $var(flags) + " sdp-attr-add-audio-ssrc:2330119309";
|
|
175
|
+
if ($dlg_val(srtp) == "1" && (!has_totag() || $DLG_dir == "downstream"))
|
|
176
|
+
$var(flags) = $var(flags) + " RTP/AVP"; # to the agent: plain RTP
|
|
177
|
+
else if ($dlg_val(srtp) == "1")
|
|
178
|
+
$var(flags) = $var(flags) + " RTP/SAVP"; # to the caller: SRTP, rtpengine's keys
|
|
179
|
+
if ($dlg_val(mutation) == "late-offer" && !has_totag()) {
|
|
180
|
+
rtpengine_offer($var(flags), , $var(sdp));
|
|
181
|
+
$var(ok) = $rc;
|
|
182
|
+
$dlg_val(offer) = $var(sdp);
|
|
183
|
+
remove_body_part();
|
|
184
|
+
} else {
|
|
185
|
+
rtpengine_offer($var(flags));
|
|
186
|
+
$var(ok) = $rc;
|
|
187
|
+
}
|
|
188
|
+
if ($var(ok) < 0) { # never let media bypass the carrier
|
|
189
|
+
sl_send_reply(503, "Media relay unavailable");
|
|
190
|
+
exit;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
onreply_route[media_answer] {
|
|
195
|
+
if (!has_body("application/sdp"))
|
|
196
|
+
return;
|
|
197
|
+
if ($dlg_val(reoffer) == "1" && is_method("INVITE") && $dlg_val(srtp) == "1")
|
|
198
|
+
rtpengine_offer("replace-origin RTP/SAVP"); # the agent's offer, to the caller
|
|
199
|
+
else if ($dlg_val(reoffer) == "1" && is_method("INVITE"))
|
|
200
|
+
rtpengine_offer("replace-origin");
|
|
201
|
+
else
|
|
202
|
+
rtpengine_answer("replace-origin");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
failure_route[media_end] {
|
|
206
|
+
route(uac);
|
|
207
|
+
rtpengine_delete();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
route[uac] { # the agent's challenge to the INVITE, answered once (CARRIER_UAC_USER)
|
|
211
|
+
$var(user) = $env(CARRIER_UAC_USER); # compose passes it empty when unset
|
|
212
|
+
if ($(var(user){s.len}) == 0 || $avp(uac_tried) != NULL || !t_check_status("401|407"))
|
|
213
|
+
return;
|
|
214
|
+
$avp(uac_tried) = "1";
|
|
215
|
+
$var(challenge) = $(<reply>hdr(Proxy-Authenticate));
|
|
216
|
+
if ($var(challenge) == NULL)
|
|
217
|
+
$var(challenge) = $(<reply>hdr(WWW-Authenticate));
|
|
218
|
+
$avp(uac_realm) = $(var(challenge){re.subst,/.*realm="([^"]*)".*/\1/});
|
|
219
|
+
$avp(uac_user) = $var(user);
|
|
220
|
+
$avp(uac_password) = $env(CARRIER_UAC_PASSWORD);
|
|
221
|
+
if (uac_auth()) {
|
|
222
|
+
t_on_failure("media_end");
|
|
223
|
+
t_relay();
|
|
224
|
+
exit;
|
|
225
|
+
}
|
|
226
|
+
}
|
rtpbench/carrier/tls.cfg
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# rtpbench carrier with SIP over TLS on the caller's leg (5061), as a trunk takes it: the
|
|
2
|
+
# certificate is the stack's test one; nothing checks the caller's. Everything else is
|
|
3
|
+
# opensips.cfg, included below; the agent's leg stays on UDP.
|
|
4
|
+
|
|
5
|
+
mpath="/usr/lib/x86_64-linux-gnu/opensips/modules/"
|
|
6
|
+
|
|
7
|
+
socket=tls:*:5061
|
|
8
|
+
|
|
9
|
+
loadmodule "tls_openssl.so"
|
|
10
|
+
loadmodule "tls_mgm.so"
|
|
11
|
+
loadmodule "proto_tls.so"
|
|
12
|
+
|
|
13
|
+
modparam("tls_mgm", "server_domain", "carrier")
|
|
14
|
+
modparam("tls_mgm", "certificate", "[carrier]/tls/server.crt")
|
|
15
|
+
modparam("tls_mgm", "private_key", "[carrier]/tls/server.key")
|
|
16
|
+
modparam("tls_mgm", "ca_list", "[carrier]/tls/stack-ca.pem")
|
|
17
|
+
modparam("tls_mgm", "tls_method", "[carrier]TLSv1_2-")
|
|
18
|
+
modparam("tls_mgm", "verify_cert", "[carrier]0")
|
|
19
|
+
modparam("tls_mgm", "require_cert", "[carrier]0")
|
|
20
|
+
# should a request to the caller need a connection of its own (the caller's is reused)
|
|
21
|
+
modparam("tls_mgm", "client_domain", "caller")
|
|
22
|
+
modparam("tls_mgm", "match_ip_address", "[caller]*")
|
|
23
|
+
modparam("tls_mgm", "verify_cert", "[caller]0")
|
|
24
|
+
|
|
25
|
+
include_file "/etc/opensips/opensips.cfg"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# The carrier with SIP over TLS on the caller's leg (carrier/tls.cfg), merged over
|
|
2
|
+
# compose.yaml: the stack's test certificate from the results' tls/ directory.
|
|
3
|
+
services:
|
|
4
|
+
carrier-sip:
|
|
5
|
+
entrypoint: [/usr/sbin/opensips, -F, -f, /etc/opensips/tls.cfg]
|
|
6
|
+
volumes:
|
|
7
|
+
- ./carrier/opensips.cfg:/etc/opensips/opensips.cfg:ro
|
|
8
|
+
- ./carrier/tls.cfg:/etc/opensips/tls.cfg:ro
|
|
9
|
+
- ${RTPBENCH_OUT:?}/tls:/tls:ro
|