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.
Files changed (144) hide show
  1. rtpbench/.dockerignore +4 -0
  2. rtpbench/Dockerfile +12 -0
  3. rtpbench/__init__.py +6 -0
  4. rtpbench/__main__.py +3 -0
  5. rtpbench/agent.py +746 -0
  6. rtpbench/analyze.py +520 -0
  7. rtpbench/bargein.py +272 -0
  8. rtpbench/bench.py +1069 -0
  9. rtpbench/caller.py +781 -0
  10. rtpbench/capture.py +33 -0
  11. rtpbench/carrier/compose.yaml +68 -0
  12. rtpbench/carrier/opensips.cfg +226 -0
  13. rtpbench/carrier/tls.cfg +25 -0
  14. rtpbench/carrier/tls.yaml +9 -0
  15. rtpbench/cases.py +1989 -0
  16. rtpbench/check.py +369 -0
  17. rtpbench/cli.py +616 -0
  18. rtpbench/clock.py +174 -0
  19. rtpbench/compose.yaml +55 -0
  20. rtpbench/corpus/README.md +55 -0
  21. rtpbench/corpus/cough-c1-0.wav +0 -0
  22. rtpbench/corpus/cough-c1-1.wav +0 -0
  23. rtpbench/corpus/cough-c2-0.wav +0 -0
  24. rtpbench/corpus/cough-c2-1.wav +0 -0
  25. rtpbench/corpus/cough-man-0.wav +0 -0
  26. rtpbench/corpus/cough-man-1.wav +0 -0
  27. rtpbench/corpus/cough-short-0.wav +0 -0
  28. rtpbench/corpus/cough-short-1.wav +0 -0
  29. rtpbench/corpus/cough-woman_three_times-0.wav +0 -0
  30. rtpbench/corpus/human-en-dr-0.wav +0 -0
  31. rtpbench/corpus/human-en-dr-1.wav +0 -0
  32. rtpbench/corpus/human-en-sc-0.wav +0 -0
  33. rtpbench/corpus/human-en-sc-1.wav +0 -0
  34. rtpbench/corpus/human-en-sc-2.wav +0 -0
  35. rtpbench/corpus/human-es-f012-0.wav +0 -0
  36. rtpbench/corpus/human-es-f012-1.wav +0 -0
  37. rtpbench/corpus/human-es-f012-2.wav +0 -0
  38. rtpbench/corpus/human-es-f016-0.wav +0 -0
  39. rtpbench/corpus/human-es-f016-1.wav +0 -0
  40. rtpbench/corpus/human-es-f016-2.wav +0 -0
  41. rtpbench/corpus/lines/en/interrupt-00.wav +0 -0
  42. rtpbench/corpus/lines/en/interrupt-01.wav +0 -0
  43. rtpbench/corpus/lines/en/interrupt-02.wav +0 -0
  44. rtpbench/corpus/lines/en/interrupt-03.wav +0 -0
  45. rtpbench/corpus/lines/en/prompt-00.wav +0 -0
  46. rtpbench/corpus/lines/en/prompt-01.wav +0 -0
  47. rtpbench/corpus/lines/en/prompt-02.wav +0 -0
  48. rtpbench/corpus/lines/en/prompt-03.wav +0 -0
  49. rtpbench/corpus/lines/en/prompt-04.wav +0 -0
  50. rtpbench/corpus/lines/en/prompt-05.wav +0 -0
  51. rtpbench/corpus/lines/en/prompt-06.wav +0 -0
  52. rtpbench/corpus/lines/en/prompt-07.wav +0 -0
  53. rtpbench/corpus/lines/en/prompt-08.wav +0 -0
  54. rtpbench/corpus/lines/en/prompt-09.wav +0 -0
  55. rtpbench/corpus/lines/en/prompt.wav +0 -0
  56. rtpbench/corpus/lines/en/transfer.wav +0 -0
  57. rtpbench/corpus/lines/en/turn-00.wav +0 -0
  58. rtpbench/corpus/lines/en/turn-01.wav +0 -0
  59. rtpbench/corpus/lines/en/turn-02.wav +0 -0
  60. rtpbench/corpus/lines/en/turn-03.wav +0 -0
  61. rtpbench/corpus/lines/en/turn-04.wav +0 -0
  62. rtpbench/corpus/lines/en/turn-05.wav +0 -0
  63. rtpbench/corpus/lines/en/turn-06.wav +0 -0
  64. rtpbench/corpus/lines/en/turn-07.wav +0 -0
  65. rtpbench/corpus/lines/en/turn-08.wav +0 -0
  66. rtpbench/corpus/lines/en/turn-09.wav +0 -0
  67. rtpbench/corpus/lines/en/turn-10.wav +0 -0
  68. rtpbench/corpus/lines/en/turn-11.wav +0 -0
  69. rtpbench/corpus/tts-ald-bc.wav +0 -0
  70. rtpbench/corpus/tts-ald-long.wav +0 -0
  71. rtpbench/corpus/tts-ald-s03.wav +0 -0
  72. rtpbench/corpus/tts-ald-s07.wav +0 -0
  73. rtpbench/corpus/tts-ald-s11.wav +0 -0
  74. rtpbench/corpus/tts-cori-bc.wav +0 -0
  75. rtpbench/corpus/tts-cori-long.wav +0 -0
  76. rtpbench/corpus/tts-cori-s01.wav +0 -0
  77. rtpbench/corpus/tts-cori-s05.wav +0 -0
  78. rtpbench/corpus/tts-cori-s09.wav +0 -0
  79. rtpbench/corpus/tts-davefx-bc.wav +0 -0
  80. rtpbench/corpus/tts-davefx-long.wav +0 -0
  81. rtpbench/corpus/tts-davefx-s00.wav +0 -0
  82. rtpbench/corpus/tts-davefx-s04.wav +0 -0
  83. rtpbench/corpus/tts-davefx-s08.wav +0 -0
  84. rtpbench/corpus/tts-libritts0-bc.wav +0 -0
  85. rtpbench/corpus/tts-libritts0-long.wav +0 -0
  86. rtpbench/corpus/tts-libritts0-s02.wav +0 -0
  87. rtpbench/corpus/tts-libritts0-s06.wav +0 -0
  88. rtpbench/corpus/tts-libritts0-s10.wav +0 -0
  89. rtpbench/corpus/tts-libritts3-bc.wav +0 -0
  90. rtpbench/corpus/tts-libritts3-long.wav +0 -0
  91. rtpbench/corpus/tts-libritts3-s03.wav +0 -0
  92. rtpbench/corpus/tts-libritts3-s07.wav +0 -0
  93. rtpbench/corpus/tts-libritts3-s11.wav +0 -0
  94. rtpbench/corpus/tts-ljspeech-bc.wav +0 -0
  95. rtpbench/corpus/tts-ljspeech-long.wav +0 -0
  96. rtpbench/corpus/tts-ljspeech-s00.wav +0 -0
  97. rtpbench/corpus/tts-ljspeech-s04.wav +0 -0
  98. rtpbench/corpus/tts-ljspeech-s08.wav +0 -0
  99. rtpbench/corpus/tts-sharvardf-bc.wav +0 -0
  100. rtpbench/corpus/tts-sharvardf-long.wav +0 -0
  101. rtpbench/corpus/tts-sharvardf-s02.wav +0 -0
  102. rtpbench/corpus/tts-sharvardf-s06.wav +0 -0
  103. rtpbench/corpus/tts-sharvardf-s10.wav +0 -0
  104. rtpbench/corpus/tts-sharvardm-bc.wav +0 -0
  105. rtpbench/corpus/tts-sharvardm-long.wav +0 -0
  106. rtpbench/corpus/tts-sharvardm-s01.wav +0 -0
  107. rtpbench/corpus/tts-sharvardm-s05.wav +0 -0
  108. rtpbench/corpus/tts-sharvardm-s09.wav +0 -0
  109. rtpbench/detect.py +145 -0
  110. rtpbench/g711.py +82 -0
  111. rtpbench/mock/compose.yaml +18 -0
  112. rtpbench/pcap.py +106 -0
  113. rtpbench/progress.py +184 -0
  114. rtpbench/realtime.py +1015 -0
  115. rtpbench/report.py +450 -0
  116. rtpbench/rtp.py +166 -0
  117. rtpbench/sip.py +653 -0
  118. rtpbench/speech.py +100 -0
  119. rtpbench/speechcal.py +367 -0
  120. rtpbench/srtp.py +191 -0
  121. rtpbench/targets/aivc/aivc.ini +25 -0
  122. rtpbench/targets/aivc/compose.yaml +72 -0
  123. rtpbench/targets/aivc/constraints.txt +52 -0
  124. rtpbench/targets/aivc/opensips.cfg +45 -0
  125. rtpbench/targets/ava/ai-agent.yaml +275 -0
  126. rtpbench/targets/ava/ari.conf +7 -0
  127. rtpbench/targets/ava/compose.yaml +128 -0
  128. rtpbench/targets/ava/constraints.txt +75 -0
  129. rtpbench/targets/ava/extensions.conf +10 -0
  130. rtpbench/targets/ava/http.conf +4 -0
  131. rtpbench/targets/ava/pjsip.conf +19 -0
  132. rtpbench/targets/ava/websocket_client.conf +15 -0
  133. rtpbench/targets/livekit/agent.py +111 -0
  134. rtpbench/targets/livekit/compose.yaml +86 -0
  135. rtpbench/targets/livekit/constraints.txt +72 -0
  136. rtpbench/tls.py +284 -0
  137. rtpbench/uri/advertise.yaml +7 -0
  138. rtpbench/uri/network.yaml +14 -0
  139. rtpbench/voice.py +18 -0
  140. rtpbench-0.3.0.dist-info/METADATA +123 -0
  141. rtpbench-0.3.0.dist-info/RECORD +144 -0
  142. rtpbench-0.3.0.dist-info/WHEEL +4 -0
  143. rtpbench-0.3.0.dist-info/entry_points.txt +2 -0
  144. 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
+ }
@@ -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