blob: 74ecfe8f5c4983e66e9e0976ccd4aa5a52689d3d [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>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020033
34#include <osmocom/core/application.h>
35#include <osmocom/core/talloc.h>
36#include <osmocom/core/utils.h>
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +020037#include <osmocom/core/socket.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020038#include <string.h>
39#include <limits.h>
40#include <dlfcn.h>
41#include <time.h>
42#include <math.h>
Neels Hofmeyrb861db92018-08-28 16:19:25 +020043#include <ctype.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020044
45char *strline_r(char *str, char **saveptr);
46
47const char *strline_test_data =
48 "one CR\r"
49 "two CR\r"
50 "\r"
51 "one CRLF\r\n"
52 "two CRLF\r\n"
Philipp Maier87bd9be2017-08-22 16:35:41 +020053 "\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 +020054
55#define EXPECTED_NUMBER_OF_LINES 13
56
57static void test_strline(void)
58{
59 char *save = NULL;
60 char *line;
61 char buf[2048];
62 int counter = 0;
63
64 osmo_strlcpy(buf, strline_test_data, sizeof(buf));
65
Philipp Maier87bd9be2017-08-22 16:35:41 +020066 for (line = mgcp_strline(buf, &save); line;
67 line = mgcp_strline(NULL, &save)) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020068 printf("line: '%s'\n", line);
69 counter++;
70 }
71
72 OSMO_ASSERT(counter == EXPECTED_NUMBER_OF_LINES);
73}
74
Philipp Maier12943ea2018-01-17 15:40:25 +010075#define AUEP1 "AUEP 158663169 ds/e1-1/2@mgw MGCP 1.0\r\n"
Philipp Maierc66ab2c2020-06-02 20:55:34 +020076#define AUEP1_RET "500 158663169 FAIL\r\n"
Philipp Maier12943ea2018-01-17 15:40:25 +010077#define AUEP2 "AUEP 18983213 ds/e1-2/1@mgw MGCP 1.0\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020078#define AUEP2_RET "500 18983213 FAIL\r\n"
79#define EMPTY "\r\n"
80#define EMPTY_RET NULL
81#define SHORT "CRCX \r\n"
82#define SHORT_RET "510 000000 FAIL\r\n"
83
Philipp Maier12943ea2018-01-17 15:40:25 +010084#define MDCX_WRONG_EP "MDCX 18983213 ds/e1-3/1@mgw MGCP 1.0\r\n"
Harald Welteabbb6b92017-12-28 13:13:50 +010085#define MDCX_ERR_RET "500 18983213 FAIL\r\n"
Philipp Maier12943ea2018-01-17 15:40:25 +010086#define MDCX_UNALLOCATED "MDCX 18983214 ds/e1-1/2@mgw MGCP 1.0\r\n"
Philipp Maierc66ab2c2020-06-02 20:55:34 +020087#define MDCX_RET "500 18983214 FAIL\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020088
Philipp Maier87bd9be2017-08-22 16:35:41 +020089#define MDCX3 \
90 "MDCX 18983215 1@mgw MGCP 1.0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +010091 "I: %s\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020092
Philipp Maier87bd9be2017-08-22 16:35:41 +020093#define MDCX3_RET \
94 "200 18983215 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +010095 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +020096 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +010097 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +020098 "s=-\r\n" \
99 "c=IN IP4 0.0.0.0\r\n" \
100 "t=0 0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100101 "m=audio 16002 RTP/AVP 97\r\n" \
102 "a=rtpmap:97 GSM-EFR/8000\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200103 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200104
Philipp Maier87bd9be2017-08-22 16:35:41 +0200105#define MDCX3A_RET \
106 "200 18983215 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100107 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200108 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100109 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200110 "s=-\r\n" \
111 "c=IN IP4 0.0.0.0\r\n" \
112 "t=0 0\r\n" \
113 "m=audio 16002 RTP/AVP 97\r\n" \
114 "a=rtpmap:97 GSM-EFR/8000\r\n" \
115 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200116
Philipp Maier87bd9be2017-08-22 16:35:41 +0200117#define MDCX3_FMTP_RET \
118 "200 18983215 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100119 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200120 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100121 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200122 "s=-\r\n" \
123 "c=IN IP4 0.0.0.0\r\n" \
124 "t=0 0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100125 "m=audio 16006 RTP/AVP 97\r\n" \
126 "a=rtpmap:97 GSM-EFR/8000\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200127 "a=fmtp:126 0/1/2\r\n" \
128 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200129
Philipp Maier87bd9be2017-08-22 16:35:41 +0200130#define MDCX4 \
131 "MDCX 18983216 1@mgw MGCP 1.0\r\n" \
132 "M: sendrecv\r" \
133 "C: 2\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100134 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200135 "L: p:20, a:AMR, nt:IN\r\n" \
136 "\n" \
137 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100138 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200139 "c=IN IP4 0.0.0.0\r\n" \
140 "t=0 0\r\n" \
141 "m=audio 4441 RTP/AVP 99\r\n" \
142 "a=rtpmap:99 AMR/8000\r\n" \
143 "a=ptime:40\r\n"
144
145#define MDCX4_RET(Ident) \
146 "200 " Ident " OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100147 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200148 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100149 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200150 "s=-\r\n" \
151 "c=IN IP4 0.0.0.0\r\n" \
152 "t=0 0\r\n" \
153 "m=audio 16002 RTP/AVP 99\r\n" \
154 "a=rtpmap:99 AMR/8000\r\n" \
155 "a=ptime:40\r\n"
156
157#define MDCX4_RO_RET(Ident) \
158 "200 " Ident " OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100159 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200160 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100161 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200162 "s=-\r\n" \
163 "c=IN IP4 0.0.0.0\r\n" \
164 "t=0 0\r\n" \
Philipp Maierbc0346e2018-06-07 09:52:16 +0200165 "m=audio 16002 RTP/AVP 112\r\n" \
166 "a=rtpmap:112 AMR\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200167 "a=ptime:40\r\n"
168
169#define MDCX4_PT1 \
170 "MDCX 18983217 1@mgw MGCP 1.0\r\n" \
Pau Espin Pedrol17058482019-06-26 12:23:02 +0200171 "M: SENDRECV\r" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200172 "C: 2\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100173 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200174 "L: p:20-40, a:AMR, nt:IN\r\n" \
175 "\n" \
176 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100177 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200178 "c=IN IP4 0.0.0.0\r\n" \
179 "t=0 0\r\n" \
180 "m=audio 4441 RTP/AVP 99\r\n" \
181 "a=rtpmap:99 AMR/8000\r\n" \
182 "a=ptime:40\r\n"
183
184#define MDCX4_PT2 \
185 "MDCX 18983218 1@mgw MGCP 1.0\r\n" \
186 "M: sendrecv\r" \
187 "C: 2\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100188 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200189 "L: p:20-20, a:AMR, nt:IN\r\n" \
190 "\n" \
191 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100192 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200193 "c=IN IP4 0.0.0.0\r\n" \
194 "t=0 0\r\n" \
195 "m=audio 4441 RTP/AVP 99\r\n" \
196 "a=rtpmap:99 AMR/8000\r\n" \
197 "a=ptime:40\r\n"
198
199#define MDCX4_PT3 \
200 "MDCX 18983219 1@mgw MGCP 1.0\r\n" \
201 "M: sendrecv\r" \
202 "C: 2\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100203 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200204 "L: a:AMR, nt:IN\r\n" \
205 "\n" \
206 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100207 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200208 "c=IN IP4 0.0.0.0\r\n" \
209 "t=0 0\r\n" \
210 "m=audio 4441 RTP/AVP 99\r\n" \
211 "a=rtpmap:99 AMR/8000\r\n" \
212 "a=ptime:40\r\n"
213
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200214/* Test different upper/lower case in options */
215#define MDCX4_PT4 \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200216 "MDCX 18983220 1@mgw MGCP 1.0\r\n" \
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200217 "m: sendrecv\r" \
218 "c: 2\r\n" \
219 "i: %s\r\n" \
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200220 "l: A:amr, NT:IN\r\n" \
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200221 "\n" \
222 "v=0\r\n" \
223 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
224 "c=IN IP4 0.0.0.0\r\n" \
225 "t=0 0\r\n" \
226 "m=audio 4441 RTP/AVP 99\r\n" \
227 "a=rtpmap:99 AMR/8000\r\n" \
228 "a=ptime:40\r\n"
229
230#define MDCX4_SO \
231 "MDCX 18983221 1@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200232 "M: sendonly\r" \
233 "C: 2\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100234 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200235 "L: p:20, a:AMR, nt:IN\r\n" \
236 "\n" \
237 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100238 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200239 "c=IN IP4 0.0.0.0\r\n" \
240 "t=0 0\r\n" \
241 "m=audio 4441 RTP/AVP 99\r\n" \
242 "a=rtpmap:99 AMR/8000\r\n" \
243 "a=ptime:40\r\n"
244
245#define MDCX4_RO \
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200246 "MDCX 18983222 1@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200247 "M: recvonly\r" \
248 "C: 2\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100249 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200250 "L: p:20, a:AMR, nt:IN\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200251
Neels Hofmeyr5336f572018-09-03 22:05:48 +0200252#define MDCX_TOO_LONG_CI \
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200253 "MDCX 18983223 1@mgw MGCP 1.0\r\n" \
Neels Hofmeyr5336f572018-09-03 22:05:48 +0200254 "I: 123456789012345678901234567890123\n"
255
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200256#define MDCX_TOO_LONG_CI_RET "510 18983223 FAIL\r\n"
Neels Hofmeyr5336f572018-09-03 22:05:48 +0200257
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200258#define SHORT2 "CRCX 1"
259#define SHORT2_RET "510 000000 FAIL\r\n"
260#define SHORT3 "CRCX 1 1@mgw"
261#define SHORT4 "CRCX 1 1@mgw MGCP"
262#define SHORT5 "CRCX 1 1@mgw MGCP 1.0"
263
Philipp Maier87bd9be2017-08-22 16:35:41 +0200264#define CRCX \
265 "CRCX 2 1@mgw MGCP 1.0\r\n" \
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200266 "m: recvonly\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200267 "C: 2\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200268 "L: p:20\r\n" \
269 "\r\n" \
270 "v=0\r\n" \
271 "c=IN IP4 123.12.12.123\r\n" \
272 "m=audio 5904 RTP/AVP 97\r\n" \
273 "a=rtpmap:97 GSM-EFR/8000\r\n" \
274 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200275
Philipp Maier87bd9be2017-08-22 16:35:41 +0200276#define CRCX_RET \
277 "200 2 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100278 "I: %s\r\n" \
279 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200280 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100281 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200282 "s=-\r\n" \
283 "c=IN IP4 0.0.0.0\r\n" \
284 "t=0 0\r\n" \
285 "m=audio 16002 RTP/AVP 97\r\n" \
286 "a=rtpmap:97 GSM-EFR/8000\r\n" \
287 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200288
Philipp Maier87bd9be2017-08-22 16:35:41 +0200289#define CRCX_RET_NO_RTPMAP \
290 "200 2 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100291 "I: %s\r\n" \
292 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200293 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100294 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200295 "s=-\r\n" \
296 "c=IN IP4 0.0.0.0\r\n" \
297 "t=0 0\r\n" \
298 "m=audio 16002 RTP/AVP 97\r\n" \
299 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200300
Philipp Maier87bd9be2017-08-22 16:35:41 +0200301#define CRCX_FMTP_RET \
302 "200 2 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100303 "I: %s\r\n" \
304 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200305 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100306 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200307 "s=-\r\n" \
308 "c=IN IP4 0.0.0.0\r\n" \
309 "t=0 0\r\n" \
310 "m=audio 16006 RTP/AVP 97\r\n" \
311 "a=rtpmap:97 GSM-EFR/8000\r\n" \
312 "a=fmtp:126 0/1/2\r\n" \
313 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200314
Philipp Maier87bd9be2017-08-22 16:35:41 +0200315#define CRCX_ZYN \
316 "CRCX 2 1@mgw MGCP 1.0\r" \
317 "M: recvonly\r" \
318 "C: 2\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200319 "\n" \
320 "v=0\r" \
321 "c=IN IP4 123.12.12.123\r" \
322 "m=audio 5904 RTP/AVP 97\r" \
323 "a=rtpmap:97 GSM-EFR/8000\r"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200324
Philipp Maier87bd9be2017-08-22 16:35:41 +0200325#define CRCX_ZYN_RET \
326 "200 2 OK\r\n" \
Philipp Maierc3cfae22018-01-22 12:03:03 +0100327 "I: %s\r\n" \
328 "\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200329 "v=0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100330 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200331 "s=-\r\n" \
332 "c=IN IP4 0.0.0.0\r\n" \
333 "t=0 0\r\n" \
334 "m=audio 16004 RTP/AVP 97\r\n" \
335 "a=rtpmap:97 GSM-EFR/8000\r\n" \
336 "a=ptime:20\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200337
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200338#define CRCX_X_OSMO_IGN \
339 "CRCX 2 1@mgw MGCP 1.0\r\n" \
340 "M: recvonly\r\n" \
341 "C: 2\r\n" \
342 "L: p:20\r\n" \
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200343 "X-Osmo-IGN: C foo\r\n" \
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200344 "\r\n" \
345 "v=0\r\n" \
346 "c=IN IP4 123.12.12.123\r\n" \
347 "m=audio 5904 RTP/AVP 97\r\n" \
348 "a=rtpmap:97 GSM-EFR/8000\r\n" \
349 "a=ptime:40\r\n"
350
351#define CRCX_X_OSMO_IGN_RET \
352 "200 2 OK\r\n" \
353 "I: %s\r\n" \
354 "\r\n" \
355 "v=0\r\n" \
356 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
357 "s=-\r\n" \
358 "c=IN IP4 0.0.0.0\r\n" \
359 "t=0 0\r\n" \
360 "m=audio 16010 RTP/AVP 97\r\n" \
361 "a=rtpmap:97 GSM-EFR/8000\r\n" \
362 "a=ptime:40\r\n"
363
Philipp Maier87bd9be2017-08-22 16:35:41 +0200364#define DLCX \
365 "DLCX 7 1@mgw MGCP 1.0\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100366 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200367 "C: 2\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200368
Philipp Maier87bd9be2017-08-22 16:35:41 +0200369#define DLCX_RET \
370 "250 7 OK\r\n" \
Pau Espin Pedrol2da99a22018-02-20 13:11:17 +0100371 "P: PS=0, OS=0, PR=0, OR=0, PL=0, JI=0\r\n"
372
373 #define DLCX_RET_OSMUX DLCX_RET \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200374 "X-Osmo-CP: EC TI=0, TO=0\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200375
Philipp Maier87bd9be2017-08-22 16:35:41 +0200376#define RQNT \
377 "RQNT 186908780 1@mgw MGCP 1.0\r\n" \
378 "X: B244F267488\r\n" \
379 "S: D/9\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200380
Philipp Maier87bd9be2017-08-22 16:35:41 +0200381#define RQNT2 \
382 "RQNT 186908781 1@mgw MGCP 1.0\r\n" \
383 "X: ADD4F26746F\r\n" \
384 "R: D/[0-9#*](N), G/ft, fxr/t38\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200385
386#define RQNT1_RET "200 186908780 OK\r\n"
387#define RQNT2_RET "200 186908781 OK\r\n"
388
Philipp Maier87bd9be2017-08-22 16:35:41 +0200389#define PTYPE_IGNORE 0 /* == default initializer */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200390#define PTYPE_NONE 128
391#define PTYPE_NYI PTYPE_NONE
392
Philipp Maier87bd9be2017-08-22 16:35:41 +0200393#define CRCX_MULT_1 \
394 "CRCX 2 1@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200395 "M: recvonly\r\n" \
396 "C: 2\r\n" \
397 "X\r\n" \
398 "L: p:20\r\n" \
399 "\r\n" \
400 "v=0\r\n" \
401 "c=IN IP4 123.12.12.123\r\n" \
402 "m=audio 5904 RTP/AVP 18 97\r\n" \
403 "a=rtpmap:18 G729/8000\r\n" \
404 "a=rtpmap:97 GSM-EFR/8000\r\n" \
405 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200406
Philipp Maier87bd9be2017-08-22 16:35:41 +0200407#define CRCX_MULT_2 \
408 "CRCX 2 2@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200409 "M: recvonly\r\n" \
410 "C: 2\r\n" \
411 "X\r\n" \
412 "L: p:20\r\n" \
413 "\r\n" \
414 "v=0\r\n" \
415 "c=IN IP4 123.12.12.123\r\n" \
416 "m=audio 5904 RTP/AVP 18 97 101\r\n" \
417 "a=rtpmap:18 G729/8000\r\n" \
418 "a=rtpmap:97 GSM-EFR/8000\r\n" \
419 "a=rtpmap:101 FOO/8000\r\n" \
420 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200421
Philipp Maier87bd9be2017-08-22 16:35:41 +0200422#define CRCX_MULT_3 \
423 "CRCX 2 3@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200424 "M: recvonly\r\n" \
425 "C: 2\r\n" \
426 "X\r\n" \
427 "L: p:20\r\n" \
428 "\r\n" \
429 "v=0\r\n" \
430 "c=IN IP4 123.12.12.123\r\n" \
431 "m=audio 5904 RTP/AVP\r\n" \
432 "a=rtpmap:18 G729/8000\r\n" \
433 "a=rtpmap:97 GSM-EFR/8000\r\n" \
434 "a=rtpmap:101 FOO/8000\r\n" \
435 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200436
Philipp Maier87bd9be2017-08-22 16:35:41 +0200437#define CRCX_MULT_4 \
438 "CRCX 2 4@mgw MGCP 1.0\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200439 "M: recvonly\r\n" \
440 "C: 2\r\n" \
441 "X\r\n" \
442 "L: p:20\r\n" \
443 "\r\n" \
444 "v=0\r\n" \
445 "c=IN IP4 123.12.12.123\r\n" \
446 "m=audio 5904 RTP/AVP 18\r\n" \
447 "a=rtpmap:18 G729/8000\r\n" \
448 "a=rtpmap:97 GSM-EFR/8000\r\n" \
449 "a=rtpmap:101 FOO/8000\r\n" \
450 "a=ptime:40\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200451
452#define CRCX_MULT_GSM_EXACT \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200453 "CRCX 259260421 5@mgw MGCP 1.0\r\n" \
454 "C: 1355c6041e\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200455 "L: p:20, a:GSM, nt:IN\r\n" \
456 "M: recvonly\r\n" \
457 "\r\n" \
458 "v=0\r\n" \
459 "o=- 1439038275 1439038275 IN IP4 192.168.181.247\r\n" \
460 "s=-\r\nc=IN IP4 192.168.181.247\r\n" \
Philipp Maierbc0346e2018-06-07 09:52:16 +0200461 "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 +0200462 "a=rtpmap:0 PCMU/8000\r\n" \
463 "a=rtpmap:8 PCMA/8000\r\n" \
464 "a=rtpmap:3 gsm/8000\r\n" \
465 "a=rtpmap:18 G729/8000\r\n" \
466 "a=fmtp:18 annexb=no\r\n" \
467 "a=rtpmap:4 G723/8000\r\n" \
468 "a=rtpmap:96 iLBC/8000\r\n" \
469 "a=fmtp:96 mode=20\r\n" \
470 "a=rtpmap:97 iLBC/8000\r\n" \
471 "a=fmtp:97 mode=30\r\n" \
472 "a=rtpmap:101 telephone-event/8000\r\n" \
473 "a=fmtp:101 0-15\r\n" \
474 "a=recvonly\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200475
Philipp Maier87bd9be2017-08-22 16:35:41 +0200476#define MDCX_NAT_DUMMY \
477 "MDCX 23 5@mgw MGCP 1.0\r\n" \
478 "C: 1355c6041e\r\n" \
Philipp Maierffd75e42017-11-22 11:44:50 +0100479 "I: %s\r\n" \
Philipp Maier87bd9be2017-08-22 16:35:41 +0200480 "\r\n" \
481 "c=IN IP4 8.8.8.8\r\n" \
Philipp Maierbc0346e2018-06-07 09:52:16 +0200482 "m=audio 16434 RTP/AVP 3\r\n"
483
484#define CRCX_NO_LCO_NO_SDP \
485 "CRCX 2 6@mgw MGCP 1.0\r\n" \
486 "M: recvonly\r\n" \
487 "C: 2\r\n"
488
Philipp Maier228e5912019-03-05 13:56:59 +0100489#define CRCX_AMR_WITH_FMTP \
490 "CRCX 2 7@mgw MGCP 1.0\r\n" \
491 "M: recvonly\r\n" \
492 "C: 2\r\n" \
493 "X\r\n" \
494 "L: p:20\r\n" \
495 "\r\n" \
496 "v=0\r\n" \
497 "c=IN IP4 123.12.12.123\r\n" \
498 "m=audio 5904 RTP/AVP 111\r\n" \
499 "a=rtpmap:111 AMR/8000/1\r\n" \
500 "a=ptime:20\r\n" \
501 "a=fmtp:111 mode-change-capability=2; octet-align=1\r\n" \
502
503#define CRCX_AMR_WITH_FMTP_RET \
504 "200 2 OK\r\n" \
505 "I: %s\r\n" \
506 "\r\n" \
507 "v=0\r\n" \
508 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
509 "s=-\r\n" \
510 "c=IN IP4 0.0.0.0\r\n" \
511 "t=0 0\r\n" \
512 "m=audio 16012 RTP/AVP 111\r\n" \
513 "a=rtpmap:111 AMR/8000/1\r\n" \
514 "a=fmtp:111 octet-align=1\r\n" \
515 "a=ptime:20\r\n"
516
Philipp Maierbc0346e2018-06-07 09:52:16 +0200517#define CRCX_NO_LCO_NO_SDP_RET \
518 "200 2 OK\r\n" \
519 "I: %s\r\n" \
520 "\r\n" \
521 "v=0\r\n" \
522 "o=- %s 23 IN IP4 0.0.0.0\r\n" \
523 "s=-\r\n" \
524 "c=IN IP4 0.0.0.0\r\n" \
525 "t=0 0\r\n" \
526 "m=audio 16008 RTP/AVP 0\r\n" \
527 "a=ptime:20\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200528
529struct mgcp_test {
530 const char *name;
531 const char *req;
532 const char *exp_resp;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200533 int ptype;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200534 const char *extra_fmtp;
535};
536
537static const struct mgcp_test tests[] = {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200538 {"AUEP1", AUEP1, AUEP1_RET},
539 {"AUEP2", AUEP2, AUEP2_RET},
540 {"MDCX1", MDCX_WRONG_EP, MDCX_ERR_RET},
541 {"MDCX2", MDCX_UNALLOCATED, MDCX_RET},
542 {"CRCX", CRCX, CRCX_RET, 97},
543 {"MDCX3", MDCX3, MDCX3_RET, PTYPE_IGNORE},
544 {"MDCX4", MDCX4, MDCX4_RET("18983216"), 99},
545 {"MDCX4_PT1", MDCX4_PT1, MDCX4_RET("18983217"), 99},
546 {"MDCX4_PT2", MDCX4_PT2, MDCX4_RET("18983218"), 99},
547 {"MDCX4_PT3", MDCX4_PT3, MDCX4_RET("18983219"), 99},
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200548 {"MDCX4_PT4", MDCX4_PT4, MDCX4_RET("18983220"), 99},
549 {"MDCX4_SO", MDCX4_SO, MDCX4_RET("18983221"), 99},
550 {"MDCX4_RO", MDCX4_RO, MDCX4_RO_RET("18983222"), PTYPE_IGNORE},
Philipp Maier87bd9be2017-08-22 16:35:41 +0200551 {"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
552 {"CRCX_ZYN", CRCX_ZYN, CRCX_ZYN_RET, 97},
553 {"EMPTY", EMPTY, EMPTY_RET},
554 {"SHORT1", SHORT, SHORT_RET},
555 {"SHORT2", SHORT2, SHORT2_RET},
556 {"SHORT3", SHORT3, SHORT2_RET},
557 {"SHORT4", SHORT4, SHORT2_RET},
558 {"RQNT1", RQNT, RQNT1_RET},
559 {"RQNT2", RQNT2, RQNT2_RET},
560 {"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE},
561 {"CRCX", CRCX, CRCX_FMTP_RET, 97,.extra_fmtp = "a=fmtp:126 0/1/2"},
562 {"MDCX3", MDCX3, MDCX3_FMTP_RET, PTYPE_NONE,.extra_fmtp =
563 "a=fmtp:126 0/1/2"},
564 {"DLCX", DLCX, DLCX_RET, PTYPE_IGNORE,.extra_fmtp = "a=fmtp:126 0/1/2"},
Philipp Maierbc0346e2018-06-07 09:52:16 +0200565 {"CRCX", CRCX_NO_LCO_NO_SDP, CRCX_NO_LCO_NO_SDP_RET, 97},
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200566 {"CRCX", CRCX_X_OSMO_IGN, CRCX_X_OSMO_IGN_RET, 97},
Neels Hofmeyr5336f572018-09-03 22:05:48 +0200567 {"MDCX_TOO_LONG_CI", MDCX_TOO_LONG_CI, MDCX_TOO_LONG_CI_RET},
Philipp Maier228e5912019-03-05 13:56:59 +0100568 {"CRCX", CRCX_AMR_WITH_FMTP, CRCX_AMR_WITH_FMTP_RET},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200569};
570
571static const struct mgcp_test retransmit[] = {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200572 {"CRCX", CRCX, CRCX_RET},
573 {"RQNT1", RQNT, RQNT1_RET},
574 {"RQNT2", RQNT2, RQNT2_RET},
575 {"MDCX3", MDCX3, MDCX3A_RET},
576 {"DLCX", DLCX, DLCX_RET},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200577};
578
Philipp Maierffd75e42017-11-22 11:44:50 +0100579static struct msgb *create_msg(const char *str, const char *conn_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200580{
581 struct msgb *msg;
Philipp Maierffd75e42017-11-22 11:44:50 +0100582 int len;
583
584 printf("creating message from statically defined input:\n");
585 printf("---------8<---------\n%s\n---------8<---------\n", str);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200586
587 msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
Philipp Maierffd75e42017-11-22 11:44:50 +0100588 if (conn_id && strlen(conn_id))
589 len = sprintf((char *)msg->data, str, conn_id, conn_id);
590 else
591 len = sprintf((char *)msg->data, "%s", str);
592
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200593 msg->l2h = msgb_put(msg, len);
594 return msg;
595}
596
Philipp Maier37a808c2020-07-03 15:48:31 +0200597static char last_endpoint[MGCP_ENDPOINT_MAXLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200598
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200599static int mgcp_test_policy_cb(struct mgcp_endpoint *endp,
600 int state, const char *transaction_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200601{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200602 unsigned int i;
603 struct mgcp_trunk *trunk;
604
605 fprintf(stderr, "Policy CB got state %d on endpoint %s\n",
606 state, endp->name);
607
608 trunk = endp->trunk;
Philipp Maier37a808c2020-07-03 15:48:31 +0200609 last_endpoint[0] = '\0';
Philipp Maier869b21c2020-07-03 16:04:16 +0200610 for (i = 0; i < trunk->number_endpoints; i++) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200611 if (strcmp(endp->name, trunk->endpoints[i]->name) == 0)
Philipp Maier37a808c2020-07-03 15:48:31 +0200612 osmo_strlcpy(last_endpoint, trunk->endpoints[i]->name,
613 sizeof(last_endpoint));
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200614 }
615
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200616 return MGCP_POLICY_CONT;
617}
618
619#define MGCP_DUMMY_LOAD 0x23
620static int dummy_packets = 0;
621/* override and forward */
622ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200623 const struct sockaddr *dest_addr, socklen_t addrlen)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200624{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200625 uint32_t dest_host =
626 htonl(((struct sockaddr_in *)dest_addr)->sin_addr.s_addr);
627 int dest_port = htons(((struct sockaddr_in *)dest_addr)->sin_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200628
Philipp Maier87bd9be2017-08-22 16:35:41 +0200629 if (len == 1 && ((const char *)buf)[0] == MGCP_DUMMY_LOAD) {
630 fprintf(stderr,
631 "Dummy packet to 0x%08x:%d, msg length %zu\n%s\n\n",
632 dest_host, dest_port, len, osmo_hexdump(buf, len));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200633 dummy_packets += 1;
634 }
635
Philipp Maier3d9b6562017-10-13 18:33:44 +0200636 return len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200637}
638
639static int64_t force_monotonic_time_us = -1;
640/* override and forward */
641int clock_gettime(clockid_t clk_id, struct timespec *tp)
642{
643 typedef int (*clock_gettime_t)(clockid_t clk_id, struct timespec *tp);
644 static clock_gettime_t real_clock_gettime = NULL;
645
646 if (!real_clock_gettime)
647 real_clock_gettime = dlsym(RTLD_NEXT, "clock_gettime");
648
649 if (clk_id == CLOCK_MONOTONIC && force_monotonic_time_us >= 0) {
650 tp->tv_sec = force_monotonic_time_us / 1000000;
651 tp->tv_nsec = (force_monotonic_time_us % 1000000) * 1000;
652 return 0;
653 }
654
655 return real_clock_gettime(clk_id, tp);
656}
657
Philipp Maier14b27a82020-06-02 20:15:30 +0200658static void mgcp_endpoints_release(struct mgcp_trunk *trunk)
Pau Espin Pedrold071a302019-09-19 17:39:31 +0200659{
660 int i;
Philipp Maier869b21c2020-07-03 16:04:16 +0200661 for (i = 0; i < trunk->number_endpoints; i++)
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200662 mgcp_endp_release(trunk->endpoints[i]);
Pau Espin Pedrold071a302019-09-19 17:39:31 +0200663}
664
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200665#define CONN_UNMODIFIED (0x1000)
666
667static void test_values(void)
668{
669 /* Check that NONE disables all output */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200670 OSMO_ASSERT((MGCP_CONN_NONE & MGCP_CONN_RECV_SEND) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200671
672 /* Check that LOOPBACK enables all output */
673 OSMO_ASSERT((MGCP_CONN_LOOPBACK & MGCP_CONN_RECV_SEND) ==
Philipp Maier87bd9be2017-08-22 16:35:41 +0200674 MGCP_CONN_RECV_SEND);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200675}
676
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200677/* Extract a connection ID from a response and return in conn_id;
678 * if there is none, return -EINVAL and leave conn_id unchanged. */
Philipp Maierffd75e42017-11-22 11:44:50 +0100679static int get_conn_id_from_response(uint8_t *resp, char *conn_id,
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200680 size_t conn_id_buflen)
Philipp Maierffd75e42017-11-22 11:44:50 +0100681{
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200682 const char *conn_id_start;
683 const char *conn_id_end;
684 int conn_id_len;
Philipp Maierffd75e42017-11-22 11:44:50 +0100685
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200686 const char *header_I = "\r\nI: ";
687 const char *header_o = "\r\no=- ";
Philipp Maierffd75e42017-11-22 11:44:50 +0100688
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200689 /* Try to get the conn_id from the 'I:' or 'o=-' parameter */
690 if ((conn_id_start = strstr((char *)resp, header_I))) {
691 conn_id_start += strlen(header_I);
692 conn_id_end = strstr(conn_id_start, "\r\n");
693 } else if ((conn_id_start = strstr((char *)resp, header_o))) {
694 conn_id_start += strlen(header_o);
695 conn_id_end = strchr(conn_id_start, ' ');
696 } else
697 return -EINVAL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100698
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200699 if (conn_id_end)
700 conn_id_len = conn_id_end - conn_id_start;
701 else
702 conn_id_len = strlen(conn_id_start);
703 OSMO_ASSERT(conn_id_len <= conn_id_buflen - 1);
Philipp Maier55295f72018-01-15 14:00:28 +0100704
Neels Hofmeyre28b6732018-08-28 16:19:25 +0200705 /* A valid conn_id must at least contain one digit, and must
706 * not exceed a length of 32 digits */
707 OSMO_ASSERT(conn_id_len <= 32);
708 OSMO_ASSERT(conn_id_len > 0);
709
710 strncpy(conn_id, conn_id_start, conn_id_len);
711 conn_id[conn_id_len] = '\0';
712 return 0;
Philipp Maierffd75e42017-11-22 11:44:50 +0100713}
714
715/* Check response, automatically patch connection ID if needed */
716static int check_response(uint8_t *resp, const char *exp_resp)
717{
718 char exp_resp_patched[4096];
719 const char *exp_resp_ptr;
720 char conn_id[256];
721
722 printf("checking response:\n");
723
724 /* If the expected response is intened to be patched
725 * (%s placeholder inside) we will patch it with the
726 * connection identifier we just received from the
727 * real response. This is necessary because the CI
728 * is generated by the mgcp code on CRCX and we can
729 * not know it in advance */
730 if (strstr(exp_resp, "%s")) {
731 if (get_conn_id_from_response(resp, conn_id, sizeof(conn_id)) ==
732 0) {
733 sprintf(exp_resp_patched, exp_resp, conn_id, conn_id);
734 exp_resp_ptr = exp_resp_patched;
735 printf
736 ("using message with patched conn_id for comparison\n");
737 } else {
738 printf
739 ("patching conn_id failed, using message as statically defined for comparison\n");
740 exp_resp_ptr = exp_resp;
741 }
742 } else {
743 printf("using message as statically defined for comparison\n");
744 exp_resp_ptr = exp_resp;
745 }
746
747 if (strcmp((char *)resp, exp_resp_ptr) != 0) {
748 printf("Unexpected response, please check!\n");
749 printf
750 ("Got:\n---------8<---------\n%s\n---------8<---------\n\n",
751 resp);
752 printf
753 ("Expected:\n---------8<---------\n%s\n---------8<---------\n",
754 exp_resp_ptr);
755 return -EINVAL;
756 }
757
758 printf("Response matches our expectations.\n");
759 return 0;
760}
761
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200762static void test_messages(void)
763{
764 struct mgcp_config *cfg;
765 struct mgcp_endpoint *endp;
Philipp Maierd19de2e2020-06-03 13:55:33 +0200766 struct mgcp_trunk *trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200767 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200768 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100769 char last_conn_id[256];
Philipp Maier7df419b2017-12-04 17:11:42 +0100770 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200771
772 cfg = mgcp_config_alloc();
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200773 trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200774
Philipp Maier889fe7f2020-07-06 17:44:12 +0200775 trunk->v.vty_number_endpoints = 64;
776 mgcp_trunk_equip(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200777 cfg->policy_cb = mgcp_test_policy_cb;
778
Philipp Maierffd75e42017-11-22 11:44:50 +0100779 memset(last_conn_id, 0, sizeof(last_conn_id));
780
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200781 for (i = 0; i < ARRAY_SIZE(tests); i++) {
782 const struct mgcp_test *t = &tests[i];
783 struct msgb *inp;
784 struct msgb *msg;
785
Philipp Maierffd75e42017-11-22 11:44:50 +0100786 printf("\n================================================\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200787 printf("Testing %s\n", t->name);
788
Philipp Maier37a808c2020-07-03 15:48:31 +0200789 last_endpoint[0] = '\0';
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200790 dummy_packets = 0;
791
Philipp Maierd19de2e2020-06-03 13:55:33 +0200792 osmo_talloc_replace_string(cfg, &trunk->audio_fmtp_extra,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200793 t->extra_fmtp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200794
Philipp Maierffd75e42017-11-22 11:44:50 +0100795 inp = create_msg(t->req, last_conn_id);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200796 msg = mgcp_handle_message(cfg, inp);
797 msgb_free(inp);
798 if (!t->exp_resp) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200799 if (msg) {
800 printf("%s failed '%s'\n", t->name,
801 (char *)msg->data);
802 OSMO_ASSERT(false);
803 }
Philipp Maierffd75e42017-11-22 11:44:50 +0100804 } else if (check_response(msg->data, t->exp_resp) != 0) {
805 printf("%s failed.\n", t->name);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200806 OSMO_ASSERT(false);
807 }
Philipp Maierffd75e42017-11-22 11:44:50 +0100808
Philipp Maier7df419b2017-12-04 17:11:42 +0100809 if (msg) {
810 rc = get_conn_id_from_response(msg->data, last_conn_id,
811 sizeof(last_conn_id));
Neels Hofmeyr08e07042018-08-28 16:22:14 +0200812 if (rc == 0)
Philipp Maier7df419b2017-12-04 17:11:42 +0100813 printf("(response contains a connection id)\n");
814 else
815 printf("(response does not contain a connection id)\n");
816 }
Philipp Maierffd75e42017-11-22 11:44:50 +0100817
Philipp Maiera330b862017-12-04 17:16:16 +0100818 if (msg)
819 msgb_free(msg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200820
821 if (dummy_packets)
822 printf("Dummy packets: %d\n", dummy_packets);
823
Philipp Maier37a808c2020-07-03 15:48:31 +0200824 if (last_endpoint[0] != '\0') {
825 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
826 OSMO_ASSERT(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200827
Philipp Maier01d24a32017-11-21 17:26:09 +0100828 conn = mgcp_conn_get_rtp(endp, "1");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200829 if (conn) {
830 OSMO_ASSERT(conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200831
Philipp Maier87bd9be2017-08-22 16:35:41 +0200832 if (conn->end.packet_duration_ms != -1)
833 printf("Detected packet duration: %d\n",
834 conn->end.packet_duration_ms);
835 else
836 printf("Packet duration not set\n");
837 if (endp->local_options.pkt_period_min ||
838 endp->local_options.pkt_period_max)
839 printf
840 ("Requested packetetization period: "
841 "%d-%d\n",
842 endp->local_options.pkt_period_min,
843 endp->
844 local_options.pkt_period_max);
845 else
846 printf
847 ("Requested packetization period not set\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200848
Philipp Maier87bd9be2017-08-22 16:35:41 +0200849 if ((conn->conn->mode & CONN_UNMODIFIED) == 0) {
850 printf("Connection mode: %d:%s%s%s%s\n",
851 conn->conn->mode,
852 !conn->conn->mode ? " NONE" : "",
853 conn->conn->mode & MGCP_CONN_SEND_ONLY
854 ? " SEND" : "",
855 conn->conn->mode & MGCP_CONN_RECV_ONLY
856 ? " RECV" : "",
857 conn->conn->mode & MGCP_CONN_LOOPBACK
858 & ~MGCP_CONN_RECV_SEND
859 ? " LOOP" : "");
860 fprintf(stderr,
861 "RTP output %sabled, NET output %sabled\n",
862 conn->end.output_enabled
863 ? "en" : "dis",
864 conn->end.output_enabled
865 ? "en" : "dis");
866 } else
867 printf("Connection mode not set\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200868
Philipp Maier87bd9be2017-08-22 16:35:41 +0200869 OSMO_ASSERT(conn->end.output_enabled
870 == (conn->conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0));
871
872 conn->conn->mode |= CONN_UNMODIFIED;
873
874 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200875 endp->local_options.pkt_period_min = 0;
876 endp->local_options.pkt_period_max = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200877 }
878
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200879 /* Check detected payload type */
Philipp Maierffd75e42017-11-22 11:44:50 +0100880 if (conn && t->ptype != PTYPE_IGNORE) {
Philipp Maier37a808c2020-07-03 15:48:31 +0200881 OSMO_ASSERT(last_endpoint[0] != '\0');
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200882
Philipp Maier37a808c2020-07-03 15:48:31 +0200883 fprintf(stderr, "endpoint:%s: "
Philipp Maier87bd9be2017-08-22 16:35:41 +0200884 "payload type %d (expected %d)\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200885 last_endpoint,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200886 conn->end.codec->payload_type, t->ptype);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200887
Philipp Maier87bd9be2017-08-22 16:35:41 +0200888 if (t->ptype != PTYPE_IGNORE)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200889 OSMO_ASSERT(conn->end.codec->payload_type ==
Philipp Maier87bd9be2017-08-22 16:35:41 +0200890 t->ptype);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200891
892 /* Reset them again for next test */
Philipp Maierbc0346e2018-06-07 09:52:16 +0200893 conn->end.codec->payload_type = PTYPE_NONE;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200894 }
895 }
896
Philipp Maierd19de2e2020-06-03 13:55:33 +0200897 mgcp_endpoints_release(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200898 talloc_free(cfg);
899}
900
901static void test_retransmission(void)
902{
903 struct mgcp_config *cfg;
Philipp Maierd19de2e2020-06-03 13:55:33 +0200904 struct mgcp_trunk *trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200905 int i;
Philipp Maierffd75e42017-11-22 11:44:50 +0100906 char last_conn_id[256];
Philipp Maier23b8e292017-12-04 16:48:45 +0100907 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200908
909 cfg = mgcp_config_alloc();
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200910 trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200911
Philipp Maier889fe7f2020-07-06 17:44:12 +0200912 trunk->v.vty_number_endpoints = 64;
913 mgcp_trunk_equip(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200914
Philipp Maierffd75e42017-11-22 11:44:50 +0100915 memset(last_conn_id, 0, sizeof(last_conn_id));
916
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200917 for (i = 0; i < ARRAY_SIZE(retransmit); i++) {
918 const struct mgcp_test *t = &retransmit[i];
919 struct msgb *inp;
920 struct msgb *msg;
921
Philipp Maierffd75e42017-11-22 11:44:50 +0100922 printf("\n================================================\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200923 printf("Testing %s\n", t->name);
924
Philipp Maierffd75e42017-11-22 11:44:50 +0100925 inp = create_msg(t->req, last_conn_id);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200926 msg = mgcp_handle_message(cfg, inp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200927
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200928 msgb_free(inp);
Philipp Maier7cedfd72017-12-04 16:49:12 +0100929 if (msg && check_response(msg->data, t->exp_resp) != 0) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200930 printf("%s failed '%s'\n", t->name, (char *)msg->data);
931 OSMO_ASSERT(false);
932 }
Philipp Maierffd75e42017-11-22 11:44:50 +0100933
Philipp Maier23b8e292017-12-04 16:48:45 +0100934 if (msg && strcmp(t->name, "CRCX") == 0) {
935 rc = get_conn_id_from_response(msg->data, last_conn_id,
936 sizeof(last_conn_id));
937 OSMO_ASSERT(rc == 0);
938 }
Philipp Maierffd75e42017-11-22 11:44:50 +0100939
Philipp Maier7cedfd72017-12-04 16:49:12 +0100940 if (msg)
941 msgb_free(msg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200942
943 /* Retransmit... */
944 printf("Re-transmitting %s\n", t->name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100945 inp = create_msg(t->req, last_conn_id);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200946 msg = mgcp_handle_message(cfg, inp);
947 msgb_free(inp);
Philipp Maierffd75e42017-11-22 11:44:50 +0100948 if (check_response(msg->data, t->exp_resp) != 0) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200949 printf("%s failed '%s'\n", t->name, (char *)msg->data);
950 OSMO_ASSERT(false);
951 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200952 msgb_free(msg);
953 }
954
Philipp Maierd19de2e2020-06-03 13:55:33 +0200955 mgcp_endpoints_release(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200956 talloc_free(cfg);
957}
958
959static int rqnt_cb(struct mgcp_endpoint *endp, char _tone)
960{
961 ptrdiff_t tone = _tone;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200962 endp->cfg->data = (void *)tone;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200963 return 0;
964}
965
966static void test_rqnt_cb(void)
967{
968 struct mgcp_config *cfg;
Philipp Maierd19de2e2020-06-03 13:55:33 +0200969 struct mgcp_trunk *trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200970 struct msgb *inp, *msg;
Philipp Maierffd75e42017-11-22 11:44:50 +0100971 char conn_id[256];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200972
973 cfg = mgcp_config_alloc();
Philipp Maier6fbbeec2020-07-01 23:00:54 +0200974 trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200975 cfg->rqnt_cb = rqnt_cb;
976
Philipp Maier889fe7f2020-07-06 17:44:12 +0200977 trunk->v.vty_number_endpoints = 64;
978 mgcp_trunk_equip(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200979
Philipp Maierffd75e42017-11-22 11:44:50 +0100980 inp = create_msg(CRCX, NULL);
981 msg = mgcp_handle_message(cfg, inp);
982 OSMO_ASSERT(msg);
983 OSMO_ASSERT(get_conn_id_from_response(msg->data, conn_id,
984 sizeof(conn_id)) == 0);
985 msgb_free(msg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200986 msgb_free(inp);
987
988 /* send the RQNT and check for the CB */
Philipp Maierffd75e42017-11-22 11:44:50 +0100989 inp = create_msg(RQNT, conn_id);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200990 msg = mgcp_handle_message(cfg, inp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200991 if (strncmp((const char *)msg->l2h, "200", 3) != 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200992 printf("FAILED: message is not 200. '%s'\n", msg->l2h);
993 abort();
994 }
995
Philipp Maier87bd9be2017-08-22 16:35:41 +0200996 if (cfg->data != (void *)'9') {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200997 printf("FAILED: callback not called: %p\n", cfg->data);
998 abort();
999 }
1000
1001 msgb_free(msg);
1002 msgb_free(inp);
1003
Philipp Maierffd75e42017-11-22 11:44:50 +01001004 inp = create_msg(DLCX, conn_id);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001005 msgb_free(mgcp_handle_message(cfg, inp));
1006 msgb_free(inp);
Philipp Maierd19de2e2020-06-03 13:55:33 +02001007 mgcp_endpoints_release(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001008 talloc_free(cfg);
1009}
1010
1011struct pl_test {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001012 int cycles;
1013 uint16_t base_seq;
1014 uint16_t max_seq;
1015 uint32_t packets;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001016
Philipp Maier87bd9be2017-08-22 16:35:41 +02001017 uint32_t expected;
1018 int loss;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001019};
1020
1021static const struct pl_test pl_test_dat[] = {
1022 /* basic.. just one package */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001023 {.cycles = 0,.base_seq = 0,.max_seq = 0,.packets = 1,.expected =
1024 1,.loss = 0},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001025 /* some packages and a bit of loss */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001026 {.cycles = 0,.base_seq = 0,.max_seq = 100,.packets = 100,.expected =
1027 101,.loss = 1},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001028 /* wrap around */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001029 {.cycles = 1 << 16,.base_seq = 0xffff,.max_seq = 2,.packets =
1030 4,.expected = 4,.loss = 0},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001031 /* min loss */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001032 {.cycles = 0,.base_seq = 0,.max_seq = 0,.packets = UINT_MAX,.expected =
1033 1,.loss = INT_MIN},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001034 /* max loss, with wrap around on expected max */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001035 {.cycles = INT_MAX,.base_seq = 0,.max_seq = UINT16_MAX,.packets =
1036 0,.expected = ((uint32_t) (INT_MAX) + UINT16_MAX + 1),.loss = INT_MAX},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001037};
1038
1039static void test_packet_loss_calc(void)
1040{
1041 int i;
Philipp Maiercede2a42018-07-03 14:14:21 +02001042 struct mgcp_endpoint endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001043 struct mgcp_endpoint *endpoints[1];
Oliver Smithe36b7752019-01-22 16:31:36 +01001044 struct mgcp_config cfg = {0};
Philipp Maier14b27a82020-06-02 20:15:30 +02001045 struct mgcp_trunk trunk;
Philipp Maiercede2a42018-07-03 14:14:21 +02001046
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001047 printf("Testing packet loss calculation.\n");
1048
Philipp Maiercede2a42018-07-03 14:14:21 +02001049 memset(&endp, 0, sizeof(endp));
1050 memset(&trunk, 0, sizeof(trunk));
1051
Oliver Smithe36b7752019-01-22 16:31:36 +01001052 endp.cfg = &cfg;
Philipp Maiercede2a42018-07-03 14:14:21 +02001053 endp.type = &ep_typeset.rtp;
Philipp Maier889fe7f2020-07-06 17:44:12 +02001054 trunk.v.vty_number_endpoints = 1;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001055 trunk.endpoints = endpoints;
1056 trunk.endpoints[0] = &endp;
Philipp Maier14b27a82020-06-02 20:15:30 +02001057 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 Pedrol907744e2021-06-04 17:57:34 +02001074 packets_rx = rate_ctr_group_get_ctr(conn->rate_ctr_group, 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
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001095}
1096
Philipp Maier87bd9be2017-08-22 16:35:41 +02001097int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os,
1098 uint32_t *pr, uint32_t *_or, int *loss,
1099 uint32_t *jitter)
1100{
1101 char *line, *save;
1102 int rc;
1103
1104 /* initialize with bad values */
1105 *ps = *os = *pr = *_or = *jitter = UINT_MAX;
1106 *loss = INT_MAX;
1107
1108 line = strtok_r((char *)msg->l2h, "\r\n", &save);
1109 if (!line)
1110 return -1;
1111
1112 /* this can only parse the message that is created above... */
1113 for_each_non_empty_line(line, save) {
1114 switch (line[0]) {
1115 case 'P':
1116 rc = sscanf(line,
1117 "P: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
1118 ps, os, pr, _or, loss, jitter);
1119 return rc == 6 ? 0 : -1;
1120 }
1121 }
1122
1123 return -1;
1124}
1125
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001126static void test_mgcp_stats(void)
1127{
1128 printf("Testing stat parsing\n");
1129
1130 uint32_t bps, bos, pr, _or, jitter;
1131 struct msgb *msg;
1132 int loss;
1133 int rc;
1134
Philipp Maierffd75e42017-11-22 11:44:50 +01001135 msg = create_msg(DLCX_RET, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001136 rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter);
1137 printf("Parsing result: %d\n", rc);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001138 if (bps != 0 || bos != 0 || pr != 0 || _or != 0 || loss != 0
1139 || jitter != 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001140 printf("FAIL: Parsing failed1.\n");
1141 msgb_free(msg);
1142
Philipp Maier87bd9be2017-08-22 16:35:41 +02001143 msg =
1144 create_msg
Philipp Maierffd75e42017-11-22 11:44:50 +01001145 ("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 +02001146 rc = mgcp_parse_stats(msg, &bps, &bos, &pr, &_or, &loss, &jitter);
1147 printf("Parsing result: %d\n", rc);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001148 if (bps != 10 || bos != 20 || pr != 30 || _or != 40 || loss != -3
1149 || jitter != 40)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001150 printf("FAIL: Parsing failed2.\n");
1151 msgb_free(msg);
1152}
1153
1154struct rtp_packet_info {
1155 float txtime;
1156 int len;
1157 char *data;
1158};
1159
1160struct rtp_packet_info test_rtp_packets1[] = {
1161 /* RTP: SeqNo=0, TS=0 */
1162 {0.000000, 20, "\x80\x62\x00\x00\x00\x00\x00\x00\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001163 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001164 /* RTP: SeqNo=1, TS=160 */
1165 {0.020000, 20, "\x80\x62\x00\x01\x00\x00\x00\xA0\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001166 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001167 /* RTP: SeqNo=2, TS=320 */
1168 {0.040000, 20, "\x80\x62\x00\x02\x00\x00\x01\x40\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001169 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001170 /* Repeat RTP timestamp: */
1171 /* RTP: SeqNo=3, TS=320 */
1172 {0.060000, 20, "\x80\x62\x00\x03\x00\x00\x01\x40\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001173 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001174 /* RTP: SeqNo=4, TS=480 */
1175 {0.080000, 20, "\x80\x62\x00\x04\x00\x00\x01\xE0\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001176 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001177 /* RTP: SeqNo=5, TS=640 */
1178 {0.100000, 20, "\x80\x62\x00\x05\x00\x00\x02\x80\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001179 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001180 /* Double skip RTP timestamp (delta = 2*160): */
1181 /* RTP: SeqNo=6, TS=960 */
1182 {0.120000, 20, "\x80\x62\x00\x06\x00\x00\x03\xC0\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001183 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001184 /* RTP: SeqNo=7, TS=1120 */
1185 {0.140000, 20, "\x80\x62\x00\x07\x00\x00\x04\x60\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001186 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001187 /* RTP: SeqNo=8, TS=1280 */
1188 {0.160000, 20, "\x80\x62\x00\x08\x00\x00\x05\x00\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001189 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001190 /* Non 20ms RTP timestamp (delta = 120): */
1191 /* RTP: SeqNo=9, TS=1400 */
1192 {0.180000, 20, "\x80\x62\x00\x09\x00\x00\x05\x78\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001193 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001194 /* RTP: SeqNo=10, TS=1560 */
1195 {0.200000, 20, "\x80\x62\x00\x0A\x00\x00\x06\x18\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001196 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001197 /* RTP: SeqNo=11, TS=1720 */
1198 {0.220000, 20, "\x80\x62\x00\x0B\x00\x00\x06\xB8\x11\x22\x33\x44"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001199 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001200 /* SSRC changed to 0x10203040, RTP timestamp jump */
1201 /* RTP: SeqNo=12, TS=34688 */
1202 {0.240000, 20, "\x80\x62\x00\x0C\x00\x00\x87\x80\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001203 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001204 /* RTP: SeqNo=13, TS=34848 */
1205 {0.260000, 20, "\x80\x62\x00\x0D\x00\x00\x88\x20\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001206 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001207 /* RTP: SeqNo=14, TS=35008 */
1208 {0.280000, 20, "\x80\x62\x00\x0E\x00\x00\x88\xC0\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001209 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001210 /* Non 20ms RTP timestamp (delta = 120): */
1211 /* RTP: SeqNo=15, TS=35128 */
1212 {0.300000, 20, "\x80\x62\x00\x0F\x00\x00\x89\x38\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001213 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001214 /* RTP: SeqNo=16, TS=35288 */
1215 {0.320000, 20, "\x80\x62\x00\x10\x00\x00\x89\xD8\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001216 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001217 /* RTP: SeqNo=17, TS=35448 */
1218 {0.340000, 20, "\x80\x62\x00\x11\x00\x00\x8A\x78\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001219 "\x01\x23\x45\x67\x8A\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001220 /* SeqNo increment by 2, RTP timestamp delta = 320: */
1221 /* RTP: SeqNo=19, TS=35768 */
1222 {0.360000, 20, "\x80\x62\x00\x13\x00\x00\x8B\xB8\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001223 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001224 /* RTP: SeqNo=20, TS=35928 */
1225 {0.380000, 20, "\x80\x62\x00\x14\x00\x00\x8C\x58\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001226 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001227 /* RTP: SeqNo=21, TS=36088 */
1228 {0.380000, 20, "\x80\x62\x00\x15\x00\x00\x8C\xF8\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001229 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001230 /* Repeat last packet */
1231 /* RTP: SeqNo=21, TS=36088 */
1232 {0.400000, 20, "\x80\x62\x00\x15\x00\x00\x8C\xF8\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001233 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001234 /* RTP: SeqNo=22, TS=36248 */
1235 {0.420000, 20, "\x80\x62\x00\x16\x00\x00\x8D\x98\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001236 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001237 /* RTP: SeqNo=23, TS=36408 */
1238 {0.440000, 20, "\x80\x62\x00\x17\x00\x00\x8E\x38\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001239 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001240 /* Don't increment SeqNo but increment timestamp by 160 */
1241 /* RTP: SeqNo=23, TS=36568 */
1242 {0.460000, 20, "\x80\x62\x00\x17\x00\x00\x8E\xD8\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001243 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001244 /* RTP: SeqNo=24, TS=36728 */
1245 {0.480000, 20, "\x80\x62\x00\x18\x00\x00\x8F\x78\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001246 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001247 /* RTP: SeqNo=25, TS=36888 */
1248 {0.500000, 20, "\x80\x62\x00\x19\x00\x00\x90\x18\x10\x20\x30\x40"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001249 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001250 /* SSRC changed to 0x50607080, RTP timestamp jump, Delay of 1.5s,
1251 * SeqNo jump */
1252 /* RTP: SeqNo=1000, TS=160000 */
1253 {2.000000, 20, "\x80\x62\x03\xE8\x00\x02\x71\x00\x50\x60\x70\x80"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001254 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001255 /* RTP: SeqNo=1001, TS=160160 */
1256 {2.020000, 20, "\x80\x62\x03\xE9\x00\x02\x71\xA0\x50\x60\x70\x80"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001257 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001258 /* RTP: SeqNo=1002, TS=160320 */
1259 {2.040000, 20, "\x80\x62\x03\xEA\x00\x02\x72\x40\x50\x60\x70\x80"
Philipp Maier87bd9be2017-08-22 16:35:41 +02001260 "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001261};
1262
Philipp Maier87bd9be2017-08-22 16:35:41 +02001263void mgcp_patch_and_count(struct mgcp_endpoint *endp,
1264 struct mgcp_rtp_state *state,
1265 struct mgcp_rtp_end *rtp_end,
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +02001266 struct osmo_sockaddr *addr, struct msgb *msg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001267
1268static void test_packet_error_detection(int patch_ssrc, int patch_ts)
1269{
1270 int i;
1271
Philipp Maier14b27a82020-06-02 20:15:30 +02001272 struct mgcp_trunk trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001273 struct mgcp_endpoint endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001274 struct mgcp_endpoint *endpoints[1];
Oliver Smithe36b7752019-01-22 16:31:36 +01001275 struct mgcp_config cfg = {0};
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001276 struct mgcp_rtp_state state;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001277 struct mgcp_rtp_end *rtp;
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +02001278 struct osmo_sockaddr addr = { 0 };
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001279 uint32_t last_ssrc = 0;
1280 uint32_t last_timestamp = 0;
1281 uint32_t last_seqno = 0;
Philipp Maier9e1d1642018-05-09 16:26:34 +02001282 uint64_t last_in_ts_err_cnt = 0;
1283 uint64_t last_out_ts_err_cnt = 0;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001284 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +01001285 struct mgcp_conn *_conn = NULL;
Philipp Maier9e1d1642018-05-09 16:26:34 +02001286 struct rate_ctr test_ctr_in;
1287 struct rate_ctr test_ctr_out;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001288
1289 printf("Testing packet error detection%s%s.\n",
1290 patch_ssrc ? ", patch SSRC" : "",
1291 patch_ts ? ", patch timestamps" : "");
1292
1293 memset(&trunk, 0, sizeof(trunk));
1294 memset(&endp, 0, sizeof(endp));
1295 memset(&state, 0, sizeof(state));
1296
Philipp Maier9e1d1642018-05-09 16:26:34 +02001297 memset(&test_ctr_in, 0, sizeof(test_ctr_in));
1298 memset(&test_ctr_out, 0, sizeof(test_ctr_out));
1299 state.in_stream.err_ts_ctr = &test_ctr_in;
1300 state.out_stream.err_ts_ctr = &test_ctr_out;
1301
Oliver Smithe36b7752019-01-22 16:31:36 +01001302 endp.cfg = &cfg;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001303 endp.type = &ep_typeset.rtp;
1304
Philipp Maier889fe7f2020-07-06 17:44:12 +02001305 trunk.v.vty_number_endpoints = 1;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001306 trunk.endpoints = endpoints;
1307 trunk.endpoints[0] = &endp;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001308 trunk.force_constant_ssrc = patch_ssrc;
1309 trunk.force_aligned_timing = patch_ts;
1310
Philipp Maier14b27a82020-06-02 20:15:30 +02001311 endp.trunk = &trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001312
Philipp Maier87bd9be2017-08-22 16:35:41 +02001313 INIT_LLIST_HEAD(&endp.conns);
Philipp Maierffd75e42017-11-22 11:44:50 +01001314 _conn = mgcp_conn_alloc(NULL, &endp, MGCP_CONN_TYPE_RTP,
1315 "test-connection");
1316 OSMO_ASSERT(_conn);
1317 conn = mgcp_conn_get_rtp(&endp, _conn->id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001318 OSMO_ASSERT(conn);
1319
1320 rtp = &conn->end;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001321
Philipp Maier228e5912019-03-05 13:56:59 +01001322 OSMO_ASSERT(mgcp_codec_add(conn, PTYPE_UNDEFINED, "AMR/8000/1", NULL) == 0);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001323 rtp->codec = &rtp->codecs[0];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001324
1325 for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
1326 struct rtp_packet_info *info = test_rtp_packets1 + i;
Neels Hofmeyr51b42ff2020-06-19 01:34:42 +02001327 struct msgb *msg = msgb_alloc(4096, __func__);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001328
1329 force_monotonic_time_us = round(1000000.0 * info->txtime);
1330
Neels Hofmeyr51b42ff2020-06-19 01:34:42 +02001331 OSMO_ASSERT(info->len <= msgb_tailroom(msg));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001332 OSMO_ASSERT(info->len >= 0);
Neels Hofmeyr51b42ff2020-06-19 01:34:42 +02001333 msg->l3h = msgb_put(msg, info->len);
1334 memcpy((char*)msgb_l3(msg), info->data, info->len);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001335 mgcp_rtp_end_config(&endp, 1, rtp);
1336
Neels Hofmeyr51b42ff2020-06-19 01:34:42 +02001337 mgcp_patch_and_count(&endp, &state, rtp, &addr, msg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001338
1339 if (state.out_stream.ssrc != last_ssrc) {
1340 printf("Output SSRC changed to %08x\n",
1341 state.out_stream.ssrc);
1342 last_ssrc = state.out_stream.ssrc;
1343 }
1344
1345 printf("In TS: %d, dTS: %d, Seq: %d\n",
1346 state.in_stream.last_timestamp,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001347 state.in_stream.last_tsdelta, state.in_stream.last_seq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001348
1349 printf("Out TS change: %d, dTS: %d, Seq change: %d, "
Philipp Maier9e1d1642018-05-09 16:26:34 +02001350 "TS Err change: in +%u, out +%u\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001351 state.out_stream.last_timestamp - last_timestamp,
1352 state.out_stream.last_tsdelta,
1353 state.out_stream.last_seq - last_seqno,
Philipp Maier9e1d1642018-05-09 16:26:34 +02001354 (unsigned int) (state.in_stream.err_ts_ctr->current - last_in_ts_err_cnt),
1355 (unsigned int) (state.out_stream.err_ts_ctr->current - last_out_ts_err_cnt));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001356
1357 printf("Stats: Jitter = %u, Transit = %d\n",
Harald Welte49e3d5a2017-12-25 09:47:57 +01001358 calc_jitter(&state), state.stats.transit);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001359
Philipp Maier9e1d1642018-05-09 16:26:34 +02001360 last_in_ts_err_cnt = state.in_stream.err_ts_ctr->current;
1361 last_out_ts_err_cnt = state.out_stream.err_ts_ctr->current;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001362 last_timestamp = state.out_stream.last_timestamp;
1363 last_seqno = state.out_stream.last_seq;
Neels Hofmeyr51b42ff2020-06-19 01:34:42 +02001364
1365 msgb_free(msg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001366 }
1367
1368 force_monotonic_time_us = -1;
Neels Hofmeyrd20910c2017-11-18 21:27:50 +01001369 mgcp_conn_free_all(&endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001370}
1371
1372static void test_multilple_codec(void)
1373{
1374 struct mgcp_config *cfg;
Philipp Maierd19de2e2020-06-03 13:55:33 +02001375 struct mgcp_trunk *trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001376 struct mgcp_endpoint *endp;
1377 struct msgb *inp, *resp;
1378 struct in_addr addr;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001379 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +01001380 char conn_id[256];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001381
1382 printf("Testing multiple payload types\n");
1383
1384 cfg = mgcp_config_alloc();
Philipp Maier6fbbeec2020-07-01 23:00:54 +02001385 trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Philipp Maier889fe7f2020-07-06 17:44:12 +02001386 trunk->v.vty_number_endpoints = 64;
1387 mgcp_trunk_equip(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001388 cfg->policy_cb = mgcp_test_policy_cb;
Pau Espin Pedrold071a302019-09-19 17:39:31 +02001389
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001390 /* Allocate endpoint 1@mgw with two codecs */
Philipp Maier37a808c2020-07-03 15:48:31 +02001391 last_endpoint[0] = '\0';
Philipp Maierffd75e42017-11-22 11:44:50 +01001392 inp = create_msg(CRCX_MULT_1, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001393 resp = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001394 OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
1395 sizeof(conn_id)) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001396 msgb_free(inp);
1397 msgb_free(resp);
1398
Philipp Maier37a808c2020-07-03 15:48:31 +02001399 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/1@mgw") == 0);
1400 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1401 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001402 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001403 OSMO_ASSERT(conn);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001404 OSMO_ASSERT(conn->end.codec->payload_type == 18);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001405
1406 /* Allocate 2@mgw with three codecs, last one ignored */
Philipp Maier37a808c2020-07-03 15:48:31 +02001407 last_endpoint[0] = '\0';
Philipp Maierffd75e42017-11-22 11:44:50 +01001408 inp = create_msg(CRCX_MULT_2, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001409 resp = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001410 OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
1411 sizeof(conn_id)) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001412 msgb_free(inp);
1413 msgb_free(resp);
1414
Philipp Maier37a808c2020-07-03 15:48:31 +02001415 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/2@mgw") == 0);
1416 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1417 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001418 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001419 OSMO_ASSERT(conn);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001420 OSMO_ASSERT(conn->end.codec->payload_type == 18);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001421
Philipp Maierbc0346e2018-06-07 09:52:16 +02001422 /* Allocate 3@mgw with no codecs, check for PT == 0 */
1423 /* Note: It usually makes no sense to leave the payload type list
1424 * out. However RFC 2327 does not clearly forbid this case and
1425 * it makes and since we already decided in OS#2658 that a missing
1426 * LCO should pick a sane default codec, it makes sense to expect
1427 * the same behaviour if SDP lacks proper payload type information */
Philipp Maier37a808c2020-07-03 15:48:31 +02001428 last_endpoint[0] = '\0';
Philipp Maierffd75e42017-11-22 11:44:50 +01001429 inp = create_msg(CRCX_MULT_3, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001430 resp = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001431 OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
1432 sizeof(conn_id)) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001433 msgb_free(inp);
1434 msgb_free(resp);
1435
Philipp Maier37a808c2020-07-03 15:48:31 +02001436 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/3@mgw") == 0);
1437 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1438 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001439 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001440 OSMO_ASSERT(conn);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001441 OSMO_ASSERT(conn->end.codec->payload_type == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001442
1443 /* Allocate 4@mgw with a single codec */
Philipp Maier37a808c2020-07-03 15:48:31 +02001444 last_endpoint[0] = '\0';
Philipp Maierffd75e42017-11-22 11:44:50 +01001445 inp = create_msg(CRCX_MULT_4, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001446 resp = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001447 OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
1448 sizeof(conn_id)) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001449 msgb_free(inp);
1450 msgb_free(resp);
1451
Philipp Maier37a808c2020-07-03 15:48:31 +02001452 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/4@mgw") == 0);
1453 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1454 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001455 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001456 OSMO_ASSERT(conn);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001457 OSMO_ASSERT(conn->end.codec->payload_type == 18);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001458
Philipp Maier7f90ddb2020-06-02 21:52:53 +02001459 /* Allocate 5@mgw and let osmo-mgw pick a codec from the list */
Philipp Maier37a808c2020-07-03 15:48:31 +02001460 last_endpoint[0] = '\0';
Philipp Maierffd75e42017-11-22 11:44:50 +01001461 inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
Philipp Maierd19de2e2020-06-03 13:55:33 +02001462 trunk->no_audio_transcoding = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001463 resp = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001464 OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
1465 sizeof(conn_id)) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001466 msgb_free(inp);
1467 msgb_free(resp);
1468
Philipp Maier37a808c2020-07-03 15:48:31 +02001469 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
1470 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1471 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001472 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001473 OSMO_ASSERT(conn);
Philipp Maier7f90ddb2020-06-02 21:52:53 +02001474 OSMO_ASSERT(conn->end.codec->payload_type == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001475
Philipp Maierffd75e42017-11-22 11:44:50 +01001476 inp = create_msg(MDCX_NAT_DUMMY, conn_id);
Philipp Maier37a808c2020-07-03 15:48:31 +02001477 last_endpoint[0] = '\0';
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001478 resp = mgcp_handle_message(cfg, inp);
1479 msgb_free(inp);
1480 msgb_free(resp);
Philipp Maier37a808c2020-07-03 15:48:31 +02001481 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
1482 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1483 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001484 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001485 OSMO_ASSERT(conn);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001486 OSMO_ASSERT(conn->end.codec->payload_type == 3);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001487 OSMO_ASSERT(conn->end.rtp_port == htons(16434));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001488 memset(&addr, 0, sizeof(addr));
1489 inet_aton("8.8.8.8", &addr);
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +02001490 OSMO_ASSERT(conn->end.addr.u.sa.sa_family == AF_INET);
1491 OSMO_ASSERT(conn->end.addr.u.sin.sin_addr.s_addr == addr.s_addr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001492
1493 /* Check what happens without that flag */
1494
Philipp Maier87bd9be2017-08-22 16:35:41 +02001495 /* Free the previous endpoint and the data and
1496 * check if the connection really vanished... */
Philipp Maier1355d7e2018-02-01 14:30:06 +01001497 mgcp_endp_release(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001498 talloc_free(endp->last_response);
1499 talloc_free(endp->last_trans);
1500 endp->last_response = endp->last_trans = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +01001501 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001502 OSMO_ASSERT(!conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001503
Philipp Maier37a808c2020-07-03 15:48:31 +02001504 last_endpoint[0] = '\0';
Philipp Maierffd75e42017-11-22 11:44:50 +01001505 inp = create_msg(CRCX_MULT_GSM_EXACT, NULL);
Philipp Maierd19de2e2020-06-03 13:55:33 +02001506 trunk->no_audio_transcoding = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001507 resp = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001508 OSMO_ASSERT(get_conn_id_from_response(resp->data, conn_id,
1509 sizeof(conn_id)) == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001510 msgb_free(inp);
1511 msgb_free(resp);
1512
Philipp Maier37a808c2020-07-03 15:48:31 +02001513 OSMO_ASSERT(strcmp(last_endpoint,"rtpbridge/5@mgw") == 0);
1514 endp = mgcp_endp_by_name(NULL, last_endpoint, cfg);
1515 OSMO_ASSERT(endp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001516 conn = mgcp_conn_get_rtp(endp, conn_id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001517 OSMO_ASSERT(conn);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001518 OSMO_ASSERT(conn->end.codec->payload_type == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001519
Philipp Maierd19de2e2020-06-03 13:55:33 +02001520 mgcp_endpoints_release(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001521 talloc_free(cfg);
1522}
1523
1524static void test_no_cycle(void)
1525{
1526 struct mgcp_config *cfg;
1527 struct mgcp_endpoint *endp;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001528 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +01001529 struct mgcp_conn *_conn = NULL;
Philipp Maierd19de2e2020-06-03 13:55:33 +02001530 struct mgcp_trunk *trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001531
1532 printf("Testing no sequence flow on initial packet\n");
1533
1534 cfg = mgcp_config_alloc();
Philipp Maier6fbbeec2020-07-01 23:00:54 +02001535 trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Philipp Maier889fe7f2020-07-06 17:44:12 +02001536 trunk->v.vty_number_endpoints = 64;
1537 mgcp_trunk_equip(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001538
Philipp Maier37a808c2020-07-03 15:48:31 +02001539 endp = mgcp_endp_by_name(NULL, "rtpbridge/1@mgw", cfg);
1540 OSMO_ASSERT(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001541
Philipp Maierffd75e42017-11-22 11:44:50 +01001542 _conn = mgcp_conn_alloc(NULL, endp, MGCP_CONN_TYPE_RTP,
1543 "test-connection");
1544 OSMO_ASSERT(_conn);
1545 conn = mgcp_conn_get_rtp(endp, _conn->id);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001546 OSMO_ASSERT(conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001547
Harald Welte49e3d5a2017-12-25 09:47:57 +01001548 OSMO_ASSERT(conn->state.stats.initialized == 0);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001549
1550 mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342);
Harald Welte49e3d5a2017-12-25 09:47:57 +01001551 OSMO_ASSERT(conn->state.stats.initialized == 1);
1552 OSMO_ASSERT(conn->state.stats.cycles == 0);
1553 OSMO_ASSERT(conn->state.stats.max_seq == 0);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001554
1555 mgcp_rtp_annex_count(endp, &conn->state, 1, 0, 2342);
Harald Welte49e3d5a2017-12-25 09:47:57 +01001556 OSMO_ASSERT(conn->state.stats.initialized == 1);
1557 OSMO_ASSERT(conn->state.stats.cycles == 0);
1558 OSMO_ASSERT(conn->state.stats.max_seq == 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001559
1560 /* now jump.. */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001561 mgcp_rtp_annex_count(endp, &conn->state, UINT16_MAX, 0, 2342);
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 == UINT16_MAX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001565
1566 /* and wrap */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001567 mgcp_rtp_annex_count(endp, &conn->state, 0, 0, 2342);
Harald Welte49e3d5a2017-12-25 09:47:57 +01001568 OSMO_ASSERT(conn->state.stats.initialized == 1);
1569 OSMO_ASSERT(conn->state.stats.cycles == UINT16_MAX + 1);
1570 OSMO_ASSERT(conn->state.stats.max_seq == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001571
Philipp Maierd19de2e2020-06-03 13:55:33 +02001572 mgcp_endpoints_release(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001573 talloc_free(cfg);
1574}
1575
1576static void test_no_name(void)
1577{
Philipp Maierd19de2e2020-06-03 13:55:33 +02001578 struct mgcp_trunk *trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001579 struct mgcp_config *cfg;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001580 struct msgb *inp, *msg;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001581
1582 printf("Testing no rtpmap name\n");
1583 cfg = mgcp_config_alloc();
Philipp Maier6fbbeec2020-07-01 23:00:54 +02001584 trunk = mgcp_trunk_by_num(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001585
Philipp Maier889fe7f2020-07-06 17:44:12 +02001586 trunk->v.vty_number_endpoints = 64;
Philipp Maierd19de2e2020-06-03 13:55:33 +02001587 trunk->audio_send_name = 0;
Philipp Maier889fe7f2020-07-06 17:44:12 +02001588 mgcp_trunk_equip(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001589
1590 cfg->policy_cb = mgcp_test_policy_cb;
1591
Philipp Maierffd75e42017-11-22 11:44:50 +01001592 inp = create_msg(CRCX, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001593 msg = mgcp_handle_message(cfg, inp);
Philipp Maierffd75e42017-11-22 11:44:50 +01001594
1595 if (check_response(msg->data, CRCX_RET_NO_RTPMAP) != 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001596 printf("FAILED: there should not be a RTPMAP: %s\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +02001597 (char *)msg->data);
1598 OSMO_ASSERT(false);
1599 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001600 msgb_free(inp);
1601 msgb_free(msg);
1602
Philipp Maierd19de2e2020-06-03 13:55:33 +02001603 mgcp_endpoints_release(trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001604 talloc_free(cfg);
1605}
1606
1607static void test_osmux_cid(void)
1608{
1609 int id, i;
1610
Pau Espin Pedrol8de58e72019-04-24 13:33:46 +02001611 OSMO_ASSERT(osmux_cid_pool_count_used() == 0);
1612
1613 id = osmux_cid_pool_get_next();
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001614 OSMO_ASSERT(id == 0);
Pau Espin Pedrol8de58e72019-04-24 13:33:46 +02001615 OSMO_ASSERT(osmux_cid_pool_count_used() == 1);
1616
1617 osmux_cid_pool_get(30);
1618 OSMO_ASSERT(osmux_cid_pool_count_used() == 2);
1619 osmux_cid_pool_get(30);
1620 OSMO_ASSERT(osmux_cid_pool_count_used() == 2);
1621
1622 osmux_cid_pool_put(id);
1623 OSMO_ASSERT(osmux_cid_pool_count_used() == 1);
1624 osmux_cid_pool_put(30);
1625 OSMO_ASSERT(osmux_cid_pool_count_used() == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001626
1627 for (i = 0; i < 256; ++i) {
Pau Espin Pedrol8de58e72019-04-24 13:33:46 +02001628 id = osmux_cid_pool_get_next();
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001629 OSMO_ASSERT(id == i);
Pau Espin Pedrol8de58e72019-04-24 13:33:46 +02001630 OSMO_ASSERT(osmux_cid_pool_count_used() == i + 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001631 }
1632
Pau Espin Pedrol8de58e72019-04-24 13:33:46 +02001633 id = osmux_cid_pool_get_next();
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001634 OSMO_ASSERT(id == -1);
1635
1636 for (i = 0; i < 256; ++i)
Pau Espin Pedrol8de58e72019-04-24 13:33:46 +02001637 osmux_cid_pool_put(i);
1638 OSMO_ASSERT(osmux_cid_pool_count_used() == 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001639}
1640
1641static const struct log_info_cat log_categories[] = {
1642};
1643
1644const struct log_info log_info = {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001645 .cat = log_categories,
1646 .num_cat = ARRAY_SIZE(log_categories),
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001647};
1648
Philipp Maier3d7b58d2018-06-06 09:35:31 +02001649static void test_get_lco_identifier(void)
1650{
1651 char *test;
1652 printf("Testing get_lco_identifier()\n");
1653
1654 /* Normal case at the beginning */
1655 test = "p:10, a:PCMU";
1656 printf("%s -> %s\n", test, get_lco_identifier(test));
1657
1658 test = "p:10, a:PCMU";
1659 printf("%s -> %s\n", test, get_lco_identifier(test));
1660
1661 /* Begin parsing in the middle of the value part of
1662 * the previous LCO option value */
1663 test = "XXXX, p:10, a:PCMU";
1664 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1665
1666 test = "XXXX,p:10,a:PCMU";
1667 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1668
1669 test = "10,a:PCMU";
1670 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1671
1672 test = "10, a:PCMU";
1673 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1674
1675 test = "10,a: PCMU";
1676 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1677
1678 test = "10 ,a: PCMU";
1679 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1680
1681 /* Begin parsing right at the end of the previous LCO
1682 * option value */
1683 test = ", a:PCMU";
1684 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1685
1686 test = " a:PCMU";
1687 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1688
1689 /* Empty string, result should be (null) */
1690 test = "";
1691 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1692
1693 /* Missing colons, result should be (null) */
1694 test = "p10, aPCMU";
1695 printf("%s -> %s\n", test, get_lco_identifier(test));
1696
1697 /* Colon separated from the identifier, result should be (null) */
1698 test = "10,a :PCMU";
1699 printf("'%s' -> '%s'\n", test, get_lco_identifier(test));
1700}
1701
1702static void test_check_local_cx_options(void *ctx)
1703{
1704 /* Legal cases */
1705 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
1706 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU") == 0);
1707 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p:10, IN:10") == 0);
1708 OSMO_ASSERT(check_local_cx_options(ctx, "one:AAA, two:BB, tree:C") ==
1709 0);
1710 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU") == 0);
1711 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:G726-32") == 0);
1712 OSMO_ASSERT(check_local_cx_options(ctx, "p:10-20, b:64") == 0);
1713 OSMO_ASSERT(check_local_cx_options(ctx, "b:32-64, e:off") == 0);
1714 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU;G726-32") == 0);
1715
1716 /* Illegal spaces before and after colon */
1717 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, p :10") == -1);
1718 OSMO_ASSERT(check_local_cx_options(ctx, "a :PCMU, p:10") == -1);
1719 OSMO_ASSERT(check_local_cx_options(ctx, "p: 10, a:PCMU") == -1);
1720
1721 /* Check if multiple appearances of LCOs are rejected */
1722 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, p:10") == -1);
1723 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:PCMU, a:PCMU, p:10") ==
1724 -1);
1725 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, p:10") == -1);
1726
1727 /* Check if empty LCO are rejected */
1728 OSMO_ASSERT(check_local_cx_options(ctx, "p: , a:PCMU") == -1);
1729 OSMO_ASSERT(check_local_cx_options(ctx, "p: , a: PCMU") == -1);
1730 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a: PCMU") == -1);
1731 OSMO_ASSERT(check_local_cx_options(ctx, "p:, a:PCMU") == -1);
1732 OSMO_ASSERT(check_local_cx_options(ctx, "p:10, a:") == -1);
1733
1734 /* Garbeled beginning and ends */
1735 OSMO_ASSERT(check_local_cx_options(ctx, ":10, a:10") == -1);
1736 OSMO_ASSERT(check_local_cx_options(ctx, "10, a:PCMU") == -1);
1737 OSMO_ASSERT(check_local_cx_options(ctx, ", a:PCMU") == -1);
1738 OSMO_ASSERT(check_local_cx_options(ctx, " a:PCMU") == -1);
1739 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU,") == -1);
1740 OSMO_ASSERT(check_local_cx_options(ctx, "a:PCMU, ") == -1);
1741
1742 /* Illegal strings */
1743 OSMO_ASSERT(check_local_cx_options(ctx, " ") == -1);
1744 OSMO_ASSERT(check_local_cx_options(ctx, "") == -1);
1745 OSMO_ASSERT(check_local_cx_options(ctx, "AAA") == -1);
1746 OSMO_ASSERT(check_local_cx_options(ctx, ":,") == -1);
1747 OSMO_ASSERT(check_local_cx_options(ctx, ",:") == -1);
1748 OSMO_ASSERT(check_local_cx_options(ctx, ",,,") == -1);
1749}
1750
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001751static const struct mgcp_codec_param amr_param_octet_aligned_true = {
1752 .amr_octet_aligned_present = true,
1753 .amr_octet_aligned = true,
1754};
1755
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001756static const struct mgcp_codec_param amr_param_octet_aligned_false = {
1757 .amr_octet_aligned_present = true,
1758 .amr_octet_aligned = false,
1759};
1760
1761static const struct mgcp_codec_param amr_param_octet_aligned_unset = {
1762 .amr_octet_aligned_present = false,
1763};
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001764
1765struct testcase_mgcp_codec_pt_translate_codec {
1766 int payload_type;
1767 const char *audio_name;
1768 const struct mgcp_codec_param *param;
1769 int expect_rc;
1770};
1771
1772struct testcase_mgcp_codec_pt_translate_expect {
1773 bool end;
1774 int payload_type_map[2];
1775};
1776
1777struct testcase_mgcp_codec_pt_translate {
1778 const char *descr;
1779 /* two conns on an endpoint, each with N configured codecs */
1780 struct testcase_mgcp_codec_pt_translate_codec codecs[2][10];
1781 struct testcase_mgcp_codec_pt_translate_expect expect[32];
1782};
1783
1784static const struct testcase_mgcp_codec_pt_translate test_mgcp_codec_pt_translate_cases[] = {
1785 {
1786 .descr = "same order, but differing payload type numbers",
1787 .codecs = {
1788 {
1789 { 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
1790 { 0, "PCMU/8000/1", NULL, },
1791 { 111, "GSM-HR-08/8000/1", NULL, },
1792 },
1793 {
1794 { 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
1795 { 0, "PCMU/8000/1", NULL, },
1796 { 97, "GSM-HR-08/8000/1", NULL, },
1797 },
1798 },
1799 .expect = {
1800 { .payload_type_map = {112, 96}, },
1801 { .payload_type_map = {0, 0}, },
1802 { .payload_type_map = {111, 97} },
1803 { .payload_type_map = {123, -EINVAL} },
1804 { .end = true },
1805 },
1806 },
1807 {
Neels Hofmeyr26985402019-08-08 22:39:55 +02001808 .descr = "different order and different payload type numbers",
1809 .codecs = {
1810 {
1811 { 0, "PCMU/8000/1", NULL, },
1812 { 111, "GSM-HR-08/8000/1", NULL, },
1813 { 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
1814 },
1815 {
1816 { 97, "GSM-HR-08/8000/1", NULL, },
1817 { 0, "PCMU/8000/1", NULL, },
1818 { 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
1819 },
1820 },
1821 .expect = {
1822 { .payload_type_map = {112, 96}, },
1823 { .payload_type_map = {0, 0}, },
1824 { .payload_type_map = {111, 97} },
1825 { .payload_type_map = {123, -EINVAL} },
1826 { .end = true },
1827 },
1828 },
1829 {
1830 .descr = "both sides have the same payload_type numbers assigned to differing codecs",
1831 .codecs = {
1832 {
1833 { 0, "PCMU/8000/1", NULL, },
1834 { 96, "GSM-HR-08/8000/1", NULL, },
1835 { 97, "AMR/8000/1", &amr_param_octet_aligned_true, },
1836 },
1837 {
1838 { 97, "GSM-HR-08/8000/1", NULL, },
1839 { 0, "PCMU/8000/1", NULL, },
1840 { 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
1841 },
1842 },
1843 .expect = {
1844 { .payload_type_map = {96, 97}, },
1845 { .payload_type_map = {97, 96}, },
1846 { .payload_type_map = {0, 0}, },
1847 { .end = true },
1848 },
1849 },
1850 {
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02001851 .descr = "conn0 has no codecs",
1852 .codecs = {
1853 {
1854 /* no codecs */
1855 },
1856 {
1857 { 96, "AMR/8000/1", &amr_param_octet_aligned_true, },
1858 { 0, "PCMU/8000/1", NULL, },
1859 { 97, "GSM-HR-08/8000/1", NULL, },
1860 },
1861 },
1862 .expect = {
1863 { .payload_type_map = {112, -EINVAL}, },
1864 { .payload_type_map = {0, -EINVAL}, },
1865 { .payload_type_map = {111, -EINVAL} },
1866 { .end = true },
1867 },
1868 },
1869 {
1870 .descr = "conn1 has no codecs",
1871 .codecs = {
1872 {
1873 { 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
1874 { 0, "PCMU/8000/1", NULL, },
1875 { 111, "GSM-HR-08/8000/1", NULL, },
1876 },
1877 {
1878 /* no codecs */
1879 },
1880 },
1881 .expect = {
1882 { .payload_type_map = {112, -EINVAL}, },
1883 { .payload_type_map = {0, -EINVAL}, },
1884 { .payload_type_map = {111, -EINVAL} },
1885 { .end = true },
1886 },
1887 },
Neels Hofmeyr16b637b2019-08-08 22:47:10 +02001888 {
1889 .descr = "test AMR with differing octet-aligned settings",
1890 .codecs = {
1891 {
1892 { 111, "AMR/8000", &amr_param_octet_aligned_true, },
1893 { 112, "AMR/8000", &amr_param_octet_aligned_false, },
1894 },
1895 {
1896 { 122, "AMR/8000", &amr_param_octet_aligned_false, },
1897 { 121, "AMR/8000", &amr_param_octet_aligned_true, },
1898 },
1899 },
1900 .expect = {
1901 { .payload_type_map = {111, 121}, },
1902 { .payload_type_map = {112, 122} },
1903 { .end = true },
1904 },
1905 },
1906 {
1907 .descr = "test AMR with missing octet-aligned settings (defaults to 0)",
1908 .codecs = {
1909 {
1910 { 111, "AMR/8000", &amr_param_octet_aligned_true, },
1911 { 112, "AMR/8000", &amr_param_octet_aligned_false, },
1912 },
1913 {
1914 { 122, "AMR/8000", &amr_param_octet_aligned_unset, },
1915 },
1916 },
1917 .expect = {
1918 { .payload_type_map = {111, -EINVAL}, },
1919 { .payload_type_map = {112, 122} },
1920 { .end = true },
1921 },
1922 },
1923 {
1924 .descr = "test AMR with NULL param (defaults to 0)",
1925 .codecs = {
1926 {
1927 { 111, "AMR/8000", &amr_param_octet_aligned_true, },
1928 { 112, "AMR/8000", &amr_param_octet_aligned_false, },
1929 },
1930 {
1931 { 122, "AMR/8000", NULL, },
1932 },
1933 },
1934 .expect = {
1935 { .payload_type_map = {111, -EINVAL}, },
1936 { .payload_type_map = {112, 122} },
1937 { .end = true },
1938 },
1939 },
Neels Hofmeyr683e05f2019-08-08 22:48:18 +02001940 {
1941 .descr = "match FOO/8000/1 and FOO/8000 as identical, single channel is implicit",
1942 .codecs = {
1943 {
1944 { 0, "PCMU/8000/1", NULL, },
1945 { 111, "GSM-HR-08/8000/1", NULL, },
1946 { 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
1947 },
1948 {
1949 { 97, "GSM-HR-08/8000", NULL, },
1950 { 0, "PCMU/8000", NULL, },
1951 { 96, "AMR/8000", &amr_param_octet_aligned_true, },
1952 },
1953 },
1954 .expect = {
1955 { .payload_type_map = {112, 96}, },
1956 { .payload_type_map = {0, 0}, },
1957 { .payload_type_map = {111, 97} },
1958 { .payload_type_map = {123, -EINVAL} },
1959 { .end = true },
1960 },
1961 },
1962 {
1963 .descr = "match FOO/8000/1 and FOO as identical, 8k and single channel are implicit",
1964 .codecs = {
1965 {
1966 { 0, "PCMU/8000/1", NULL, },
1967 { 111, "GSM-HR-08/8000/1", NULL, },
1968 { 112, "AMR/8000/1", &amr_param_octet_aligned_true, },
1969 },
1970 {
1971 { 97, "GSM-HR-08", NULL, },
1972 { 0, "PCMU", NULL, },
1973 { 96, "AMR", &amr_param_octet_aligned_true, },
1974 },
1975 },
1976 .expect = {
1977 { .payload_type_map = {112, 96}, },
1978 { .payload_type_map = {0, 0}, },
1979 { .payload_type_map = {111, 97} },
1980 { .payload_type_map = {123, -EINVAL} },
1981 { .end = true },
1982 },
1983 },
1984 {
1985 .descr = "test whether channel number matching is waterproof",
1986 .codecs = {
1987 {
1988 { 111, "GSM-HR-08/8000", },
1989 { 112, "GSM-HR-08/8000/2", .expect_rc = -22},
1990 { 113, "GSM-HR-08/8000/3", .expect_rc = -22},
1991 },
1992 {
1993 { 122, "GSM-HR-08/8000/2", .expect_rc = -22},
1994 { 121, "GSM-HR-08/8000/1", },
1995 },
1996 },
1997 .expect = {
1998 { .payload_type_map = {111, 121}, },
1999 { .payload_type_map = {112, -EINVAL} },
2000 { .payload_type_map = {113, -EINVAL} },
2001 { .end = true },
2002 },
2003 },
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002004};
Philipp Maier6931f9a2018-07-26 09:29:31 +02002005
2006static void test_mgcp_codec_pt_translate(void)
2007{
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002008 int i;
2009 bool ok = true;
2010 printf("\nTesting mgcp_codec_pt_translate()\n");
Philipp Maier6931f9a2018-07-26 09:29:31 +02002011
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002012 for (i = 0; i < ARRAY_SIZE(test_mgcp_codec_pt_translate_cases); i++) {
2013 const struct testcase_mgcp_codec_pt_translate *t = &test_mgcp_codec_pt_translate_cases[i];
2014 struct mgcp_conn_rtp conn[2] = {};
2015 int rc;
2016 int conn_i;
2017 int c;
Philipp Maier6931f9a2018-07-26 09:29:31 +02002018
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002019 printf("#%d: %s\n", i, t->descr);
Philipp Maier6931f9a2018-07-26 09:29:31 +02002020
Neels Hofmeyrd2f5e692019-08-08 21:59:26 +02002021 for (conn_i = 0; conn_i < 2; conn_i++) {
2022 printf(" - add codecs on conn%d:\n", conn_i);
2023 for (c = 0; c < ARRAY_SIZE(t->codecs[conn_i]); c++) {
2024 const struct testcase_mgcp_codec_pt_translate_codec *codec = &t->codecs[conn_i][c];
2025 if (!codec->audio_name)
2026 break;
2027
2028 rc = mgcp_codec_add(&conn[conn_i], codec->payload_type, codec->audio_name, codec->param);
2029
2030 printf(" %2d: %3d %s%s -> rc=%d\n", c, codec->payload_type, codec->audio_name,
2031 codec->param ?
2032 (codec->param->amr_octet_aligned_present?
2033 (codec->param->amr_octet_aligned ?
2034 " octet-aligned=1" : " octet-aligned=0")
2035 : " octet-aligned=unset")
2036 : "",
2037 rc);
2038 if (rc != codec->expect_rc) {
2039 printf(" ERROR: expected rc=%d\n", codec->expect_rc);
2040 ok = false;
2041 }
2042 }
2043 if (!c)
2044 printf(" (none)\n");
2045 }
2046
2047 for (c = 0; c < ARRAY_SIZE(t->expect); c++) {
2048 const struct testcase_mgcp_codec_pt_translate_expect *expect = &t->expect[c];
2049 int result;
2050
2051 if (expect->end)
2052 break;
2053
2054 result = mgcp_codec_pt_translate(&conn[0], &conn[1], expect->payload_type_map[0]);
2055 printf(" - mgcp_codec_pt_translate(conn0, conn1, %d) -> %d\n",
2056 expect->payload_type_map[0], result);
2057 if (result != expect->payload_type_map[1]) {
2058 printf(" ERROR: expected -> %d\n", expect->payload_type_map[1]);
2059 ok = false;
2060 }
2061
2062 /* If the expected result is an error, don't do reverse map test */
2063 if (expect->payload_type_map[1] < 0)
2064 continue;
2065
2066 result = mgcp_codec_pt_translate(&conn[1], &conn[0], expect->payload_type_map[1]);
2067 printf(" - mgcp_codec_pt_translate(conn1, conn0, %d) -> %d\n",
2068 expect->payload_type_map[1], result);
2069 if (result != expect->payload_type_map[0]) {
2070 printf(" ERROR: expected -> %d\n", expect->payload_type_map[0]);
2071 ok = false;
2072 }
2073 }
2074
2075 for (conn_i = 0; conn_i < 2; conn_i++)
2076 mgcp_codec_reset_all(&conn[conn_i]);
2077 }
2078
2079 OSMO_ASSERT(ok);
Philipp Maier6931f9a2018-07-26 09:29:31 +02002080}
2081
Neels Hofmeyr65317262018-09-03 22:11:05 +02002082void test_conn_id_matching()
2083{
2084 struct mgcp_endpoint endp = {};
2085 struct mgcp_conn *conn;
2086 struct mgcp_conn *conn_match;
2087 int i;
2088 const char *conn_id_generated = "000023AB";
2089 const char *conn_id_request[] = {
Neels Hofmeyra77eade2018-08-29 02:30:39 +02002090 "23AB",
2091 "0023AB",
Neels Hofmeyr65317262018-09-03 22:11:05 +02002092 "000023AB",
Neels Hofmeyra77eade2018-08-29 02:30:39 +02002093 "00000023AB",
2094 "23ab",
2095 "0023ab",
Neels Hofmeyr65317262018-09-03 22:11:05 +02002096 "000023ab",
Neels Hofmeyra77eade2018-08-29 02:30:39 +02002097 "00000023ab",
Neels Hofmeyr65317262018-09-03 22:11:05 +02002098 };
2099
2100 printf("\nTesting %s\n", __func__);
2101
2102 INIT_LLIST_HEAD(&endp.conns);
2103
2104 conn = talloc_zero(NULL, struct mgcp_conn);
2105 OSMO_ASSERT(conn);
2106 osmo_strlcpy(conn->id, conn_id_generated, sizeof(conn->id));
2107 llist_add(&conn->entry, &endp.conns);
2108
2109 for (i = 0; i < ARRAY_SIZE(conn_id_request); i++) {
2110 const char *needle = conn_id_request[i];
2111 printf("needle='%s' ", needle);
2112 conn_match = mgcp_conn_get(&endp, needle);
2113 OSMO_ASSERT(conn_match);
2114 printf("found '%s'\n", conn_match->id);
2115 OSMO_ASSERT(conn_match == conn);
2116 }
2117
2118 llist_del(&conn->entry);
2119 talloc_free(conn);
2120}
2121
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002122void test_e1_trunk_nr_from_epname()
2123{
2124 int trunk_nr;
2125
2126 /* Note: e1_trunk_nr_from_epname does not check the text
2127 * after the E1 trunk number, after the delimiter
2128 * character "/" arbitrary text may follow. */
Philipp Maier0653cc82020-08-10 22:52:51 +02002129 trunk_nr = e1_trunk_nr_from_epname("ds/e1-0/s-1/su16-0");
2130 OSMO_ASSERT(trunk_nr == 0);
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002131 trunk_nr = e1_trunk_nr_from_epname("ds/e1-1/s-1/su16-0");
2132 OSMO_ASSERT(trunk_nr == 1);
2133 trunk_nr = e1_trunk_nr_from_epname("ds/e1-2/s-2/su16-0");
2134 OSMO_ASSERT(trunk_nr == 2);
2135 trunk_nr = e1_trunk_nr_from_epname("ds/e1-3/s-23/su32-0");
2136 OSMO_ASSERT(trunk_nr == 3);
2137 trunk_nr = e1_trunk_nr_from_epname("ds/e1-3/xxxxxxx");
2138 OSMO_ASSERT(trunk_nr == 3);
2139 trunk_nr = e1_trunk_nr_from_epname("ds/e1-24/s-1/su16-0");
2140 OSMO_ASSERT(trunk_nr == 24);
2141 trunk_nr = e1_trunk_nr_from_epname("ds/e1-64/s-1/su16-0");
2142 OSMO_ASSERT(trunk_nr == 64);
2143
2144 /* The following endpoint strings should fail, either the
2145 * trunk number exceeds the valid range or the trunk prefix
2146 * is wrong. Also when the delimiter character "/" at the
2147 * end of the trunk is wrong the parsing should fail. */
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002148 trunk_nr = e1_trunk_nr_from_epname("ds/e1-65/s-1/su16-0");
2149 OSMO_ASSERT(trunk_nr == -EINVAL);
2150 trunk_nr = e1_trunk_nr_from_epname("ds/e1--1/s-1/su16-0");
2151 OSMO_ASSERT(trunk_nr == -EINVAL);
2152 trunk_nr = e1_trunk_nr_from_epname("xxxxxx4zyz");
2153 OSMO_ASSERT(trunk_nr == -EINVAL);
2154 trunk_nr = e1_trunk_nr_from_epname("ds/e1+2/s-1/su16-0");
2155 OSMO_ASSERT(trunk_nr == -EINVAL);
2156 trunk_nr = e1_trunk_nr_from_epname("ds/e2-24/s-1/su16-0");
2157 OSMO_ASSERT(trunk_nr == -EINVAL);
2158 trunk_nr = e1_trunk_nr_from_epname("ds/e1-24s-1/su16-0");
2159 OSMO_ASSERT(trunk_nr == -EINVAL);
2160
2161 return;
2162}
2163
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02002164int main(int argc, char **argv)
2165{
Neels Hofmeyr60f8e312018-03-30 23:01:07 +02002166 void *ctx = talloc_named_const(NULL, 0, "mgcp_test");
2167 void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
2168 osmo_init_logging2(ctx, &log_info);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02002169
2170 test_strline();
2171 test_values();
2172 test_messages();
2173 test_retransmission();
2174 test_packet_loss_calc();
2175 test_rqnt_cb();
2176 test_mgcp_stats();
2177 test_packet_error_detection(1, 0);
2178 test_packet_error_detection(0, 0);
2179 test_packet_error_detection(0, 1);
2180 test_packet_error_detection(1, 1);
2181 test_multilple_codec();
2182 test_no_cycle();
2183 test_no_name();
2184 test_osmux_cid();
Philipp Maier3d7b58d2018-06-06 09:35:31 +02002185 test_get_lco_identifier();
2186 test_check_local_cx_options(ctx);
Philipp Maier6931f9a2018-07-26 09:29:31 +02002187 test_mgcp_codec_pt_translate();
Neels Hofmeyr65317262018-09-03 22:11:05 +02002188 test_conn_id_matching();
Philipp Maier7e9ddc92020-06-10 15:22:32 +02002189 test_e1_trunk_nr_from_epname();
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02002190
Neels Hofmeyr465446b2017-11-18 21:26:26 +01002191 OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
2192 OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
2193 talloc_free(msgb_ctx);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02002194 printf("Done\n");
2195 return EXIT_SUCCESS;
2196}