blob: 2374e91950ba95c51ce321ad6a97a7241a11ef90 [file] [log] [blame]
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001/*
2 * (C) 2011-2012,2014 by Holger Hans Peter Freyther <zecke@selfish.org>
3 * (C) 2011-2012,2014 by On-Waves
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19#undef _GNU_SOURCE
20#define _GNU_SOURCE
21
Philipp Maier87bd9be2017-08-22 16:35:41 +020022#include <osmocom/mgcp/mgcp.h>
23#include <osmocom/mgcp/vty.h>
Neels Hofmeyr67793542017-09-08 04:25:16 +020024#include <osmocom/mgcp/mgcp_common.h>
Philipp Maier993ea6b2020-08-04 18:26:50 +020025#include <osmocom/mgcp/mgcp_conn.h>
26#include <osmocom/mgcp/mgcp_protocol.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020027#include <osmocom/mgcp/mgcp_stat.h>
28#include <osmocom/mgcp/mgcp_msg.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010029#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020030#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maierbc0346e2018-06-07 09:52:16 +020031#include <osmocom/mgcp/mgcp_sdp.h>
32#include <osmocom/mgcp/mgcp_codec.h>
Philipp Maierb3d14eb2021-05-20 14:18:52 +020033#include <osmocom/mgcp/mgcp_network.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020034
35#include <osmocom/core/application.h>
36#include <osmocom/core/talloc.h>
37#include <osmocom/core/utils.h>
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +020038#include <osmocom/core/socket.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020039#include <string.h>
40#include <limits.h>
41#include <dlfcn.h>
42#include <time.h>
43#include <math.h>
Neels Hofmeyrb861db92018-08-28 16:19:25 +020044#include <ctype.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020045
46char *strline_r(char *str, char **saveptr);
47
48const char *strline_test_data =
49 "one CR\r"
50 "two CR\r"
51 "\r"
52 "one CRLF\r\n"
53 "two CRLF\r\n"
Philipp Maier87bd9be2017-08-22 16:35:41 +020054 "\r\n" "one LF\n" "two LF\n" "\n" "mixed (4 lines)\r\r\n\n\r\n";
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020055
56#define EXPECTED_NUMBER_OF_LINES 13
57
58static void test_strline(void)
59{
60 char *save = NULL;
61 char *line;
62 char buf[2048];
63 int counter = 0;
64
65 osmo_strlcpy(buf, strline_test_data, sizeof(buf));
66
Philipp Maier87bd9be2017-08-22 16:35:41 +020067 for (line = mgcp_strline(buf, &save); line;
68 line = mgcp_strline(NULL, &save)) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020069 printf("line: '%s'\n", line);
70 counter++;
71 }
72
73 OSMO_ASSERT(counter == EXPECTED_NUMBER_OF_LINES);
74}
75
Philipp Maier12943ea2018-01-17 15:40:25 +010076#define AUEP1 "AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0\r\n"
Philipp Maierc66ab2c2020-06-02 20:55:34 +020077#define AUEP1_RET "500 158663169 FAIL\r\n"
Philipp Maier12943ea2018-01-17 15:40:25 +010078#define AUEP2 "AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020079#define AUEP2_RET "500 18983213 FAIL\r\n"
80#define EMPTY "\r\n"
81#define EMPTY_RET NULL
82#define SHORT "CRCX \r\n"
83#define SHORT_RET "510 000000 FAIL\r\n"
84
Philipp Maier12943ea2018-01-17 15:40:25 +010085#define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0\r\n"
Harald Welteabbb6b92017-12-28 13:13:50 +010086#define MDCX_ERR_RET "500 18983213 FAIL\r\n"
Philipp Maier12943ea2018-01-17 15:40:25 +010087#define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0\r\n"
Philipp Maierc66ab2c2020-06-02 20:55:34 +020088#define MDCX_RET "500 18983214 FAIL\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020089
Philipp Maier87bd9be2017-08-22 16:35:41 +020090#define MDCX3 \
91 "MDCX 18983215 1@mgw MGCP 1.0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +010092 "I: %s\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020093
Philipp Maier87bd9be2017-08-22 16:35:41 +020094#define MDCX3_RET \
95 "200 18983215 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +010096 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +020097 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +010098 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +020099 "s=-\r\n" \
100 "c=IN IP4 0.0.0.0\r\n" \
101 "t=0 0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100102 "m=audio 16002 RTP/AVP 97\r\n" \
103 "a=rtpmap:97 GSM-EFR/8000\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200104 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200105
Philipp Maier87bd9be2017-08-22 16:35:41 +0200106#define MDCX3A_RET \
107 "200 18983215 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100108 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200109 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100110 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200111 "s=-\r\n" \
112 "c=IN IP4 0.0.0.0\r\n" \
113 "t=0 0\r\n" \
114 "m=audio 16002 RTP/AVP 97\r\n" \
115 "a=rtpmap:97 GSM-EFR/8000\r\n" \
116 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200117
Philipp Maier87bd9be2017-08-22 16:35:41 +0200118#define MDCX3_FMTP_RET \
119 "200 18983215 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100120 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200121 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100122 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200123 "s=-\r\n" \
124 "c=IN IP4 0.0.0.0\r\n" \
125 "t=0 0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100126 "m=audio 16006 RTP/AVP 97\r\n" \
127 "a=rtpmap:97 GSM-EFR/8000\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200128 "a=fmtp:126 0/1/2\r\n" \
129 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200130
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200131#define MDCX4_ADDR0000 \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200132 "MDCX 18983216 1@mgw MGCP 1.0\r\n" \
133 "M: sendrecv\r" \
134 "C: 2\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100135 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200136 "L: p:20, a:AMR, nt:IN\r\n" \
137 "\n" \
138 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100139 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200140 "c=IN IP4 0.0.0.0\r\n" \
141 "t=0 0\r\n" \
142 "m=audio 4441 RTP/AVP 99\r\n" \
143 "a=rtpmap:99 AMR/8000\r\n" \
144 "a=ptime:40\r\n"
145
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200146#define MDCX4_ADDR0000_RET \
147 "527 18983216 FAIL\r\n"
148
149#define MDCX4 \
150 "MDCX 18983217 1@mgw MGCP 1.0\r\n" \
151 "M: sendrecv\r" \
152 "C: 2\r\n" \
153 "I: %s\r\n" \
154 "L: p:20, a:AMR, nt:IN\r\n" \
155 "\n" \
156 "v=0\r\n" \
157 "o=- %s 23 IN IP4 5.6.7.8\r\n" \
158 "c=IN IP4 5.6.7.8\r\n" \
159 "t=0 0\r\n" \
160 "m=audio 4441 RTP/AVP 99\r\n" \
161 "a=rtpmap:99 AMR/8000\r\n" \
162 "a=ptime:40\r\n"
163
Philipp Maier87bd9be2017-08-22 16:35:41 +0200164#define MDCX4_RET(Ident) \
165 "200 " Ident " OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100166 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200167 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100168 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200169 "s=-\r\n" \
170 "c=IN IP4 0.0.0.0\r\n" \
171 "t=0 0\r\n" \
172 "m=audio 16002 RTP/AVP 99\r\n" \
173 "a=rtpmap:99 AMR/8000\r\n" \
174 "a=ptime:40\r\n"
175
176#define MDCX4_RO_RET(Ident) \
177 "200 " Ident " OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100178 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200179 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100180 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200181 "s=-\r\n" \
182 "c=IN IP4 0.0.0.0\r\n" \
183 "t=0 0\r\n" \
Philipp Maierbc0346e2018-06-07 09:52:16 +0200184 "m=audio 16002 RTP/AVP 112\r\n" \
185 "a=rtpmap:112 AMR\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200186 "a=ptime:40\r\n"
187
188#define MDCX4_PT1 \
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200189 "MDCX 18983218 1@mgw MGCP 1.0\r\n" \
Pau Espin Pedrol17058482019-06-26 12:23:02 +0200190 "M: SENDRECV\r" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200191 "C: 2\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100192 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200193 "L: p:20-40, a:AMR, nt:IN\r\n" \
194 "\n" \
195 "v=0\r\n" \
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200196 "o=- %s 23 IN IP4 5.6.7.8\r\n" \
197 "c=IN IP4 5.6.7.8\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200198 "t=0 0\r\n" \
199 "m=audio 4441 RTP/AVP 99\r\n" \
200 "a=rtpmap:99 AMR/8000\r\n" \
201 "a=ptime:40\r\n"
202
203#define MDCX4_PT2 \
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200204 "MDCX 18983219 1@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200205 "M: sendrecv\r" \
206 "C: 2\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100207 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200208 "L: p:20-20, a:AMR, nt:IN\r\n" \
209 "\n" \
210 "v=0\r\n" \
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200211 "o=- %s 23 IN IP4 5.6.7.8\r\n" \
212 "c=IN IP4 5.6.7.8\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200213 "t=0 0\r\n" \
214 "m=audio 4441 RTP/AVP 99\r\n" \
215 "a=rtpmap:99 AMR/8000\r\n" \
216 "a=ptime:40\r\n"
217
218#define MDCX4_PT3 \
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200219 "MDCX 18983220 1@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200220 "M: sendrecv\r" \
221 "C: 2\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100222 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200223 "L: a:AMR, nt:IN\r\n" \
224 "\n" \
225 "v=0\r\n" \
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200226 "o=- %s 23 IN IP4 5.6.7.8\r\n" \
227 "c=IN IP4 5.6.7.8\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200228 "t=0 0\r\n" \
229 "m=audio 4441 RTP/AVP 99\r\n" \
230 "a=rtpmap:99 AMR/8000\r\n" \
231 "a=ptime:40\r\n"
232
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200233/* Test different upper/lower case in options */
234#define MDCX4_PT4 \
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200235 "MDCX 18983221 1@mgw MGCP 1.0\r\n" \
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200236 "m: sendrecv\r" \
237 "c: 2\r\n" \
238 "i: %s\r\n" \
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200239 "l: A:amr, NT:IN\r\n" \
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200240 "\n" \
241 "v=0\r\n" \
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200242 "o=- %s 23 IN IP4 5.6.7.8\r\n" \
243 "c=IN IP4 5.6.7.8\r\n" \
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200244 "t=0 0\r\n" \
245 "m=audio 4441 RTP/AVP 99\r\n" \
246 "a=rtpmap:99 AMR/8000\r\n" \
247 "a=ptime:40\r\n"
248
249#define MDCX4_SO \
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200250 "MDCX 18983222 1@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200251 "M: sendonly\r" \
252 "C: 2\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100253 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200254 "L: p:20, a:AMR, nt:IN\r\n" \
255 "\n" \
256 "v=0\r\n" \
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200257 "o=- %s 23 IN IP4 5.6.7.8\r\n" \
258 "c=IN IP4 5.6.7.8\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200259 "t=0 0\r\n" \
260 "m=audio 4441 RTP/AVP 99\r\n" \
261 "a=rtpmap:99 AMR/8000\r\n" \
262 "a=ptime:40\r\n"
263
264#define MDCX4_RO \
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200265 "MDCX 18983223 1@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200266 "M: recvonly\r" \
267 "C: 2\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100268 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200269 "L: p:20, a:AMR, nt:IN\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200270
Neels Hofmeyr5336f572018-09-03 22:05:48 +0200271#define MDCX_TOO_LONG_CI \
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200272 "MDCX 18983224 1@mgw MGCP 1.0\r\n" \
Neels Hofmeyr5336f572018-09-03 22:05:48 +0200273 "I: 123456789012345678901234567890123\n"
274
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200275#define MDCX_TOO_LONG_CI_RET "510 18983224 FAIL\r\n"
Neels Hofmeyr5336f572018-09-03 22:05:48 +0200276
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200277#define SHORT2 "CRCX 1"
278#define SHORT2_RET "510 000000 FAIL\r\n"
279#define SHORT3 "CRCX 1 1@mgw"
280#define SHORT4 "CRCX 1 1@mgw MGCP"
281#define SHORT5 "CRCX 1 1@mgw MGCP 1.0"
282
Philipp Maier87bd9be2017-08-22 16:35:41 +0200283#define CRCX \
284 "CRCX 2 1@mgw MGCP 1.0\r\n" \
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200285 "m: recvonly\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200286 "C: 2\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200287 "L: p:20\r\n" \
288 "\r\n" \
289 "v=0\r\n" \
290 "c=IN IP4 123.12.12.123\r\n" \
291 "m=audio 5904 RTP/AVP 97\r\n" \
292 "a=rtpmap:97 GSM-EFR/8000\r\n" \
293 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200294
Philipp Maier87bd9be2017-08-22 16:35:41 +0200295#define CRCX_RET \
296 "200 2 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100297 "I: %s\r\n" \
298 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200299 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100300 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200301 "s=-\r\n" \
302 "c=IN IP4 0.0.0.0\r\n" \
303 "t=0 0\r\n" \
304 "m=audio 16002 RTP/AVP 97\r\n" \
305 "a=rtpmap:97 GSM-EFR/8000\r\n" \
306 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200307
Philipp Maier87bd9be2017-08-22 16:35:41 +0200308#define CRCX_RET_NO_RTPMAP \
309 "200 2 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100310 "I: %s\r\n" \
311 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200312 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100313 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200314 "s=-\r\n" \
315 "c=IN IP4 0.0.0.0\r\n" \
316 "t=0 0\r\n" \
317 "m=audio 16002 RTP/AVP 97\r\n" \
318 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200319
Philipp Maier87bd9be2017-08-22 16:35:41 +0200320#define CRCX_FMTP_RET \
321 "200 2 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100322 "I: %s\r\n" \
323 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200324 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100325 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200326 "s=-\r\n" \
327 "c=IN IP4 0.0.0.0\r\n" \
328 "t=0 0\r\n" \
329 "m=audio 16006 RTP/AVP 97\r\n" \
330 "a=rtpmap:97 GSM-EFR/8000\r\n" \
331 "a=fmtp:126 0/1/2\r\n" \
332 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200333
Philipp Maier87bd9be2017-08-22 16:35:41 +0200334#define CRCX_ZYN \
335 "CRCX 2 1@mgw MGCP 1.0\r" \
336 "M: recvonly\r" \
337 "C: 2\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200338 "\n" \
339 "v=0\r" \
340 "c=IN IP4 123.12.12.123\r" \
341 "m=audio 5904 RTP/AVP 97\r" \
342 "a=rtpmap:97 GSM-EFR/8000\r"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200343
Philipp Maier87bd9be2017-08-22 16:35:41 +0200344#define CRCX_ZYN_RET \
345 "200 2 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100346 "I: %s\r\n" \
347 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200348 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100349 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200350 "s=-\r\n" \
351 "c=IN IP4 0.0.0.0\r\n" \
352 "t=0 0\r\n" \
353 "m=audio 16004 RTP/AVP 97\r\n" \
354 "a=rtpmap:97 GSM-EFR/8000\r\n" \
355 "a=ptime:20\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200356
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200357#define CRCX_X_OSMO_IGN \
358 "CRCX 2 1@mgw MGCP 1.0\r\n" \
359 "M: recvonly\r\n" \
360 "C: 2\r\n" \
361 "L: p:20\r\n" \
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200362 "X-Osmo-IGN: C foo\r\n" \
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200363 "\r\n" \
364 "v=0\r\n" \
365 "c=IN IP4 123.12.12.123\r\n" \
366 "m=audio 5904 RTP/AVP 97\r\n" \
367 "a=rtpmap:97 GSM-EFR/8000\r\n" \
368 "a=ptime:40\r\n"
369
370#define CRCX_X_OSMO_IGN_RET \
371 "200 2 OK\r\n" \
372 "I: %s\r\n" \
373 "\r\n" \
374 "v=0\r\n" \
375 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
376 "s=-\r\n" \
377 "c=IN IP4 0.0.0.0\r\n" \
378 "t=0 0\r\n" \
379 "m=audio 16010 RTP/AVP 97\r\n" \
380 "a=rtpmap:97 GSM-EFR/8000\r\n" \
381 "a=ptime:40\r\n"
382
Philipp Maier87bd9be2017-08-22 16:35:41 +0200383#define DLCX \
384 "DLCX 7 1@mgw MGCP 1.0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100385 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200386 "C: 2\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200387
Philipp Maier87bd9be2017-08-22 16:35:41 +0200388#define DLCX_RET \
389 "250 7 OK\r\n" \
Pau Espin Pedrol2da99a22018-02-20 13:11:17 +0100390 "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n"
391
392 #define DLCX_RET_OSMUX DLCX_RET \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200393 "X-Osmo-CP: EC TI=0, TO=0\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200394
Philipp Maier87bd9be2017-08-22 16:35:41 +0200395#define RQNT \
396 "RQNT 186908780 1@mgw MGCP 1.0\r\n" \
397 "X: B244F267488\r\n" \
398 "S: D/9\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200399
Philipp Maier87bd9be2017-08-22 16:35:41 +0200400#define RQNT2 \
401 "RQNT 186908781 1@mgw MGCP 1.0\r\n" \
402 "X: ADD4F26746F\r\n" \
403 "R: D/[0-9#*](N), G/ft, fxr/t38\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200404
405#define RQNT1_RET "200 186908780 OK\r\n"
406#define RQNT2_RET "200 186908781 OK\r\n"
407
Philipp Maier87bd9be2017-08-22 16:35:41 +0200408#define PTYPE_IGNORE 0 /* == default initializer */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200409#define PTYPE_NONE 128
410#define PTYPE_NYI PTYPE_NONE
411
Philipp Maier87bd9be2017-08-22 16:35:41 +0200412#define CRCX_MULT_1 \
413 "CRCX 2 1@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200414 "M: recvonly\r\n" \
415 "C: 2\r\n" \
416 "X\r\n" \
417 "L: p:20\r\n" \
418 "\r\n" \
419 "v=0\r\n" \
420 "c=IN IP4 123.12.12.123\r\n" \
421 "m=audio 5904 RTP/AVP 18 97\r\n" \
422 "a=rtpmap:18 G729/8000\r\n" \
423 "a=rtpmap:97 GSM-EFR/8000\r\n" \
424 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200425
Philipp Maier87bd9be2017-08-22 16:35:41 +0200426#define CRCX_MULT_2 \
427 "CRCX 2 2@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200428 "M: recvonly\r\n" \
429 "C: 2\r\n" \
430 "X\r\n" \
431 "L: p:20\r\n" \
432 "\r\n" \
433 "v=0\r\n" \
434 "c=IN IP4 123.12.12.123\r\n" \
435 "m=audio 5904 RTP/AVP 18 97 101\r\n" \
436 "a=rtpmap:18 G729/8000\r\n" \
437 "a=rtpmap:97 GSM-EFR/8000\r\n" \
438 "a=rtpmap:101 FOO/8000\r\n" \
439 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200440
Philipp Maier87bd9be2017-08-22 16:35:41 +0200441#define CRCX_MULT_3 \
442 "CRCX 2 3@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200443 "M: recvonly\r\n" \
444 "C: 2\r\n" \
445 "X\r\n" \
446 "L: p:20\r\n" \
447 "\r\n" \
448 "v=0\r\n" \
449 "c=IN IP4 123.12.12.123\r\n" \
450 "m=audio 5904 RTP/AVP\r\n" \
451 "a=rtpmap:18 G729/8000\r\n" \
452 "a=rtpmap:97 GSM-EFR/8000\r\n" \
453 "a=rtpmap:101 FOO/8000\r\n" \
454 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200455
Philipp Maier87bd9be2017-08-22 16:35:41 +0200456#define CRCX_MULT_4 \
457 "CRCX 2 4@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200458 "M: recvonly\r\n" \
459 "C: 2\r\n" \
460 "X\r\n" \
461 "L: p:20\r\n" \
462 "\r\n" \
463 "v=0\r\n" \
464 "c=IN IP4 123.12.12.123\r\n" \
465 "m=audio 5904 RTP/AVP 18\r\n" \
466 "a=rtpmap:18 G729/8000\r\n" \
467 "a=rtpmap:97 GSM-EFR/8000\r\n" \
468 "a=rtpmap:101 FOO/8000\r\n" \
469 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200470
471#define CRCX_MULT_GSM_EXACT \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200472 "CRCX 259260421 5@mgw MGCP 1.0\r\n" \
473 "C: 1355c6041e\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200474 "L: p:20, a:GSM, nt:IN\r\n" \
475 "M: recvonly\r\n" \
476 "\r\n" \
477 "v=0\r\n" \
478 "o=- 1439038275 1439038275 IN IP4 192.168.181.247\r\n" \
479 "s=-\r\nc=IN IP4 192.168.181.247\r\n" \
Philipp Maierbc0346e2018-06-07 09:52:16 +0200480 "t=0 0\r\nm=audio 29084 RTP/AVP 0 8 3 18 4 96 97 101\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200481 "a=rtpmap:0 PCMU/8000\r\n" \
482 "a=rtpmap:8 PCMA/8000\r\n" \
483 "a=rtpmap:3 gsm/8000\r\n" \
484 "a=rtpmap:18 G729/8000\r\n" \
485 "a=fmtp:18 annexb=no\r\n" \
486 "a=rtpmap:4 G723/8000\r\n" \
487 "a=rtpmap:96 iLBC/8000\r\n" \
488 "a=fmtp:96 mode=20\r\n" \
489 "a=rtpmap:97 iLBC/8000\r\n" \
490 "a=fmtp:97 mode=30\r\n" \
491 "a=rtpmap:101 telephone-event/8000\r\n" \
492 "a=fmtp:101 0-15\r\n" \
493 "a=recvonly\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200494
Philipp Maier87bd9be2017-08-22 16:35:41 +0200495#define MDCX_NAT_DUMMY \
496 "MDCX 23 5@mgw MGCP 1.0\r\n" \
497 "C: 1355c6041e\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100498 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200499 "\r\n" \
500 "c=IN IP4 8.8.8.8\r\n" \
Philipp Maierbc0346e2018-06-07 09:52:16 +0200501 "m=audio 16434 RTP/AVP 3\r\n"
502
503#define CRCX_NO_LCO_NO_SDP \
504 "CRCX 2 6@mgw MGCP 1.0\r\n" \
505 "M: recvonly\r\n" \
506 "C: 2\r\n"
507
Philipp Maier228e5912019-03-05 13:56:59 +0100508#define CRCX_AMR_WITH_FMTP \
509 "CRCX 2 7@mgw MGCP 1.0\r\n" \
510 "M: recvonly\r\n" \
511 "C: 2\r\n" \
512 "X\r\n" \
513 "L: p:20\r\n" \
514 "\r\n" \
515 "v=0\r\n" \
516 "c=IN IP4 123.12.12.123\r\n" \
517 "m=audio 5904 RTP/AVP 111\r\n" \
518 "a=rtpmap:111 AMR/8000/1\r\n" \
519 "a=ptime:20\r\n" \
520 "a=fmtp:111 mode-change-capability=2; octet-align=1\r\n" \
521
522#define CRCX_AMR_WITH_FMTP_RET \
523 "200 2 OK\r\n" \
524 "I: %s\r\n" \
525 "\r\n" \
526 "v=0\r\n" \
527 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
528 "s=-\r\n" \
529 "c=IN IP4 0.0.0.0\r\n" \
530 "t=0 0\r\n" \
531 "m=audio 16012 RTP/AVP 111\r\n" \
532 "a=rtpmap:111 AMR/8000/1\r\n" \
533 "a=fmtp:111 octet-align=1\r\n" \
534 "a=ptime:20\r\n"
535
Philipp Maierbc0346e2018-06-07 09:52:16 +0200536#define CRCX_NO_LCO_NO_SDP_RET \
537 "200 2 OK\r\n" \
538 "I: %s\r\n" \
539 "\r\n" \
540 "v=0\r\n" \
541 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
542 "s=-\r\n" \
543 "c=IN IP4 0.0.0.0\r\n" \
544 "t=0 0\r\n" \
545 "m=audio 16008 RTP/AVP 0\r\n" \
546 "a=ptime:20\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200547
548struct mgcp_test {
549 const char *name;
550 const char *req;
551 const char *exp_resp;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200552 int ptype;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200553 const char *extra_fmtp;
554};
555
556static const struct mgcp_test tests[] = {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200557 {"AUEP1", AUEP1, AUEP1_RET},
558 {"AUEP2", AUEP2, AUEP2_RET},
559 {"MDCX1", MDCX_WRONG_EP, MDCX_ERR_RET},
560 {"MDCX2", MDCX_UNALLOCATED, MDCX_RET},
561 {"CRCX", CRCX, CRCX_RET, 97},
562 {"MDCX3", MDCX3, MDCX3_RET, PTYPE_IGNORE},
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +0200563 {"MDCX4_ADDR000", MDCX4_ADDR0000, MDCX4_ADDR0000_RET},
564 {"MDCX4", MDCX4, MDCX4_RET("18983217"), 99},
565 {"MDCX4_PT1", MDCX4_PT1, MDCX4_RET("18983218"), 99},
566 {"MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983219"), 99},
567 {"MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983220"), 99},
568 {"MDCX4_PT4", MDCX4_PT4, MDCX4_RET("18983221"), 99},
569 {"MDCX4_SO", MDCX4_SO, MDCX4_RET("18983222"), 99},
570 {"MDCX4_RO", MDCX4_RO, MDCX4_RO_RET("18983223"), PTYPE_IGNORE},
Philipp Maier87bd9be2017-08-22 16:35:41 +0200571 {"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
572 {"CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET, 97},
573 {"EMPTY", EMPTY, EMPTY_RET},
574 {"SHORT1", SHORT, SHORT_RET},
575 {"SHORT2", SHORT2, SHORT2_RET},
576 {"SHORT3", SHORT3, SHORT2_RET},
577 {"SHORT4", SHORT4, SHORT2_RET},
578 {"RQNT1", RQNT, RQNT1_RET},
579 {"RQNT2", RQNT2, RQNT2_RET},
580 {"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
581 {"CRCX", CRCX, CRCX_FMTP_RET, 97,.extra_fmtp = "a=fmtp:126 0/1/2"},
582 {"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE,.extra_fmtp =
583 "a=fmtp:126 0/1/2"},
584 {"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
Philipp Maierbc0346e2018-06-07 09:52:16 +0200585 {"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200586 {"CRCX", CRCX_X_OSMO_IGN, CRCX_X_OSMO_IGN_RET, 97},
Neels Hofmeyr5336f572018-09-03 22:05:48 +0200587 {"MDCX_TOO_LONG_CI", MDCX_TOO_LONG_CI, MDCX_TOO_LONG_CI_RET},
Philipp Maier228e5912019-03-05 13:56:59 +0100588 {"CRCX", CRCX_AMR_WITH_FMTP, CRCX_AMR_WITH_FMTP_RET},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200589};
590
591static const struct mgcp_test retransmit[] = {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200592 {"CRCX", CRCX, CRCX_RET},
593 {"RQNT1", RQNT, RQNT1_RET},
594 {"RQNT2", RQNT2, RQNT2_RET},
595 {"MDCX3", MDCX3, MDCX3A_RET},
596 {"DLCX", DLCX, DLCX_RET},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200597};
598
Philipp Maierffd75e42017-11-22 11:44:50 +0100599static struct msgb *create_msg(const char *str, const char *conn_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200600{
601 struct msgb *msg;
Philipp Maierffd75e42017-11-22 11:44:50 +0100602 int len;
603
604 printf("creating message from statically defined input:\n");
605 printf("---------8<---------\n%s\n---------8<---------\n", str);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200606
607 msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
Philipp Maierffd75e42017-11-22 11:44:50 +0100608 if (conn_id && strlen(conn_id))
609 len = sprintf((char *)msg->data, str, conn_id, conn_id);
610 else
611 len = sprintf((char *)msg->data, "%s", str);
612
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200613 msg->l2h = msgb_put(msg, len);
614 return msg;
615}
616
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200617static int dummy_packets = 0;
618/* override and forward */
619ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200620 const struct sockaddr *dest_addr, socklen_t addrlen)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200621{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200622 uint32_t dest_host =
623 htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr);
624 int dest_port = htons(((struct sockaddr_in *)dest_addr)->sin_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200625
Philipp Maierb3d14eb2021-05-20 14:18:52 +0200626 if (len == sizeof(rtp_dummy_payload)
627 && memcmp(buf, rtp_dummy_payload, sizeof(rtp_dummy_payload)) == 0) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200628 fprintf(stderr,
629 "Dummy packet to 0x%08x:%d, msg length %zu\n%s\n\n",
630 dest_host, dest_port, len, osmo_hexdump(buf, len));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200631 dummy_packets += 1;
632 }
633
Pau Espin Pedrola24dcc62021-07-06 17:48:47 +0200634 /* Make sure address+port are valid */
635 OSMO_ASSERT(dest_host);
636 OSMO_ASSERT(dest_port);
637
Philipp Maier3d9b6562017-10-13 18:33:44 +0200638 return len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200639}
640
641static int64_t force_monotonic_time_us = -1;
642/* override and forward */
643int clock_gettime(clockid_t clk_id, struct timespec *tp)
644{
645 typedef int (*clock_gettime_t)(clockid_t clk_id, struct timespec *tp);
646 static clock_gettime_t real_clock_gettime = NULL;
647
648 if (!real_clock_gettime)
649 real_clock_gettime = dlsym(RTLD_NEXT, "clock_gettime");
650
651 if (clk_id == CLOCK_MONOTONIC && force_monotonic_time_us >= 0) {
652 tp->tv_sec = force_monotonic_time_us / 1000000;
653 tp->tv_nsec = (force_monotonic_time_us % 1000000) * 1000;
654 return 0;
655 }
656
657 return real_clock_gettime(clk_id, tp);
658}
659
Philipp Maier14b27a82020-06-02 20:15:30 +0200660static void mgcp_endpoints_release(struct mgcp_trunk *trunk)
Pau Espin Pedrold071a302019-09-19 17:39:31 +0200661{
662 int i;
Philipp Maier869b21c2020-07-03 16:04:16 +0200663 for (i = 0; i < trunk->number_endpoints; i++)
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200664 mgcp_endp_release(trunk->endpoints[i]);
Pau Espin Pedrold071a302019-09-19 17:39:31 +0200665}
666
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200667#define CONN_UNMODIFIED (0x1000)
668
669static void test_values(void)
670{
671 /* Check that NONE disables all output */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200672 OSMO_ASSERT((MGCP_CONN_NONE & MGCP_CONN_RECV_SEND) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200673
674 /* Check that LOOPBACK enables all output */
675 OSMO_ASSERT((MGCP_CONN_LOOPBACK & MGCP_CONN_RECV_SEND) ==
Philipp Maier87bd9be2017-08-22 16:35:41 +0200676 MGCP_CONN_RECV_SEND);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200677}
678
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200679/* Extract a connection ID from a response and return in conn_id;
680 * if there is none, return -EINVAL and leave conn_id unchanged. */
Philipp Maierffd75e42017-11-22 11:44:50 +0100681static int get_conn_id_from_response(uint8_t *resp, char *conn_id,
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200682 size_t conn_id_buflen)
Philipp Maierffd75e42017-11-22 11:44:50 +0100683{
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200684 const char *conn_id_start;
685 const char *conn_id_end;
686 int conn_id_len;
Philipp Maierffd75e42017-11-22 11:44:50 +0100687
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200688 const char *header_I = "\r\nI: ";
689 const char *header_o = "\r\no=- ";
Philipp Maierffd75e42017-11-22 11:44:50 +0100690
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200691 /* Try to get the conn_id from the 'I:' or 'o=-' parameter */
692 if ((conn_id_start = strstr((char *)resp, header_I))) {
693 conn_id_start += strlen(header_I);
694 conn_id_end = strstr(conn_id_start, "\r\n");
695 } else if ((conn_id_start = strstr((char *)resp, header_o))) {
696 conn_id_start += strlen(header_o);
697 conn_id_end = strchr(conn_id_start, ' ');
698 } else
699 return -EINVAL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100700
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200701 if (conn_id_end)
702 conn_id_len = conn_id_end - conn_id_start;
703 else
704 conn_id_len = strlen(conn_id_start);
705 OSMO_ASSERT(conn_id_len <= conn_id_buflen - 1);
Philipp Maier55295f72018-01-15 14:00:28 +0100706
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200707 /* A valid conn_id must at least contain one digit, and must
708 * not exceed a length of 32 digits */
709 OSMO_ASSERT(conn_id_len <= 32);
710 OSMO_ASSERT(conn_id_len > 0);
711
712 strncpy(conn_id, conn_id_start, conn_id_len);
713 conn_id[conn_id_len] = '\0';
714 return 0;
Philipp Maierffd75e42017-11-22 11:44:50 +0100715}
716
717/* Check response, automatically patch connection ID if needed */
718static int check_response(uint8_t *resp, const char *exp_resp)
719{
720 char exp_resp_patched[4096];
721 const char *exp_resp_ptr;
722 char conn_id[256];
723
724 printf("checking response:\n");
725
726 /* If the expected response is intened to be patched
727 * (%s placeholder inside) we will patch it with the
728 * connection identifier we just received from the
729 * real response. This is necessary because the CI
730 * is generated by the mgcp code on CRCX and we can
731 * not know it in advance */
732 if (strstr(exp_resp, "%s")) {
733 if (get_conn_id_from_response(resp, conn_id, sizeof(conn_id)) ==
734 0) {
735 sprintf(exp_resp_patched, exp_resp, conn_id, conn_id);
736 exp_resp_ptr = exp_resp_patched;
737 printf
738 ("using message with patched conn_id for comparison\n");
739 } else {
740 printf
741 ("patching conn_id failed, using message as statically defined for comparison\n");
742 exp_resp_ptr = exp_resp;
743 }
744 } else {
745 printf("using message as statically defined for comparison\n");
746 exp_resp_ptr = exp_resp;
747 }
748
749 if (strcmp((char *)resp, exp_resp_ptr) != 0) {
750 printf("Unexpected response, please check!\n");
751 printf
752 ("Got:\n---------8<---------\n%s\n---------8<---------\n\n",
753 resp);
754 printf
755 ("Expected:\n---------8<---------\n%s\n---------8<---------\n",
756 exp_resp_ptr);
757 return -EINVAL;
758 }
759
760 printf("Response matches our expectations.\n");
761 return 0;
762}
763
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200764static void test_messages(void)
765{
766 struct mgcp_config *cfg;
767 struct mgcp_endpoint *endp;
Philipp Maierd19de2e2020-06-03 13:55:33 +0200768 struct mgcp_trunk *trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200769 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200770 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100771 char last_conn_id[256];
Philipp Maier7df419b2017-12-04 17:11:42 +0100772 int rc;
Philipp Maier39889e42021-08-04 17:42:57 +0200773 char *last_endpoint = mgcp_debug_get_last_endpoint_name();
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200774
775 cfg = mgcp_config_alloc();
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200776 trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200777
Philipp Maier889fe7f2020-07-06 17:44:12 +0200778 trunk->v.vty_number_endpoints = 64;
779 mgcp_trunk_equip(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200780
Philipp Maierffd75e42017-11-22 11:44:50 +0100781 memset(last_conn_id, 0, sizeof(last_conn_id));
782
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200783 for (i = 0; i < ARRAY_SIZE(tests); i++) {
784 const struct mgcp_test *t = &tests[i];
785 struct msgb *inp;
786 struct msgb *msg;
787
Philipp Maierffd75e42017-11-22 11:44:50 +0100788 printf("\n================================================\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200789 printf("Testing %s\n", t->name);
790
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200791 dummy_packets = 0;
792
Philipp Maierd19de2e2020-06-03 13:55:33 +0200793 osmo_talloc_replace_string(cfg, &trunk->audio_fmtp_extra,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200794 t->extra_fmtp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200795
Philipp Maierffd75e42017-11-22 11:44:50 +0100796 inp = create_msg(t->req, last_conn_id);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200797 msg = mgcp_handle_message(cfg, inp);
798 msgb_free(inp);
799 if (!t->exp_resp) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200800 if (msg) {
801 printf("%s failed '%s'\n", t->name,
802 (char *)msg->data);
803 OSMO_ASSERT(false);
804 }
Philipp Maierffd75e42017-11-22 11:44:50 +0100805 } else if (check_response(msg->data, t->exp_resp) != 0) {
806 printf("%s failed.\n", t->name);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200807 OSMO_ASSERT(false);
808 }
Philipp Maierffd75e42017-11-22 11:44:50 +0100809
Philipp Maier7df419b2017-12-04 17:11:42 +0100810 if (msg) {
811 rc = get_conn_id_from_response(msg->data, last_conn_id,
812 sizeof(last_conn_id));
Neels Hofmeyr08e07042018-08-28 16:22:14 +0200813 if (rc == 0)
Philipp Maier7df419b2017-12-04 17:11:42 +0100814 printf("(response contains a connection id)\n");
815 else
816 printf("(response does not contain a connection id)\n");
817 }
Philipp Maierffd75e42017-11-22 11:44:50 +0100818
Philipp Maiera330b862017-12-04 17:16:16 +0100819 if (msg)
820 msgb_free(msg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200821
822 if (dummy_packets)
823 printf("Dummy packets: %d\n", dummy_packets);
824
Philipp Maier37a808c2020-07-03 15:48:31 +0200825 if (last_endpoint[0] != '\0') {
826 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
827 OSMO_ASSERT(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200828
Philipp Maier01d24a32017-11-21 17:26:09 +0100829 conn = mgcp_conn_get_rtp(endp, "1");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200830 if (conn) {
831 OSMO_ASSERT(conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200832
Philipp Maier87bd9be2017-08-22 16:35:41 +0200833 if (conn->end.packet_duration_ms != -1)
834 printf("Detected packet duration: %d\n",
835 conn->end.packet_duration_ms);
836 else
837 printf("Packet duration not set\n");
838 if (endp->local_options.pkt_period_min ||
839 endp->local_options.pkt_period_max)
840 printf
Oliver Smith169d50e2023-01-24 13:12:54 +0100841 ("Requested packetization period: "
Philipp Maier87bd9be2017-08-22 16:35:41 +0200842 "%d-%d\n",
843 endp->local_options.pkt_period_min,
844 endp->
845 local_options.pkt_period_max);
846 else
847 printf
848 ("Requested packetization period not set\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200849
Philipp Maier87bd9be2017-08-22 16:35:41 +0200850 if ((conn->conn->mode & CONN_UNMODIFIED) == 0) {
851 printf("Connection mode: %d:%s%s%s%s\n",
852 conn->conn->mode,
853 !conn->conn->mode ? " NONE" : "",
854 conn->conn->mode & MGCP_CONN_SEND_ONLY
855 ? " SEND" : "",
856 conn->conn->mode & MGCP_CONN_RECV_ONLY
857 ? " RECV" : "",
858 conn->conn->mode & MGCP_CONN_LOOPBACK
859 & ~MGCP_CONN_RECV_SEND
860 ? " LOOP" : "");
861 fprintf(stderr,
862 "RTP output %sabled, NET output %sabled\n",
863 conn->end.output_enabled
864 ? "en" : "dis",
865 conn->end.output_enabled
866 ? "en" : "dis");
867 } else
868 printf("Connection mode not set\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200869
Philipp Maier87bd9be2017-08-22 16:35:41 +0200870 OSMO_ASSERT(conn->end.output_enabled
Pau Espin Pedrol2c401642021-12-24 14:48:26 +0100871 == !!(conn->conn->mode & MGCP_CONN_SEND_ONLY));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200872
873 conn->conn->mode |= CONN_UNMODIFIED;
874
875 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200876 endp->local_options.pkt_period_min = 0;
877 endp->local_options.pkt_period_max = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200878 }
879
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200880 /* Check detected payload type */
Philipp Maierffd75e42017-11-22 11:44:50 +0100881 if (conn && t->ptype != PTYPE_IGNORE) {
Philipp Maier37a808c2020-07-03 15:48:31 +0200882 OSMO_ASSERT(last_endpoint[0] != '\0');
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200883
Philipp Maier37a808c2020-07-03 15:48:31 +0200884 fprintf(stderr, "endpoint:%s: "
Philipp Maier87bd9be2017-08-22 16:35:41 +0200885 "payload type %d (expected %d)\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200886 last_endpoint,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200887 conn->end.codec->payload_type, t->ptype);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200888
Philipp Maier87bd9be2017-08-22 16:35:41 +0200889 if (t->ptype != PTYPE_IGNORE)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200890 OSMO_ASSERT(conn->end.codec->payload_type ==
Philipp Maier87bd9be2017-08-22 16:35:41 +0200891 t->ptype);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200892
893 /* Reset them again for next test */
Philipp Maierbc0346e2018-06-07 09:52:16 +0200894 conn->end.codec->payload_type = PTYPE_NONE;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200895 }
896 }
897
Philipp Maierd19de2e2020-06-03 13:55:33 +0200898 mgcp_endpoints_release(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200899 talloc_free(cfg);
900}
901
902static void test_retransmission(void)
903{
904 struct mgcp_config *cfg;
Philipp Maierd19de2e2020-06-03 13:55:33 +0200905 struct mgcp_trunk *trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200906 int i;
Philipp Maierffd75e42017-11-22 11:44:50 +0100907 char last_conn_id[256];
Philipp Maier23b8e292017-12-04 16:48:45 +0100908 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200909
910 cfg = mgcp_config_alloc();
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200911 trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200912
Philipp Maier889fe7f2020-07-06 17:44:12 +0200913 trunk->v.vty_number_endpoints = 64;
914 mgcp_trunk_equip(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200915
Philipp Maierffd75e42017-11-22 11:44:50 +0100916 memset(last_conn_id, 0, sizeof(last_conn_id));
917
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200918 for (i = 0; i < ARRAY_SIZE(retransmit); i++) {
919 const struct mgcp_test *t = &retransmit[i];
920 struct msgb *inp;
921 struct msgb *msg;
922
Philipp Maierffd75e42017-11-22 11:44:50 +0100923 printf("\n================================================\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200924 printf("Testing %s\n", t->name);
925
Philipp Maierffd75e42017-11-22 11:44:50 +0100926 inp = create_msg(t->req, last_conn_id);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200927 msg = mgcp_handle_message(cfg, inp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200928
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200929 msgb_free(inp);
Philipp Maier7cedfd72017-12-04 16:49:12 +0100930 if (msg && check_response(msg->data, t->exp_resp) != 0) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200931 printf("%s failed '%s'\n", t->name, (char *)msg->data);
932 OSMO_ASSERT(false);
933 }
Philipp Maierffd75e42017-11-22 11:44:50 +0100934
Philipp Maier23b8e292017-12-04 16:48:45 +0100935 if (msg && strcmp(t->name, "CRCX") == 0) {
936 rc = get_conn_id_from_response(msg->data, last_conn_id,
937 sizeof(last_conn_id));
938 OSMO_ASSERT(rc == 0);
939 }
Philipp Maierffd75e42017-11-22 11:44:50 +0100940
Philipp Maier7cedfd72017-12-04 16:49:12 +0100941 if (msg)
942 msgb_free(msg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200943
944 /* Retransmit... */
945 printf("Re-transmitting %s\n", t->name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100946 inp = create_msg(t->req, last_conn_id);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200947 msg = mgcp_handle_message(cfg, inp);
948 msgb_free(inp);
Philipp Maierffd75e42017-11-22 11:44:50 +0100949 if (check_response(msg->data, t->exp_resp) != 0) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200950 printf("%s failed '%s'\n", t->name, (char *)msg->data);
951 OSMO_ASSERT(false);
952 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200953 msgb_free(msg);
954 }
955
Philipp Maierd19de2e2020-06-03 13:55:33 +0200956 mgcp_endpoints_release(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200957 talloc_free(cfg);
958}
959
960static int rqnt_cb(struct mgcp_endpoint *endp, char _tone)
961{
962 ptrdiff_t tone = _tone;
Ericfbf78d12021-08-23 22:31:39 +0200963 endp->trunk->cfg->data = (void *)tone;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200964 return 0;
965}
966
967static void test_rqnt_cb(void)
968{
969 struct mgcp_config *cfg;
Philipp Maierd19de2e2020-06-03 13:55:33 +0200970 struct mgcp_trunk *trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200971 struct msgb *inp, *msg;
Philipp Maierffd75e42017-11-22 11:44:50 +0100972 char conn_id[256];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200973
974 cfg = mgcp_config_alloc();
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200975 trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200976 cfg->rqnt_cb = rqnt_cb;
977
Philipp Maier889fe7f2020-07-06 17:44:12 +0200978 trunk->v.vty_number_endpoints = 64;
979 mgcp_trunk_equip(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200980
Philipp Maierffd75e42017-11-22 11:44:50 +0100981 inp = create_msg(CRCX, NULL);
982 msg = mgcp_handle_message(cfg, inp);
983 OSMO_ASSERT(msg);
984 OSMO_ASSERT(get_conn_id_from_response(msg->data, conn_id,
985 sizeof(conn_id)) == 0);
986 msgb_free(msg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200987 msgb_free(inp);
988
989 /* send the RQNT and check for the CB */
Philipp Maierffd75e42017-11-22 11:44:50 +0100990 inp = create_msg(RQNT, conn_id);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200991 msg = mgcp_handle_message(cfg, inp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200992 if (strncmp((const char *)msg->l2h, "200", 3) != 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200993 printf("FAILED: message is not 200. '%s'\n", msg->l2h);
994 abort();
995 }
996
Philipp Maier87bd9be2017-08-22 16:35:41 +0200997 if (cfg->data != (void *)'9') {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200998 printf("FAILED: callback not called: %p\n", cfg->data);
999 abort();
1000 }
1001
1002 msgb_free(msg);
1003 msgb_free(inp);
1004
Philipp Maierffd75e42017-11-22 11:44:50 +01001005 inp = create_msg(DLCX, conn_id);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001006 msgb_free(mgcp_handle_message(cfg, inp));
1007 msgb_free(inp);
Philipp Maierd19de2e2020-06-03 13:55:33 +02001008 mgcp_endpoints_release(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001009 talloc_free(cfg);
1010}
1011
1012struct pl_test {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001013 int cycles;
1014 uint16_t base_seq;
1015 uint16_t max_seq;
1016 uint32_t packets;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001017
Philipp Maier87bd9be2017-08-22 16:35:41 +02001018 uint32_t expected;
1019 int loss;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001020};
1021
1022static const struct pl_test pl_test_dat[] = {
1023 /* basic.. just one package */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001024 {.cycles = 0,.base_seq = 0,.max_seq = 0,.packets = 1,.expected =
1025 1,.loss = 0},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001026 /* some packages and a bit of loss */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001027 {.cycles = 0,.base_seq = 0,.max_seq = 100,.packets = 100,.expected =
1028 101,.loss = 1},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001029 /* wrap around */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001030 {.cycles = 1 << 16,.base_seq = 0xffff,.max_seq = 2,.packets =
1031 4,.expected = 4,.loss = 0},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001032 /* min loss */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001033 {.cycles = 0,.base_seq = 0,.max_seq = 0,.packets = UINT_MAX,.expected =
1034 1,.loss = INT_MIN},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001035 /* max loss, with wrap around on expected max */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001036 {.cycles = INT_MAX,.base_seq = 0,.max_seq = UINT16_MAX,.packets =
1037 0,.expected = ((uint32_t) (INT_MAX) + UINT16_MAX + 1),.loss = INT_MAX},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001038};
1039
1040static void test_packet_loss_calc(void)
1041{
1042 int i;
Philipp Maiercede2a42018-07-03 14:14:21 +02001043 struct mgcp_endpoint endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001044 struct mgcp_endpoint *endpoints[1];
Philipp Maier124a3e02021-07-26 11:17:15 +02001045 struct mgcp_config *cfg;
1046 struct mgcp_trunk *trunk;
Philipp Maiercede2a42018-07-03 14:14:21 +02001047
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001048 printf("Testing packet loss calculation.\n");
1049
Philipp Maiercede2a42018-07-03 14:14:21 +02001050 memset(&endp, 0, sizeof(endp));
Philipp Maier124a3e02021-07-26 11:17:15 +02001051 cfg = mgcp_config_alloc();
1052 trunk = mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Philipp Maiercede2a42018-07-03 14:14:21 +02001053 endp.type = &ep_typeset.rtp;
Philipp Maier124a3e02021-07-26 11:17:15 +02001054 trunk->v.vty_number_endpoints = 1;
1055 trunk->endpoints = endpoints;
1056 trunk->endpoints[0] = &endp;
1057 endp.trunk = trunk;
Philipp Maiercede2a42018-07-03 14:14:21 +02001058 INIT_LLIST_HEAD(&endp.conns);
1059
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001060 for (i = 0; i < ARRAY_SIZE(pl_test_dat); ++i) {
1061 uint32_t expected;
1062 int loss;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001063
Philipp Maiercede2a42018-07-03 14:14:21 +02001064 struct mgcp_conn_rtp *conn = NULL;
1065 struct mgcp_conn *_conn = NULL;
1066 struct mgcp_rtp_state *state;
1067 struct rate_ctr *packets_rx;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001068
Philipp Maiercede2a42018-07-03 14:14:21 +02001069 _conn =
1070 mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
1071 "test-connection");
1072 conn = mgcp_conn_get_rtp(&endp, _conn->id);
1073 state = &conn->state;
Pau Espin Pedroldaf5bce2022-09-22 19:14:24 +02001074 packets_rx = rate_ctr_group_get_ctr(conn->ctrg, RTP_PACKETS_RX_CTR);
Philipp Maiercede2a42018-07-03 14:14:21 +02001075
1076 state->stats.initialized = 1;
1077 state->stats.base_seq = pl_test_dat[i].base_seq;
1078 state->stats.max_seq = pl_test_dat[i].max_seq;
1079 state->stats.cycles = pl_test_dat[i].cycles;
1080
1081 packets_rx->current = pl_test_dat[i].packets;
1082 calc_loss(conn, &expected, &loss);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001083
Philipp Maier87bd9be2017-08-22 16:35:41 +02001084 if (loss != pl_test_dat[i].loss
1085 || expected != pl_test_dat[i].expected) {
1086 printf
1087 ("FAIL: Wrong exp/loss at idx(%d) Loss(%d vs. %d) Exp(%u vs. %u)\n",
1088 i, loss, pl_test_dat[i].loss, expected,
1089 pl_test_dat[i].expected);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001090 }
Philipp Maiercede2a42018-07-03 14:14:21 +02001091
1092 mgcp_conn_free_all(&endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001093 }
Philipp Maiercede2a42018-07-03 14:14:21 +02001094
Philipp Maier124a3e02021-07-26 11:17:15 +02001095 talloc_free(trunk);
1096 talloc_free(cfg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001097}
1098
Philipp Maier87bd9be2017-08-22 16:35:41 +02001099int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os,
1100 uint32_t *pr, uint32_t *_or, int *loss,
1101 uint32_t *jitter)
1102{
1103 char *line, *save;
1104 int rc;
1105
1106 /* initialize with bad values */
1107 *ps = *os = *pr = *_or = *jitter = UINT_MAX;
1108 *loss = INT_MAX;
1109
1110 line = strtok_r((char *)msg->l2h, "\r\n", &save);
1111 if (!line)
1112 return -1;
1113
1114 /* this can only parse the message that is created above... */
1115 for_each_non_empty_line(line, save) {
1116 switch (line[0]) {
1117 case 'P':
1118 rc = sscanf(line,
1119 "P: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
1120 ps, os, pr, _or, loss, jitter);
1121 return rc == 6 ? 0 : -1;
1122 }
1123 }
1124
1125 return -1;
1126}
1127
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001128static void test_mgcp_stats(void)
1129{
1130 printf("Testing stat parsing\n");
1131
1132 uint32_t bps, bos, pr, _or, jitter;
1133 struct msgb *msg;
1134 int loss;
1135 int rc;
1136
Philipp Maierffd75e42017-11-22 11:44:50 +01001137 msg = create_msg(DLCX_RET, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001138 rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter);
1139 printf("Parsing result: %d\n", rc);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001140 if (bps != 0 || bos != 0 || pr != 0 || _or != 0 || loss != 0
1141 || jitter != 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001142 printf("FAIL: Parsing failed1.\n");
1143 msgb_free(msg);
1144
Philipp Maier87bd9be2017-08-22 16:35:41 +02001145 msg =
1146 create_msg
Philipp Maierffd75e42017-11-22 11:44:50 +01001147 ("250 7 OK\r\nP: PS=10, OS=20, PR=30, OR=40, PL=-3, JI=40\r\n", NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001148 rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter);
1149 printf("Parsing result: %d\n", rc);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001150 if (bps != 10 || bos != 20 || pr != 30 || _or != 40 || loss != -3
1151 || jitter != 40)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001152 printf("FAIL: Parsing failed2.\n");
1153 msgb_free(msg);
1154}
1155
1156struct rtp_packet_info {
1157 float txtime;
1158 int len;
1159 char *data;
1160};
1161
1162struct rtp_packet_info test_rtp_packets1[] = {
1163 /* RTP: SeqNo=0, TS=0 */
1164 {0.000000, 20, "\x80\x62\x00\x00\x00\x00\x00\x00\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001165 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001166 /* RTP: SeqNo=1, TS=160 */
1167 {0.020000, 20, "\x80\x62\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001168 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001169 /* RTP: SeqNo=2, TS=320 */
1170 {0.040000, 20, "\x80\x62\x00\x02\x00\x00\x01\x40\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001171 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001172 /* Repeat RTP timestamp: */
1173 /* RTP: SeqNo=3, TS=320 */
1174 {0.060000, 20, "\x80\x62\x00\x03\x00\x00\x01\x40\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001175 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001176 /* RTP: SeqNo=4, TS=480 */
1177 {0.080000, 20, "\x80\x62\x00\x04\x00\x00\x01\xE0\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001178 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001179 /* RTP: SeqNo=5, TS=640 */
1180 {0.100000, 20, "\x80\x62\x00\x05\x00\x00\x02\x80\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001181 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001182 /* Double skip RTP timestamp (delta = 2*160): */
1183 /* RTP: SeqNo=6, TS=960 */
1184 {0.120000, 20, "\x80\x62\x00\x06\x00\x00\x03\xC0\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001185 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001186 /* RTP: SeqNo=7, TS=1120 */
1187 {0.140000, 20, "\x80\x62\x00\x07\x00\x00\x04\x60\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001188 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001189 /* RTP: SeqNo=8, TS=1280 */
1190 {0.160000, 20, "\x80\x62\x00\x08\x00\x00\x05\x00\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001191 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001192 /* Non 20ms RTP timestamp (delta = 120): */
1193 /* RTP: SeqNo=9, TS=1400 */
1194 {0.180000, 20, "\x80\x62\x00\x09\x00\x00\x05\x78\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001195 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001196 /* RTP: SeqNo=10, TS=1560 */
1197 {0.200000, 20, "\x80\x62\x00\x0A\x00\x00\x06\x18\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001198 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001199 /* RTP: SeqNo=11, TS=1720 */
1200 {0.220000, 20, "\x80\x62\x00\x0B\x00\x00\x06\xB8\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001201 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001202 /* SSRC changed to 0x10203040, RTP timestamp jump */
1203 /* RTP: SeqNo=12, TS=34688 */
1204 {0.240000, 20, "\x80\x62\x00\x0C\x00\x00\x87\x80\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001205 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001206 /* RTP: SeqNo=13, TS=34848 */
1207 {0.260000, 20, "\x80\x62\x00\x0D\x00\x00\x88\x20\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001208 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001209 /* RTP: SeqNo=14, TS=35008 */
1210 {0.280000, 20, "\x80\x62\x00\x0E\x00\x00\x88\xC0\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001211 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001212 /* Non 20ms RTP timestamp (delta = 120): */
1213 /* RTP: SeqNo=15, TS=35128 */
1214 {0.300000, 20, "\x80\x62\x00\x0F\x00\x00\x89\x38\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001215 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001216 /* RTP: SeqNo=16, TS=35288 */
1217 {0.320000, 20, "\x80\x62\x00\x10\x00\x00\x89\xD8\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001218 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001219 /* RTP: SeqNo=17, TS=35448 */
1220 {0.340000, 20, "\x80\x62\x00\x11\x00\x00\x8A\x78\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001221 "\x01\x23\x45\x67\x8A\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001222 /* SeqNo increment by 2, RTP timestamp delta = 320: */
1223 /* RTP: SeqNo=19, TS=35768 */
1224 {0.360000, 20, "\x80\x62\x00\x13\x00\x00\x8B\xB8\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001225 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001226 /* RTP: SeqNo=20, TS=35928 */
1227 {0.380000, 20, "\x80\x62\x00\x14\x00\x00\x8C\x58\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001228 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001229 /* RTP: SeqNo=21, TS=36088 */
1230 {0.380000, 20, "\x80\x62\x00\x15\x00\x00\x8C\xF8\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001231 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001232 /* Repeat last packet */
1233 /* RTP: SeqNo=21, TS=36088 */
1234 {0.400000, 20, "\x80\x62\x00\x15\x00\x00\x8C\xF8\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001235 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001236 /* RTP: SeqNo=22, TS=36248 */
1237 {0.420000, 20, "\x80\x62\x00\x16\x00\x00\x8D\x98\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001238 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001239 /* RTP: SeqNo=23, TS=36408 */
1240 {0.440000, 20, "\x80\x62\x00\x17\x00\x00\x8E\x38\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001241 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001242 /* Don't increment SeqNo but increment timestamp by 160 */
1243 /* RTP: SeqNo=23, TS=36568 */
1244 {0.460000, 20, "\x80\x62\x00\x17\x00\x00\x8E\xD8\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001245 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001246 /* RTP: SeqNo=24, TS=36728 */
1247 {0.480000, 20, "\x80\x62\x00\x18\x00\x00\x8F\x78\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001248 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001249 /* RTP: SeqNo=25, TS=36888 */
1250 {0.500000, 20, "\x80\x62\x00\x19\x00\x00\x90\x18\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001251 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001252 /* SSRC changed to 0x50607080, RTP timestamp jump, Delay of 1.5s,
1253 * SeqNo jump */
1254 /* RTP: SeqNo=1000, TS=160000 */
1255 {2.000000, 20, "\x80\x62\x03\xE8\x00\x02\x71\x00\x50\x60\x70\x80"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001256 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001257 /* RTP: SeqNo=1001, TS=160160 */
1258 {2.020000, 20, "\x80\x62\x03\xE9\x00\x02\x71\xA0\x50\x60\x70\x80"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001259 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001260 /* RTP: SeqNo=1002, TS=160320 */
1261 {2.040000, 20, "\x80\x62\x03\xEA\x00\x02\x72\x40\x50\x60\x70\x80"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001262 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Pau Espin Pedrolb066bd02021-07-07 13:41:19 +02001263 /* RTP: SeqNo=1003, TS=180320, Marker */
1264 {2.060000, 20, "\x80\xE2\x03\xEB\x00\x02\xC0\x60\x50\x60\x70\x80"
1265 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
1266 /* RTP: SeqNo=1004, TS=180480 */
1267 {2.080000, 20, "\x80\x62\x03\xEC\x00\x02\xC1\x00\x50\x60\x70\x80"
1268 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
1269 /* RTP: SeqNo=1005, TS=180480, 10ms too late */
1270 {2.110000, 20, "\x80\x62\x03\xED\x00\x02\xC1\xA0\x50\x60\x70\x80"
1271 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001272};
1273
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001274static void test_packet_error_detection(int patch_ssrc, int patch_ts)
1275{
1276 int i;
1277
Philipp Maier124a3e02021-07-26 11:17:15 +02001278 struct mgcp_trunk *trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001279 struct mgcp_endpoint endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001280 struct mgcp_endpoint *endpoints[1];
Philipp Maier124a3e02021-07-26 11:17:15 +02001281 struct mgcp_config *cfg;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001282 struct mgcp_rtp_state state;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001283 struct mgcp_rtp_end *rtp;
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +02001284 struct osmo_sockaddr addr = { 0 };
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001285 uint32_t last_ssrc = 0;
1286 uint32_t last_timestamp = 0;
1287 uint32_t last_seqno = 0;
Philipp Maier9e1d1642018-05-09 16:26:34 +02001288 uint64_t last_in_ts_err_cnt = 0;
1289 uint64_t last_out_ts_err_cnt = 0;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001290 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +01001291 struct mgcp_conn *_conn = NULL;
Philipp Maier9e1d1642018-05-09 16:26:34 +02001292 struct rate_ctr test_ctr_in;
1293 struct rate_ctr test_ctr_out;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001294
1295 printf("Testing packet error detection%s%s.\n",
1296 patch_ssrc ? ", patch SSRC" : "",
1297 patch_ts ? ", patch timestamps" : "");
1298
Philipp Maier124a3e02021-07-26 11:17:15 +02001299 cfg = mgcp_config_alloc();
1300 trunk = mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001301 memset(&endp, 0, sizeof(endp));
1302 memset(&state, 0, sizeof(state));
1303
Philipp Maier9e1d1642018-05-09 16:26:34 +02001304 memset(&test_ctr_in, 0, sizeof(test_ctr_in));
1305 memset(&test_ctr_out, 0, sizeof(test_ctr_out));
1306 state.in_stream.err_ts_ctr = &test_ctr_in;
1307 state.out_stream.err_ts_ctr = &test_ctr_out;
1308
Philipp Maier87bd9be2017-08-22 16:35:41 +02001309 endp.type = &ep_typeset.rtp;
1310
Philipp Maier124a3e02021-07-26 11:17:15 +02001311 trunk->v.vty_number_endpoints = 1;
1312 trunk->endpoints = endpoints;
1313 trunk->endpoints[0] = &endp;
1314 trunk->force_constant_ssrc = patch_ssrc;
1315 trunk->force_aligned_timing = patch_ts;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001316
Philipp Maier124a3e02021-07-26 11:17:15 +02001317 endp.trunk = trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001318
Philipp Maier87bd9be2017-08-22 16:35:41 +02001319 INIT_LLIST_HEAD(&endp.conns);
Philipp Maierffd75e42017-11-22 11:44:50 +01001320 _conn = mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
1321 "test-connection");
1322 OSMO_ASSERT(_conn);
1323 conn = mgcp_conn_get_rtp(&endp, _conn->id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001324 OSMO_ASSERT(conn);
1325
1326 rtp = &conn->end;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001327
Philipp Maier228e5912019-03-05 13:56:59 +01001328 OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1", NULL) == 0);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001329 rtp->codec = &rtp->codecs[0];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001330
1331 for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
1332 struct rtp_packet_info *info = test_rtp_packets1 + i;
Neels Hofmeyr51b42ff2020-06-19 01:34:42 +02001333 struct msgb *msg = msgb_alloc(4096, __func__);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001334
1335 force_monotonic_time_us = round(1000000.0 * info->txtime);
1336
Neels Hofmeyr51b42ff2020-06-19 01:34:42 +02001337 OSMO_ASSERT(info->len <= msgb_tailroom(msg));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001338 OSMO_ASSERT(info->len >= 0);
Neels Hofmeyr51b42ff2020-06-19 01:34:42 +02001339 msg->l3h = msgb_put(msg, info->len);
1340 memcpy((char*)msgb_l3(msg), info->data, info->len);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001341 mgcp_rtp_end_config(&endp, 1, rtp);
1342
Neels Hofmeyr51b42ff2020-06-19 01:34:42 +02001343 mgcp_patch_and_count(&endp, &state, rtp, &addr, msg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001344
1345 if (state.out_stream.ssrc != last_ssrc) {
1346 printf("Output SSRC changed to %08x\n",
1347 state.out_stream.ssrc);
1348 last_ssrc = state.out_stream.ssrc;
1349 }
1350
1351 printf("In TS: %d, dTS: %d, Seq: %d\n",
1352 state.in_stream.last_timestamp,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001353 state.in_stream.last_tsdelta, state.in_stream.last_seq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001354
1355 printf("Out TS change: %d, dTS: %d, Seq change: %d, "
Philipp Maier9e1d1642018-05-09 16:26:34 +02001356 "TS Err change: in +%u, out +%u\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001357 state.out_stream.last_timestamp - last_timestamp,
1358 state.out_stream.last_tsdelta,
1359 state.out_stream.last_seq - last_seqno,
Philipp Maier9e1d1642018-05-09 16:26:34 +02001360 (unsigned int) (state.in_stream.err_ts_ctr->current - last_in_ts_err_cnt),
1361 (unsigned int) (state.out_stream.err_ts_ctr->current - last_out_ts_err_cnt));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001362
1363 printf("Stats: Jitter = %u, Transit = %d\n",
Harald Welte49e3d5a2017-12-25 09:47:57 +01001364 calc_jitter(&state), state.stats.transit);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001365
Philipp Maier9e1d1642018-05-09 16:26:34 +02001366 last_in_ts_err_cnt = state.in_stream.err_ts_ctr->current;
1367 last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001368 last_timestamp = state.out_stream.last_timestamp;
1369 last_seqno = state.out_stream.last_seq;
Neels Hofmeyr51b42ff2020-06-19 01:34:42 +02001370
1371 msgb_free(msg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001372 }
1373
1374 force_monotonic_time_us = -1;
Neels Hofmeyrd20910c2017-11-18 21:27:50 +01001375 mgcp_conn_free_all(&endp);
Philipp Maier124a3e02021-07-26 11:17:15 +02001376 talloc_free(trunk);
1377 talloc_free(cfg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001378}
1379
1380static void test_multilple_codec(void)
1381{
1382 struct mgcp_config *cfg;
Philipp Maierd19de2e2020-06-03 13:55:33 +02001383 struct mgcp_trunk *trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001384 struct mgcp_endpoint *endp;
1385 struct msgb *inp, *resp;
1386 struct in_addr addr;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001387 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +01001388 char conn_id[256];
Philipp Maier39889e42021-08-04 17:42:57 +02001389 char *last_endpoint = mgcp_debug_get_last_endpoint_name();
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001390
1391 printf("Testing multiple payload types\n");
1392
1393 cfg = mgcp_config_alloc();
Philipp Maier6fbbeec2020-07-01 23:00:54 +02001394 trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Philipp Maier889fe7f2020-07-06 17:44:12 +02001395 trunk->v.vty_number_endpoints = 64;
1396 mgcp_trunk_equip(trunk);
Pau Espin Pedrold071a302019-09-19 17:39:31 +02001397
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001398 /* Allocate endpoint 1@mgw with two codecs */
Philipp Maier37a808c2020-07-03 15:48:31 +02001399 last_endpoint[0] = '\0';
Philipp Maierffd75e42017-11-22 11:44:50 +01001400 inp = create_msg(CRCX_MULT_1, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001401 resp = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001402 OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
1403 sizeof(conn_id)) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001404 msgb_free(inp);
1405 msgb_free(resp);
1406
Philipp Maier37a808c2020-07-03 15:48:31 +02001407 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/1@mgw") == 0);
1408 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1409 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001410 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001411 OSMO_ASSERT(conn);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001412 OSMO_ASSERT(conn->end.codec->payload_type == 18);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001413
1414 /* Allocate 2@mgw with three codecs, last one ignored */
Philipp Maier37a808c2020-07-03 15:48:31 +02001415 last_endpoint[0] = '\0';
Philipp Maierffd75e42017-11-22 11:44:50 +01001416 inp = create_msg(CRCX_MULT_2, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001417 resp = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001418 OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
1419 sizeof(conn_id)) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001420 msgb_free(inp);
1421 msgb_free(resp);
1422
Philipp Maier37a808c2020-07-03 15:48:31 +02001423 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/2@mgw") == 0);
1424 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1425 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001426 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001427 OSMO_ASSERT(conn);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001428 OSMO_ASSERT(conn->end.codec->payload_type == 18);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001429
Philipp Maierbc0346e2018-06-07 09:52:16 +02001430 /* Allocate 3@mgw with no codecs, check for PT == 0 */
1431 /* Note: It usually makes no sense to leave the payload type list
1432 * out. However RFC 2327 does not clearly forbid this case and
1433 * it makes and since we already decided in OS#2658 that a missing
1434 * LCO should pick a sane default codec, it makes sense to expect
1435 * the same behaviour if SDP lacks proper payload type information */
Philipp Maier37a808c2020-07-03 15:48:31 +02001436 last_endpoint[0] = '\0';
Philipp Maierffd75e42017-11-22 11:44:50 +01001437 inp = create_msg(CRCX_MULT_3, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001438 resp = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001439 OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
1440 sizeof(conn_id)) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001441 msgb_free(inp);
1442 msgb_free(resp);
1443
Philipp Maier37a808c2020-07-03 15:48:31 +02001444 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/3@mgw") == 0);
1445 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1446 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001447 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001448 OSMO_ASSERT(conn);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001449 OSMO_ASSERT(conn->end.codec->payload_type == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001450
1451 /* Allocate 4@mgw with a single codec */
Philipp Maier37a808c2020-07-03 15:48:31 +02001452 last_endpoint[0] = '\0';
Philipp Maierffd75e42017-11-22 11:44:50 +01001453 inp = create_msg(CRCX_MULT_4, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001454 resp = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001455 OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
1456 sizeof(conn_id)) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001457 msgb_free(inp);
1458 msgb_free(resp);
1459
Philipp Maier37a808c2020-07-03 15:48:31 +02001460 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/4@mgw") == 0);
1461 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1462 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001463 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001464 OSMO_ASSERT(conn);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001465 OSMO_ASSERT(conn->end.codec->payload_type == 18);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001466
Philipp Maier7f90ddb2020-06-02 21:52:53 +02001467 /* Allocate 5@mgw and let osmo-mgw pick a codec from the list */
Philipp Maier37a808c2020-07-03 15:48:31 +02001468 last_endpoint[0] = '\0';
Philipp Maierffd75e42017-11-22 11:44:50 +01001469 inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001470 resp = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001471 OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
1472 sizeof(conn_id)) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001473 msgb_free(inp);
1474 msgb_free(resp);
1475
Philipp Maier37a808c2020-07-03 15:48:31 +02001476 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
1477 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1478 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001479 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001480 OSMO_ASSERT(conn);
Philipp Maier7f90ddb2020-06-02 21:52:53 +02001481 OSMO_ASSERT(conn->end.codec->payload_type == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001482
Philipp Maierffd75e42017-11-22 11:44:50 +01001483 inp = create_msg(MDCX_NAT_DUMMY, conn_id);
Philipp Maier37a808c2020-07-03 15:48:31 +02001484 last_endpoint[0] = '\0';
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001485 resp = mgcp_handle_message(cfg, inp);
1486 msgb_free(inp);
1487 msgb_free(resp);
Philipp Maier37a808c2020-07-03 15:48:31 +02001488 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
1489 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1490 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001491 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001492 OSMO_ASSERT(conn);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001493 OSMO_ASSERT(conn->end.codec->payload_type == 3);
Pau Espin Pedrol5ffd1272022-10-04 13:45:48 +02001494 OSMO_ASSERT(osmo_sockaddr_port(&conn->end.addr.u.sa) == 16434);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001495 memset(&addr, 0, sizeof(addr));
1496 inet_aton("8.8.8.8", &addr);
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +02001497 OSMO_ASSERT(conn->end.addr.u.sa.sa_family == AF_INET);
1498 OSMO_ASSERT(conn->end.addr.u.sin.sin_addr.s_addr == addr.s_addr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001499
1500 /* Check what happens without that flag */
1501
Philipp Maier87bd9be2017-08-22 16:35:41 +02001502 /* Free the previous endpoint and the data and
1503 * check if the connection really vanished... */
Philipp Maier1355d7e2018-02-01 14:30:06 +01001504 mgcp_endp_release(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001505 talloc_free(endp->last_response);
1506 talloc_free(endp->last_trans);
1507 endp->last_response = endp->last_trans = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +01001508 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001509 OSMO_ASSERT(!conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001510
Philipp Maier37a808c2020-07-03 15:48:31 +02001511 last_endpoint[0] = '\0';
Philipp Maierffd75e42017-11-22 11:44:50 +01001512 inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001513 resp = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001514 OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
1515 sizeof(conn_id)) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001516 msgb_free(inp);
1517 msgb_free(resp);
1518
Philipp Maier37a808c2020-07-03 15:48:31 +02001519 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
1520 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1521 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001522 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001523 OSMO_ASSERT(conn);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001524 OSMO_ASSERT(conn->end.codec->payload_type == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001525
Philipp Maierd19de2e2020-06-03 13:55:33 +02001526 mgcp_endpoints_release(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001527 talloc_free(cfg);
1528}
1529
1530static void test_no_cycle(void)
1531{
1532 struct mgcp_config *cfg;
1533 struct mgcp_endpoint *endp;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001534 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +01001535 struct mgcp_conn *_conn = NULL;
Philipp Maierd19de2e2020-06-03 13:55:33 +02001536 struct mgcp_trunk *trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001537
1538 printf("Testing no sequence flow on initial packet\n");
1539
1540 cfg = mgcp_config_alloc();
Philipp Maier6fbbeec2020-07-01 23:00:54 +02001541 trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Philipp Maier889fe7f2020-07-06 17:44:12 +02001542 trunk->v.vty_number_endpoints = 64;
1543 mgcp_trunk_equip(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001544
Philipp Maier37a808c2020-07-03 15:48:31 +02001545 endp = mgcp_endp_by_name(NULL, "rtpbridge/1@mgw", cfg);
1546 OSMO_ASSERT(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001547
Philipp Maierffd75e42017-11-22 11:44:50 +01001548 _conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP,
1549 "test-connection");
1550 OSMO_ASSERT(_conn);
1551 conn = mgcp_conn_get_rtp(endp, _conn->id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001552 OSMO_ASSERT(conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001553
Harald Welte49e3d5a2017-12-25 09:47:57 +01001554 OSMO_ASSERT(conn->state.stats.initialized == 0);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001555
Pau Espin Pedrolb066bd02021-07-07 13:41:19 +02001556 mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342, false);
Harald Welte49e3d5a2017-12-25 09:47:57 +01001557 OSMO_ASSERT(conn->state.stats.initialized == 1);
1558 OSMO_ASSERT(conn->state.stats.cycles == 0);
1559 OSMO_ASSERT(conn->state.stats.max_seq == 0);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001560
Pau Espin Pedrolb066bd02021-07-07 13:41:19 +02001561 mgcp_rtp_annex_count(endp, &conn->state, 1, 0, 2342, false);
Harald Welte49e3d5a2017-12-25 09:47:57 +01001562 OSMO_ASSERT(conn->state.stats.initialized == 1);
1563 OSMO_ASSERT(conn->state.stats.cycles == 0);
1564 OSMO_ASSERT(conn->state.stats.max_seq == 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001565
1566 /* now jump.. */
Pau Espin Pedrolb066bd02021-07-07 13:41:19 +02001567 mgcp_rtp_annex_count(endp, &conn->state, UINT16_MAX, 0, 2342, false);
Harald Welte49e3d5a2017-12-25 09:47:57 +01001568 OSMO_ASSERT(conn->state.stats.initialized == 1);
1569 OSMO_ASSERT(conn->state.stats.cycles == 0);
1570 OSMO_ASSERT(conn->state.stats.max_seq == UINT16_MAX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001571
1572 /* and wrap */
Pau Espin Pedrolb066bd02021-07-07 13:41:19 +02001573 mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342, false);
Harald Welte49e3d5a2017-12-25 09:47:57 +01001574 OSMO_ASSERT(conn->state.stats.initialized == 1);
1575 OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1);
1576 OSMO_ASSERT(conn->state.stats.max_seq == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001577
Philipp Maierd19de2e2020-06-03 13:55:33 +02001578 mgcp_endpoints_release(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001579 talloc_free(cfg);
1580}
1581
1582static void test_no_name(void)
1583{
Philipp Maierd19de2e2020-06-03 13:55:33 +02001584 struct mgcp_trunk *trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001585 struct mgcp_config *cfg;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001586 struct msgb *inp, *msg;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001587
1588 printf("Testing no rtpmap name\n");
1589 cfg = mgcp_config_alloc();
Philipp Maier6fbbeec2020-07-01 23:00:54 +02001590 trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001591
Philipp Maier889fe7f2020-07-06 17:44:12 +02001592 trunk->v.vty_number_endpoints = 64;
Philipp Maierd19de2e2020-06-03 13:55:33 +02001593 trunk->audio_send_name = 0;
Philipp Maier889fe7f2020-07-06 17:44:12 +02001594 mgcp_trunk_equip(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001595
Philipp Maierffd75e42017-11-22 11:44:50 +01001596 inp = create_msg(CRCX, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001597 msg = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001598
1599 if (check_response(msg->data, CRCX_RET_NO_RTPMAP) != 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001600 printf("FAILED: there should not be a RTPMAP: %s\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +02001601 (char *)msg->data);
1602 OSMO_ASSERT(false);
1603 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001604 msgb_free(inp);
1605 msgb_free(msg);
1606
Philipp Maierd19de2e2020-06-03 13:55:33 +02001607 mgcp_endpoints_release(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001608 talloc_free(cfg);
1609}
1610
1611static void test_osmux_cid(void)
1612{
1613 int id, i;
1614
Pau Espin Pedrol8de58e72019-04-24 13:33:46 +02001615 OSMO_ASSERT(osmux_cid_pool_count_used() == 0);
1616
1617 id = osmux_cid_pool_get_next();
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001618 OSMO_ASSERT(id == 0);
Pau Espin Pedrol8de58e72019-04-24 13:33:46 +02001619 OSMO_ASSERT(osmux_cid_pool_count_used() == 1);
1620
1621 osmux_cid_pool_get(30);
1622 OSMO_ASSERT(osmux_cid_pool_count_used() == 2);
1623 osmux_cid_pool_get(30);
1624 OSMO_ASSERT(osmux_cid_pool_count_used() == 2);
1625
1626 osmux_cid_pool_put(id);
1627 OSMO_ASSERT(osmux_cid_pool_count_used() == 1);
1628 osmux_cid_pool_put(30);
1629 OSMO_ASSERT(osmux_cid_pool_count_used() == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001630
1631 for (i = 0; i < 256; ++i) {
Pau Espin Pedrol8de58e72019-04-24 13:33:46 +02001632 id = osmux_cid_pool_get_next();
Pau Espin Pedrol310e41c2022-11-15 13:23:38 +01001633 /* We called osmux_cid_pool_get_next() above, so next CID is i+1. */
1634 OSMO_ASSERT(id == ((i + 1) & 0xff));
Pau Espin Pedrol8de58e72019-04-24 13:33:46 +02001635 OSMO_ASSERT(osmux_cid_pool_count_used() == i + 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001636 }
1637
Pau Espin Pedrol8de58e72019-04-24 13:33:46 +02001638 id = osmux_cid_pool_get_next();
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001639 OSMO_ASSERT(id == -1);
1640
1641 for (i = 0; i < 256; ++i)
Pau Espin Pedrol8de58e72019-04-24 13:33:46 +02001642 osmux_cid_pool_put(i);
1643 OSMO_ASSERT(osmux_cid_pool_count_used() == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001644}
1645
1646static const struct log_info_cat log_categories[] = {
1647};
1648
1649const struct log_info log_info = {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001650 .cat = log_categories,
1651 .num_cat = ARRAY_SIZE(log_categories),
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001652};
1653
Philipp Maier3d7b58d2018-06-06 09:35:31 +02001654static void test_get_lco_identifier(void)
1655{
1656 char *test;
1657 printf("Testing get_lco_identifier()\n");
1658
1659 /* Normal case at the beginning */
1660 test = "p:10, a:PCMU";
1661 printf("%s -> %s\n", test, get_lco_identifier(test));
1662
1663 test = "p:10, a:PCMU";
1664 printf("%s -> %s\n", test, get_lco_identifier(test));
1665
1666 /* Begin parsing in the middle of the value part of
1667 * the previous LCO option value */
1668 test = "XXXX, p:10, a:PCMU";
1669 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1670
1671 test = "XXXX,p:10,a:PCMU";
1672 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1673
1674 test = "10,a:PCMU";
1675 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1676
1677 test = "10, a:PCMU";
1678 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1679
1680 test = "10,a: PCMU";
1681 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1682
1683 test = "10 ,a: PCMU";
1684 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1685
1686 /* Begin parsing right at the end of the previous LCO
1687 * option value */
1688 test = ", a:PCMU";
1689 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1690
1691 test = " a:PCMU";
1692 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1693
1694 /* Empty string, result should be (null) */
1695 test = "";
1696 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1697
1698 /* Missing colons, result should be (null) */
1699 test = "p10, aPCMU";
1700 printf("%s -> %s\n", test, get_lco_identifier(test));
1701
1702 /* Colon separated from the identifier, result should be (null) */
1703 test = "10,a :PCMU";
1704 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1705}
1706
1707static void test_check_local_cx_options(void *ctx)
1708{
1709 /* Legal cases */
1710 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
1711 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU") == 0);
1712 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p:10, IN:10") == 0);
1713 OSMO_ASSERT(check_local_cx_options(ctx, "one:AAA, two:BB, tree:C") ==
1714 0);
1715 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
1716 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:G726-32") == 0);
1717 OSMO_ASSERT(check_local_cx_options(ctx, "p:10-20, b:64") == 0);
1718 OSMO_ASSERT(check_local_cx_options(ctx, "b:32-64, e:off") == 0);
1719 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU;G726-32") == 0);
1720
1721 /* Illegal spaces before and after colon */
1722 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p :10") == -1);
1723 OSMO_ASSERT(check_local_cx_options(ctx, "a :PCMU, p:10") == -1);
1724 OSMO_ASSERT(check_local_cx_options(ctx, "p: 10, a:PCMU") == -1);
1725
1726 /* Check if multiple appearances of LCOs are rejected */
1727 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, p:10") == -1);
1728 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, a:PCMU, p:10") ==
1729 -1);
1730 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, p:10") == -1);
1731
1732 /* Check if empty LCO are rejected */
1733 OSMO_ASSERT(check_local_cx_options(ctx, "p: , a:PCMU") == -1);
1734 OSMO_ASSERT(check_local_cx_options(ctx, "p: , a: PCMU") == -1);
1735 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a: PCMU") == -1);
1736 OSMO_ASSERT(check_local_cx_options(ctx, "p:, a:PCMU") == -1);
1737 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:") == -1);
1738
1739 /* Garbeled beginning and ends */
1740 OSMO_ASSERT(check_local_cx_options(ctx, ":10, a:10") == -1);
1741 OSMO_ASSERT(check_local_cx_options(ctx, "10, a:PCMU") == -1);
1742 OSMO_ASSERT(check_local_cx_options(ctx, ", a:PCMU") == -1);
1743 OSMO_ASSERT(check_local_cx_options(ctx, " a:PCMU") == -1);
1744 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU,") == -1);
1745 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, ") == -1);
1746
1747 /* Illegal strings */
1748 OSMO_ASSERT(check_local_cx_options(ctx, " ") == -1);
1749 OSMO_ASSERT(check_local_cx_options(ctx, "") == -1);
1750 OSMO_ASSERT(check_local_cx_options(ctx, "AAA") == -1);
1751 OSMO_ASSERT(check_local_cx_options(ctx, ":,") == -1);
1752 OSMO_ASSERT(check_local_cx_options(ctx, ",:") == -1);
1753 OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
1754}
1755
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001756static const struct mgcp_codec_param amr_param_octet_aligned_true = {
1757 .amr_octet_aligned_present = true,
1758 .amr_octet_aligned = true,
1759};
1760
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001761static const struct mgcp_codec_param amr_param_octet_aligned_false = {
1762 .amr_octet_aligned_present = true,
1763 .amr_octet_aligned = false,
1764};
1765
1766static const struct mgcp_codec_param amr_param_octet_aligned_unset = {
1767 .amr_octet_aligned_present = false,
1768};
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001769
Philipp Maier4c4d2272023-04-26 15:45:17 +02001770struct testcase_mgcp_codec_decide_codec {
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001771 int payload_type;
1772 const char *audio_name;
1773 const struct mgcp_codec_param *param;
1774 int expect_rc;
1775};
1776
Philipp Maier4c4d2272023-04-26 15:45:17 +02001777struct testcase_mgcp_codec_decide_expect {
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001778 int payload_type_map[2];
1779};
1780
Philipp Maier4c4d2272023-04-26 15:45:17 +02001781struct testcase_mgcp_codec_decide {
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001782 const char *descr;
1783 /* two conns on an endpoint, each with N configured codecs */
Philipp Maier4c4d2272023-04-26 15:45:17 +02001784 struct testcase_mgcp_codec_decide_codec codecs[2][10];
1785 struct testcase_mgcp_codec_decide_expect expect[2];
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001786};
1787
Philipp Maier4c4d2272023-04-26 15:45:17 +02001788static const struct testcase_mgcp_codec_decide test_mgcp_codec_find_convertible_cases[] = {
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001789 {
1790 .descr = "same order, but differing payload type numbers",
1791 .codecs = {
1792 {
1793 { 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
1794 { 0, "PCMU/8000/1", NULL, },
1795 { 111, "GSM-HR-08/8000/1", NULL, },
1796 },
1797 {
1798 { 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
1799 { 0, "PCMU/8000/1", NULL, },
1800 { 97, "GSM-HR-08/8000/1", NULL, },
1801 },
1802 },
1803 .expect = {
1804 { .payload_type_map = {112, 96}, },
Philipp Maier4c4d2272023-04-26 15:45:17 +02001805 { .payload_type_map = {112, 96}, },
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001806 },
1807 },
1808 {
Neels Hofmeyr26985402019-08-08 22:39:55 +02001809 .descr = "different order and different payload type numbers",
1810 .codecs = {
1811 {
1812 { 0, "PCMU/8000/1", NULL, },
1813 { 111, "GSM-HR-08/8000/1", NULL, },
1814 { 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
1815 },
1816 {
1817 { 97, "GSM-HR-08/8000/1", NULL, },
1818 { 0, "PCMU/8000/1", NULL, },
1819 { 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
1820 },
1821 },
1822 .expect = {
Neels Hofmeyr26985402019-08-08 22:39:55 +02001823 { .payload_type_map = {0, 0}, },
Philipp Maier4c4d2272023-04-26 15:45:17 +02001824 { .payload_type_map = {111, 97}, },
Neels Hofmeyr26985402019-08-08 22:39:55 +02001825 },
1826 },
1827 {
1828 .descr = "both sides have the same payload_type numbers assigned to differing codecs",
1829 .codecs = {
1830 {
1831 { 0, "PCMU/8000/1", NULL, },
1832 { 96, "GSM-HR-08/8000/1", NULL, },
1833 { 97, "AMR/8000/1", &amr_param_octet_aligned_true, },
1834 },
1835 {
1836 { 97, "GSM-HR-08/8000/1", NULL, },
1837 { 0, "PCMU/8000/1", NULL, },
1838 { 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
1839 },
1840 },
1841 .expect = {
Neels Hofmeyr26985402019-08-08 22:39:55 +02001842 { .payload_type_map = {0, 0}, },
Philipp Maier4c4d2272023-04-26 15:45:17 +02001843 { .payload_type_map = {96, 97}, },
Neels Hofmeyr26985402019-08-08 22:39:55 +02001844 },
1845 },
1846 {
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001847 .descr = "conn0 has no codecs",
1848 .codecs = {
1849 {
1850 /* no codecs */
1851 },
1852 {
1853 { 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
1854 { 0, "PCMU/8000/1", NULL, },
1855 { 97, "GSM-HR-08/8000/1", NULL, },
1856 },
1857 },
1858 .expect = {
Philipp Maier4c4d2272023-04-26 15:45:17 +02001859 { .payload_type_map = {-EINVAL, -EINVAL}, },
1860 { .payload_type_map = {-EINVAL, 96}, },
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001861 },
1862 },
1863 {
1864 .descr = "conn1 has no codecs",
1865 .codecs = {
1866 {
1867 { 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
1868 { 0, "PCMU/8000/1", NULL, },
1869 { 111, "GSM-HR-08/8000/1", NULL, },
1870 },
1871 {
1872 /* no codecs */
1873 },
1874 },
1875 .expect = {
1876 { .payload_type_map = {112, -EINVAL}, },
Philipp Maier4c4d2272023-04-26 15:45:17 +02001877 { .payload_type_map = {-EINVAL, -EINVAL}, },
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001878 },
1879 },
Neels Hofmeyr16b637b2019-08-08 22:47:10 +02001880 {
Philipp Maier4c4d2272023-04-26 15:45:17 +02001881 .descr = "test AMR with differing octet-aligned settings (both <-> both)",
Neels Hofmeyr16b637b2019-08-08 22:47:10 +02001882 .codecs = {
1883 {
1884 { 111, "AMR/8000", &amr_param_octet_aligned_true, },
Philipp Maierec967d72023-03-22 16:20:37 +01001885 { 112, "AMR/8000", &amr_param_octet_aligned_false, },
Neels Hofmeyr16b637b2019-08-08 22:47:10 +02001886 },
1887 {
1888 { 122, "AMR/8000", &amr_param_octet_aligned_false, },
Philipp Maierec967d72023-03-22 16:20:37 +01001889 { 121, "AMR/8000", &amr_param_octet_aligned_true, },
Neels Hofmeyr16b637b2019-08-08 22:47:10 +02001890 },
1891 },
1892 .expect = {
Philipp Maierec967d72023-03-22 16:20:37 +01001893 { .payload_type_map = {111, 121}, },
1894 { .payload_type_map = {112, 122} },
Philipp Maier4c4d2272023-04-26 15:45:17 +02001895 },
1896 },
1897 {
1898 .descr = "test AMR with differing octet-aligned settings (oa <-> both)",
1899 .codecs = {
1900 {
1901 { 111, "AMR/8000", &amr_param_octet_aligned_true, },
1902 },
1903 {
1904 { 122, "AMR/8000", &amr_param_octet_aligned_false, },
1905 { 121, "AMR/8000", &amr_param_octet_aligned_true, },
1906 },
1907 },
1908 .expect = {
1909 { .payload_type_map = {111, 121}, },
1910 { .payload_type_map = {111, 121}, },
1911 },
1912 },
1913 {
1914 .descr = "test AMR with differing octet-aligned settings (bwe <-> both)",
1915 .codecs = {
1916 {
1917 { 112, "AMR/8000", &amr_param_octet_aligned_false, },
1918 },
1919 {
1920 { 122, "AMR/8000", &amr_param_octet_aligned_false, },
1921 { 121, "AMR/8000", &amr_param_octet_aligned_true, },
1922 },
1923 },
1924 .expect = {
1925 { .payload_type_map = {112, 122}, },
1926 { .payload_type_map = {112, 122}, },
Neels Hofmeyr16b637b2019-08-08 22:47:10 +02001927 },
1928 },
1929 {
Philipp Maier621e8662023-03-22 16:53:30 +01001930 .descr = "test AMR with missing octet-aligned settings (oa <-> unset)",
Neels Hofmeyr16b637b2019-08-08 22:47:10 +02001931 .codecs = {
1932 {
1933 { 111, "AMR/8000", &amr_param_octet_aligned_true, },
Neels Hofmeyr16b637b2019-08-08 22:47:10 +02001934 },
1935 {
1936 { 122, "AMR/8000", &amr_param_octet_aligned_unset, },
1937 },
1938 },
1939 .expect = {
Philipp Maier621e8662023-03-22 16:53:30 +01001940 { .payload_type_map = {111, 122}, },
Philipp Maier4c4d2272023-04-26 15:45:17 +02001941 { .payload_type_map = {111, 122}, },
Neels Hofmeyr16b637b2019-08-08 22:47:10 +02001942 },
1943 },
1944 {
Philipp Maier621e8662023-03-22 16:53:30 +01001945 .descr = "test AMR with missing octet-aligned settings (bwe <-> unset)",
Neels Hofmeyr16b637b2019-08-08 22:47:10 +02001946 .codecs = {
1947 {
Philipp Maier621e8662023-03-22 16:53:30 +01001948 { 111, "AMR/8000", &amr_param_octet_aligned_false, },
1949 },
1950 {
1951 { 122, "AMR/8000", &amr_param_octet_aligned_unset, },
1952 },
1953 },
1954 .expect = {
1955 { .payload_type_map = {111, 122}, },
Philipp Maier4c4d2272023-04-26 15:45:17 +02001956 { .payload_type_map = {111, 122}, },
Philipp Maier621e8662023-03-22 16:53:30 +01001957 },
1958 },
1959 {
1960 .descr = "test AMR with NULL param (oa <-> null)",
1961 .codecs = {
1962 {
1963 { 112, "AMR/8000", &amr_param_octet_aligned_true, },
1964 },
1965 {
1966 { 122, "AMR/8000", NULL, },
1967 },
1968 },
1969 .expect = {
1970 /* Note: Both 111, anbd 112 will translate to 122. The translation from 112 */
1971 { .payload_type_map = {112, 122} },
Philipp Maier4c4d2272023-04-26 15:45:17 +02001972 { .payload_type_map = {112, 122}, },
Philipp Maier621e8662023-03-22 16:53:30 +01001973 },
1974 },
1975 {
1976 .descr = "test AMR with NULL param (bwe <-> null)",
1977 .codecs = {
1978 {
Philipp Maierec967d72023-03-22 16:20:37 +01001979 { 112, "AMR/8000", &amr_param_octet_aligned_false, },
Neels Hofmeyr16b637b2019-08-08 22:47:10 +02001980 },
1981 {
1982 { 122, "AMR/8000", NULL, },
1983 },
1984 },
1985 .expect = {
Philipp Maier621e8662023-03-22 16:53:30 +01001986 /* Note: Both 111, anbd 112 will translate to 122. The translation from 112 */
Philipp Maierec967d72023-03-22 16:20:37 +01001987 { .payload_type_map = {112, 122} },
Philipp Maier4c4d2272023-04-26 15:45:17 +02001988 { .payload_type_map = {112, 122}, },
Neels Hofmeyr16b637b2019-08-08 22:47:10 +02001989 },
1990 },
Neels Hofmeyr683e05f2019-08-08 22:48:18 +02001991 {
1992 .descr = "match FOO/8000/1 and FOO/8000 as identical, single channel is implicit",
1993 .codecs = {
1994 {
1995 { 0, "PCMU/8000/1", NULL, },
1996 { 111, "GSM-HR-08/8000/1", NULL, },
1997 { 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
1998 },
1999 {
2000 { 97, "GSM-HR-08/8000", NULL, },
2001 { 0, "PCMU/8000", NULL, },
2002 { 96, "AMR/8000", &amr_param_octet_aligned_true, },
2003 },
2004 },
2005 .expect = {
Neels Hofmeyr683e05f2019-08-08 22:48:18 +02002006 { .payload_type_map = {0, 0}, },
Philipp Maier4c4d2272023-04-26 15:45:17 +02002007 { .payload_type_map = {111, 97}, },
Neels Hofmeyr683e05f2019-08-08 22:48:18 +02002008 },
2009 },
2010 {
2011 .descr = "match FOO/8000/1 and FOO as identical, 8k and single channel are implicit",
2012 .codecs = {
2013 {
2014 { 0, "PCMU/8000/1", NULL, },
2015 { 111, "GSM-HR-08/8000/1", NULL, },
2016 { 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
2017 },
2018 {
2019 { 97, "GSM-HR-08", NULL, },
2020 { 0, "PCMU", NULL, },
2021 { 96, "AMR", &amr_param_octet_aligned_true, },
2022 },
2023 },
2024 .expect = {
Neels Hofmeyr683e05f2019-08-08 22:48:18 +02002025 { .payload_type_map = {0, 0}, },
Philipp Maier4c4d2272023-04-26 15:45:17 +02002026 { .payload_type_map = {111, 97}, },
Neels Hofmeyr683e05f2019-08-08 22:48:18 +02002027 },
2028 },
2029 {
2030 .descr = "test whether channel number matching is waterproof",
2031 .codecs = {
2032 {
2033 { 111, "GSM-HR-08/8000", },
2034 { 112, "GSM-HR-08/8000/2", .expect_rc = -22},
2035 { 113, "GSM-HR-08/8000/3", .expect_rc = -22},
2036 },
2037 {
2038 { 122, "GSM-HR-08/8000/2", .expect_rc = -22},
2039 { 121, "GSM-HR-08/8000/1", },
2040 },
2041 },
2042 .expect = {
2043 { .payload_type_map = {111, 121}, },
Philipp Maier4c4d2272023-04-26 15:45:17 +02002044 { .payload_type_map = {111, 121} },
Neels Hofmeyr683e05f2019-08-08 22:48:18 +02002045 },
2046 },
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002047};
Philipp Maier6931f9a2018-07-26 09:29:31 +02002048
Philipp Maier4c4d2272023-04-26 15:45:17 +02002049static bool codec_decision(struct mgcp_conn_rtp *conn, unsigned int index_conn_src, unsigned int index_conn_dst,
2050 const struct testcase_mgcp_codec_decide_expect *expect)
Philipp Maier9dd80ca2023-03-27 15:45:36 +02002051{
Philipp Maier4c4d2272023-04-26 15:45:17 +02002052 bool ok = true;
2053 int payload_type_conn_src;
2054 int payload_type_conn_dst;
Philipp Maier9dd80ca2023-03-27 15:45:36 +02002055
Philipp Maier4c4d2272023-04-26 15:45:17 +02002056 printf(" - mgcp_codec_decide(&conn[%u], &conn[%u]):\n", index_conn_src, index_conn_dst);
2057 if (mgcp_codec_decide(&conn[index_conn_src], &conn[index_conn_dst]) != 0) {
2058 if (expect->payload_type_map[index_conn_src] == -EINVAL
2059 && expect->payload_type_map[index_conn_dst] == -EINVAL)
2060 printf(" codec decision failed (expected)!\n");
2061 else {
2062 printf(" ERROR: codec decision failed!\n");
2063 ok = false;
2064 }
2065 } else {
2066 printf(" Codec decision result:\n");
2067 if (conn[index_conn_src].end.codec) {
2068 payload_type_conn_src = conn[index_conn_src].end.codec->payload_type;
2069 printf(" conn[%u]: codec:%s, pt:%d\n",
2070 index_conn_src, conn[index_conn_src].end.codec->subtype_name, payload_type_conn_src);
2071 } else {
2072 payload_type_conn_src = -EINVAL;
2073 printf(" conn[%u]: codec:none, pt:none\n", index_conn_src);
2074 }
Philipp Maier9dd80ca2023-03-27 15:45:36 +02002075
Philipp Maier4c4d2272023-04-26 15:45:17 +02002076 if (conn[index_conn_dst].end.codec) {
2077 payload_type_conn_dst = conn[index_conn_dst].end.codec->payload_type;
2078 printf(" conn[%u]: codec:%s, pt:%d\n",
2079 index_conn_dst, conn[index_conn_dst].end.codec->subtype_name,
2080 payload_type_conn_dst);
2081 } else {
2082 payload_type_conn_dst = -EINVAL;
2083 printf(" conn[%u]: codec:none, pt:none\n", index_conn_dst);
2084 }
2085
2086 if (payload_type_conn_src != expect->payload_type_map[index_conn_src]) {
2087 printf(" ERROR: conn[%u] unexpected codec decision, expected pt=%d, got pt=%d\n",
2088 index_conn_src, expect->payload_type_map[index_conn_src], payload_type_conn_src);
2089 ok = false;
2090 }
2091
2092 if (payload_type_conn_dst != expect->payload_type_map[index_conn_dst]) {
2093 printf(" ERROR: conn[%u] unexpected codec decision, expected pt=%d, got pt=%d\n",
2094 index_conn_dst, expect->payload_type_map[index_conn_dst],
2095 payload_type_conn_dst);
2096 ok = false;
2097 }
Philipp Maier9dd80ca2023-03-27 15:45:36 +02002098 }
Philipp Maier4c4d2272023-04-26 15:45:17 +02002099
2100 return ok;
Philipp Maier9dd80ca2023-03-27 15:45:36 +02002101}
2102
Philipp Maier4c4d2272023-04-26 15:45:17 +02002103static void test_mgcp_codec_decide(void)
Philipp Maier6931f9a2018-07-26 09:29:31 +02002104{
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002105 int i;
2106 bool ok = true;
Philipp Maier9dd80ca2023-03-27 15:45:36 +02002107 printf("\nTesting mgcp_codec_find_convertible()\n");
Philipp Maier6931f9a2018-07-26 09:29:31 +02002108
Philipp Maier9dd80ca2023-03-27 15:45:36 +02002109 for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_find_convertible_cases); i++) {
Philipp Maier4c4d2272023-04-26 15:45:17 +02002110 const struct testcase_mgcp_codec_decide *t = &test_mgcp_codec_find_convertible_cases[i];
2111 struct mgcp_conn_rtp conn[2] = { };
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002112 int rc;
2113 int conn_i;
2114 int c;
Philipp Maier6931f9a2018-07-26 09:29:31 +02002115
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002116 printf("#%d: %s\n", i, t->descr);
Philipp Maier6931f9a2018-07-26 09:29:31 +02002117
Philipp Maier4c4d2272023-04-26 15:45:17 +02002118 /* Build testvector (add codecs to conn, set properties etc... */
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002119 for (conn_i = 0; conn_i < 2; conn_i++) {
2120 printf(" - add codecs on conn%d:\n", conn_i);
2121 for (c = 0; c < ARRAY_SIZE(t->codecs[conn_i]); c++) {
Philipp Maier4c4d2272023-04-26 15:45:17 +02002122 const struct testcase_mgcp_codec_decide_codec *codec = &t->codecs[conn_i][c];
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002123 if (!codec->audio_name)
2124 break;
2125
Philipp Maier4c4d2272023-04-26 15:45:17 +02002126 rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name,
2127 codec->param);
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002128
2129 printf(" %2d: %3d %s%s -> rc=%d\n", c, codec->payload_type, codec->audio_name,
2130 codec->param ?
Philipp Maier4c4d2272023-04-26 15:45:17 +02002131 (codec->param->amr_octet_aligned_present ?
2132 (codec->param->amr_octet_aligned ? " octet-aligned=1" : " octet-aligned=0")
2133 : " octet-aligned=unset")
2134 : "", rc);
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002135 if (rc != codec->expect_rc) {
2136 printf(" ERROR: expected rc=%d\n", codec->expect_rc);
2137 ok = false;
2138 }
2139 }
2140 if (!c)
2141 printf(" (none)\n");
2142 }
2143
Philipp Maier4c4d2272023-04-26 15:45:17 +02002144 /* Run codec decision and check expectation */
2145 if (!codec_decision(conn, 0, 1, &t->expect[0]))
2146 ok = false;
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002147
Philipp Maier4c4d2272023-04-26 15:45:17 +02002148 if (!codec_decision(conn, 1, 0, &t->expect[1]))
2149 ok = false;
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002150
Philipp Maier4c4d2272023-04-26 15:45:17 +02002151 if (ok)
2152 printf(" ===> SUCCESS: codec decision as expected!\n");
2153 else
2154 printf(" ===> FAIL: unexpected codec decision!\n");
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002155 }
2156
2157 OSMO_ASSERT(ok);
Philipp Maier6931f9a2018-07-26 09:29:31 +02002158}
2159
Harald Welte9befdeb2022-11-03 11:41:05 +01002160void test_conn_id_matching(void)
Neels Hofmeyr65317262018-09-03 22:11:05 +02002161{
2162 struct mgcp_endpoint endp = {};
2163 struct mgcp_conn *conn;
2164 struct mgcp_conn *conn_match;
2165 int i;
2166 const char *conn_id_generated = "000023AB";
2167 const char *conn_id_request[] = {
Neels Hofmeyra77eade2018-08-29 02:30:39 +02002168 "23AB",
2169 "0023AB",
Neels Hofmeyr65317262018-09-03 22:11:05 +02002170 "000023AB",
Neels Hofmeyra77eade2018-08-29 02:30:39 +02002171 "00000023AB",
2172 "23ab",
2173 "0023ab",
Neels Hofmeyr65317262018-09-03 22:11:05 +02002174 "000023ab",
Neels Hofmeyra77eade2018-08-29 02:30:39 +02002175 "00000023ab",
Neels Hofmeyr65317262018-09-03 22:11:05 +02002176 };
2177
2178 printf("\nTesting %s\n", __func__);
2179
2180 INIT_LLIST_HEAD(&endp.conns);
2181
2182 conn = talloc_zero(NULL, struct mgcp_conn);
2183 OSMO_ASSERT(conn);
2184 osmo_strlcpy(conn->id, conn_id_generated, sizeof(conn->id));
2185 llist_add(&conn->entry, &endp.conns);
2186
2187 for (i = 0; i < ARRAY_SIZE(conn_id_request); i++) {
2188 const char *needle = conn_id_request[i];
2189 printf("needle='%s' ", needle);
2190 conn_match = mgcp_conn_get(&endp, needle);
2191 OSMO_ASSERT(conn_match);
2192 printf("found '%s'\n", conn_match->id);
2193 OSMO_ASSERT(conn_match == conn);
2194 }
2195
2196 llist_del(&conn->entry);
2197 talloc_free(conn);
2198}
2199
Harald Welte9befdeb2022-11-03 11:41:05 +01002200void test_e1_trunk_nr_from_epname(void)
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002201{
Philipp Maierd70eef62021-07-19 13:53:28 +02002202 unsigned int trunk_nr;
2203 int rc;
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002204
2205 /* Note: e1_trunk_nr_from_epname does not check the text
2206 * after the E1 trunk number, after the delimiter
2207 * character "/" arbitrary text may follow. */
Philipp Maierd70eef62021-07-19 13:53:28 +02002208 rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-0/s-1/su16-0");
Philipp Maier0653cc82020-08-10 22:52:51 +02002209 OSMO_ASSERT(trunk_nr == 0);
Philipp Maierd70eef62021-07-19 13:53:28 +02002210 OSMO_ASSERT(rc == 0);
2211 rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-1/s-1/su16-0");
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002212 OSMO_ASSERT(trunk_nr == 1);
Philipp Maierd70eef62021-07-19 13:53:28 +02002213 OSMO_ASSERT(rc == 0);
2214 rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-2/s-2/su16-0");
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002215 OSMO_ASSERT(trunk_nr == 2);
Philipp Maierd70eef62021-07-19 13:53:28 +02002216 OSMO_ASSERT(rc == 0);
2217 rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-3/s-23/su32-0");
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002218 OSMO_ASSERT(trunk_nr == 3);
Philipp Maierd70eef62021-07-19 13:53:28 +02002219 OSMO_ASSERT(rc == 0);
2220 rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-3/xxxxxxx");
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002221 OSMO_ASSERT(trunk_nr == 3);
Philipp Maierd70eef62021-07-19 13:53:28 +02002222 OSMO_ASSERT(rc == 0);
2223 rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-24/s-1/su16-0");
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002224 OSMO_ASSERT(trunk_nr == 24);
Philipp Maierd70eef62021-07-19 13:53:28 +02002225 OSMO_ASSERT(rc == 0);
2226 rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-64/s-1/su16-0");
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002227 OSMO_ASSERT(trunk_nr == 64);
Philipp Maierd70eef62021-07-19 13:53:28 +02002228 OSMO_ASSERT(rc == 0);
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002229
2230 /* The following endpoint strings should fail, either the
2231 * trunk number exceeds the valid range or the trunk prefix
2232 * is wrong. Also when the delimiter character "/" at the
2233 * end of the trunk is wrong the parsing should fail. */
Philipp Maierd70eef62021-07-19 13:53:28 +02002234 rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-65/s-1/su16-0");
2235 OSMO_ASSERT(rc == -EINVAL);
2236 rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1--1/s-1/su16-0");
2237 OSMO_ASSERT(rc == -EINVAL);
2238 rc = e1_trunk_nr_from_epname(&trunk_nr, "xxxxxx4zyz");
2239 OSMO_ASSERT(rc == -EINVAL);
2240 rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1+2/s-1/su16-0");
2241 OSMO_ASSERT(rc == -EINVAL);
2242 rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e2-24/s-1/su16-0");
2243 OSMO_ASSERT(rc == -EINVAL);
2244 rc = e1_trunk_nr_from_epname(&trunk_nr, "ds/e1-24s-1/su16-0");
2245 OSMO_ASSERT(rc == -EINVAL);
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002246
2247 return;
2248}
2249
Harald Welte9befdeb2022-11-03 11:41:05 +01002250void test_mgcp_is_rtp_dummy_payload(void)
Philipp Maierb3d14eb2021-05-20 14:18:52 +02002251{
2252 /* realistic rtp packet */
2253 static const char rtp_payload[] =
2254 { 0x80, 0x03, 0xca, 0xd7, 0x62, 0x12, 0x75, 0xc4, 0x43, 0x4b, 0x3e,
2255 0x72, 0xd2, 0x57, 0x7a, 0x1c, 0xda, 0x50, 0x00, 0x49, 0x24, 0x92,
2256 0x49, 0x24, 0x50, 0x00, 0x49, 0x24, 0x92, 0x49, 0x24, 0x50, 0x00,
2257 0x49, 0x24, 0x92, 0x49, 0x24, 0x50, 0x00, 0x49, 0x23, 0x92, 0x49,
2258 0x24 };
2259
2260 struct msgb *msg_dummy = msgb_alloc(RTP_BUF_SIZE, "RTP-msg_dummy");
2261 struct msgb *msg_rtp = msgb_alloc(RTP_BUF_SIZE, "RTP-msg_rtp");
2262 struct msgb *msg_dummy_rtp =
2263 msgb_alloc(RTP_BUF_SIZE, "RTP-msg_dummy_rtp");
2264
2265 uint8_t *buf;
2266
2267 /* Dummy RTP packet */
2268 buf = msgb_put(msg_dummy, ARRAY_SIZE(rtp_dummy_payload));
2269 memcpy(buf, rtp_dummy_payload, ARRAY_SIZE(rtp_dummy_payload));
2270
2271 /* Normal RTP packet */
2272 buf = msgb_put(msg_rtp, ARRAY_SIZE(rtp_payload));
2273 memcpy(buf, rtp_payload, ARRAY_SIZE(rtp_payload));
2274
2275 /* Dummy RTP packet with normal RTP packet attached, this must not be
2276 * recognized as Dummy RTP packet */
2277 buf = msgb_put(msg_dummy_rtp, ARRAY_SIZE(rtp_dummy_payload));
2278 memcpy(buf, rtp_dummy_payload, ARRAY_SIZE(rtp_dummy_payload));
2279 buf = msgb_put(msg_dummy_rtp, ARRAY_SIZE(rtp_payload));
2280 memcpy(buf, rtp_payload, ARRAY_SIZE(rtp_payload));
2281
2282 OSMO_ASSERT(mgcp_is_rtp_dummy_payload(msg_dummy) == true);
2283 OSMO_ASSERT(mgcp_is_rtp_dummy_payload(msg_rtp) == false);
2284 OSMO_ASSERT(mgcp_is_rtp_dummy_payload(msg_dummy_rtp) == false);
2285
2286 msgb_free(msg_dummy);
2287 msgb_free(msg_rtp);
2288 msgb_free(msg_dummy_rtp);
2289}
2290
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02002291int main(int argc, char **argv)
2292{
Neels Hofmeyr60f8e312018-03-30 23:01:07 +02002293 void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
2294 void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
2295 osmo_init_logging2(ctx, &log_info);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02002296
2297 test_strline();
2298 test_values();
2299 test_messages();
2300 test_retransmission();
2301 test_packet_loss_calc();
2302 test_rqnt_cb();
2303 test_mgcp_stats();
2304 test_packet_error_detection(1, 0);
2305 test_packet_error_detection(0, 0);
2306 test_packet_error_detection(0, 1);
2307 test_packet_error_detection(1, 1);
2308 test_multilple_codec();
2309 test_no_cycle();
2310 test_no_name();
2311 test_osmux_cid();
Philipp Maier3d7b58d2018-06-06 09:35:31 +02002312 test_get_lco_identifier();
2313 test_check_local_cx_options(ctx);
Philipp Maier4c4d2272023-04-26 15:45:17 +02002314 test_mgcp_codec_decide();
Neels Hofmeyr65317262018-09-03 22:11:05 +02002315 test_conn_id_matching();
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002316 test_e1_trunk_nr_from_epname();
Philipp Maierb3d14eb2021-05-20 14:18:52 +02002317 test_mgcp_is_rtp_dummy_payload();
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02002318
Neels Hofmeyr465446b2017-11-18 21:26:26 +01002319 OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
2320 OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
2321 talloc_free(msgb_ctx);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02002322 printf("Done\n");
2323 return EXIT_SUCCESS;
2324}