blob: ad2da7f51ab0bb7d8a2b4c7a2801c5c2f647e8f1 [file] [log] [blame]
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001/*
2 * Some SDP file parsing...
3 *
4 * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * (C) 2009-2014 by On-Waves
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
Philipp Maier8970c492017-10-11 13:33:42 +020023#include <osmocom/core/msgb.h>
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +020024#include <osmocom/core/socket.h>
25#include <osmocom/core/sockaddr_str.h>
26
Philipp Maier87bd9be2017-08-22 16:35:41 +020027#include <osmocom/mgcp/mgcp.h>
Philipp Maier993ea6b2020-08-04 18:26:50 +020028#include <osmocom/mgcp/osmux.h>
29#include <osmocom/mgcp/mgcp_conn.h>
30#include <osmocom/mgcp/mgcp_protocol.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020031#include <osmocom/mgcp/mgcp_msg.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010032#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020033#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maierbc0346e2018-06-07 09:52:16 +020034#include <osmocom/mgcp/mgcp_codec.h>
Philipp Maier7e37ce62019-03-05 14:00:11 +010035#include <osmocom/mgcp/mgcp_sdp.h>
Philipp Maier993ea6b2020-08-04 18:26:50 +020036#include <osmocom/mgcp/mgcp_protocol.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020037
38#include <errno.h>
Pau Espin Pedrolc5c14302019-07-23 16:19:41 +020039#include <stdlib.h>
40#include <limits.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020041
Philipp Maier228e5912019-03-05 13:56:59 +010042/* Two structs to store intermediate parsing results. The function
43 * mgcp_parse_sdp_data() is using the following two structs as temporary
44 * storage for parsing the SDP codec information. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020045struct sdp_rtp_map {
46 /* the type */
47 int payload_type;
48 /* null, static or later dynamic codec name */
49 char *codec_name;
50 /* A pointer to the original line for later parsing */
51 char *map_line;
52
53 int rate;
54 int channels;
55};
Philipp Maier228e5912019-03-05 13:56:59 +010056struct sdp_fmtp_param {
57 int payload_type;
58 struct mgcp_codec_param param;
59};
60
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020061
Philipp Maierbc0346e2018-06-07 09:52:16 +020062/* Helper function to extrapolate missing codec parameters in a codec mao from
63 * an already filled in payload_type, called from: mgcp_parse_sdp_data() */
Philipp Maier8970c492017-10-11 13:33:42 +020064static void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020065{
66 int i;
67
68 for (i = 0; i < used; ++i) {
69 switch (codecs[i].payload_type) {
70 case 0:
71 codecs[i].codec_name = "PCMU";
72 codecs[i].rate = 8000;
73 codecs[i].channels = 1;
74 break;
75 case 3:
76 codecs[i].codec_name = "GSM";
77 codecs[i].rate = 8000;
78 codecs[i].channels = 1;
79 break;
80 case 8:
81 codecs[i].codec_name = "PCMA";
82 codecs[i].rate = 8000;
83 codecs[i].channels = 1;
84 break;
85 case 18:
86 codecs[i].codec_name = "G729";
87 codecs[i].rate = 8000;
88 codecs[i].channels = 1;
89 break;
Philipp Maierbc0346e2018-06-07 09:52:16 +020090 default:
91 codecs[i].codec_name = NULL;
92 codecs[i].rate = 0;
93 codecs[i].channels = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020094 }
95 }
96}
97
Philipp Maierbc0346e2018-06-07 09:52:16 +020098/* Helper function to update codec map information with additional data from
99 * SDP, called from: mgcp_parse_sdp_data() */
Philipp Maier8970c492017-10-11 13:33:42 +0200100static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
Pau Espin Pedrol8029b122021-12-23 16:22:30 +0100101 int payload_type, const char *audio_name)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200102{
103 int i;
104
105 for (i = 0; i < used; ++i) {
106 char audio_codec[64];
107 int rate = -1;
108 int channels = -1;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200109
110 /* Note: We can only update payload codecs that already exist
111 * in our codec list. If we get an unexpected payload type,
112 * we just drop it */
Pau Espin Pedrol8029b122021-12-23 16:22:30 +0100113 if (codecs[i].payload_type != payload_type)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200114 continue;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200115
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200116 if (sscanf(audio_name, "%63[^/]/%d/%d",
Philipp Maierd8d7b4b2017-10-18 11:45:35 +0200117 audio_codec, &rate, &channels) < 1) {
118 LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n",
119 audio_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200120 continue;
121 }
122
123 codecs[i].map_line = talloc_strdup(ctx, audio_name);
124 codecs[i].codec_name = talloc_strdup(ctx, audio_codec);
125 codecs[i].rate = rate;
126 codecs[i].channels = channels;
127 return;
128 }
129
Pau Espin Pedrol8029b122021-12-23 16:22:30 +0100130 LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload_type,
Philipp Maierd8d7b4b2017-10-18 11:45:35 +0200131 audio_name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200132}
133
Philipp Maierbc0346e2018-06-07 09:52:16 +0200134/* Extract payload types from SDP, also check for duplicates */
135static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
136 unsigned int codecs_len, char *sdp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200137{
Philipp Maierbc0346e2018-06-07 09:52:16 +0200138 char *str;
139 char *str_ptr;
140 char *pt_str;
Pau Espin Pedrolc5c14302019-07-23 16:19:41 +0200141 char *pt_end;
Pau Espin Pedrola2b1c5e2019-07-26 14:13:14 +0200142 unsigned long int pt;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200143 unsigned int count = 0;
144 unsigned int i;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200145
Philipp Maierbc0346e2018-06-07 09:52:16 +0200146 str = talloc_zero_size(ctx, strlen(sdp) + 1);
147 str_ptr = str;
148 strcpy(str_ptr, sdp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200149
Philipp Maierbc0346e2018-06-07 09:52:16 +0200150 str_ptr = strstr(str_ptr, "RTP/AVP ");
151 if (!str_ptr)
152 goto exit;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200153
Philipp Maierbc0346e2018-06-07 09:52:16 +0200154 pt_str = strtok(str_ptr, " ");
155 if (!pt_str)
156 goto exit;
157
158 while (1) {
159 /* Do not allow excessive payload types */
160 if (count > codecs_len)
161 goto error;
162
163 pt_str = strtok(NULL, " ");
164 if (!pt_str)
165 break;
166
Pau Espin Pedrolc5c14302019-07-23 16:19:41 +0200167 errno = 0;
168 pt = strtoul(pt_str, &pt_end, 0);
169 if ((errno == ERANGE && pt == ULONG_MAX) || (errno && !pt) ||
170 pt_str == pt_end)
171 goto error;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200172
Pau Espin Pedrola2b1c5e2019-07-26 14:13:14 +0200173 if (pt >> 7) /* PT is 7 bit field, higher values not allowed */
174 goto error;
175
Philipp Maierbc0346e2018-06-07 09:52:16 +0200176 /* Do not allow duplicate payload types */
177 for (i = 0; i < count; i++)
178 if (codecs[i].payload_type == pt)
179 goto error;
180
181 codecs[count].payload_type = pt;
182 count++;
183 }
184
185exit:
186 talloc_free(str);
187 return count;
188error:
189 talloc_free(str);
190 return -EINVAL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200191}
192
Philipp Maier228e5912019-03-05 13:56:59 +0100193/* Extract fmtp parameters from SDP, called from: mgcp_parse_sdp_data() */
194static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp)
195{
196 char *str;
197 char *str_ptr;
198 char *param_str;
199 unsigned int pt;
200 unsigned int count = 0;
201 char delimiter;
202 unsigned int amr_octet_aligned;
203
204 memset(fmtp_param, 0, sizeof(*fmtp_param));
205
206 str = talloc_zero_size(ctx, strlen(sdp) + 1);
207 str_ptr = str;
208 strcpy(str_ptr, sdp);
209
210 /* Check if the input string begins with an fmtp token */
211 str_ptr = strstr(str_ptr, "fmtp:");
212 if (!str_ptr)
213 goto exit;
214 str_ptr += 5;
215
216 /* Extract payload type */
217 if (sscanf(str_ptr, "%u ", &pt) != 1)
218 goto error;
219 fmtp_param->payload_type = pt;
220
221 /* Advance pointer to the beginning of the parameter section and
222 * tokenize string */
223 str_ptr = strstr(str_ptr, " ");
224 if (!str_ptr)
225 goto error;
226 str_ptr++;
227
228 param_str = strtok(str_ptr, " ");
229 if (!param_str)
230 goto exit;
231
232 while (1) {
233 /* Make sure that we don't get trapped in an endless loop */
234 if (count > 256)
235 goto error;
236
237 /* Chop off delimiters ';' at the end */
238 delimiter = str_ptr[strlen(str_ptr) - 1];
239 if (delimiter == ';' || delimiter == ',')
240 str_ptr[strlen(str_ptr) - 1] = '\0';
241
242 /* AMR octet aligned parameter */
243 if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
244 fmtp_param->param.amr_octet_aligned_present = true;
245 fmtp_param->param.amr_octet_aligned = false;
246 if (amr_octet_aligned == 1)
247 fmtp_param->param.amr_octet_aligned = true;
248
249 }
250
251 param_str = strtok(NULL, " ");
252 if (!param_str)
253 break;
254 count++;
255 }
256
257exit:
258 talloc_free(str);
259 return 0;
260error:
261 talloc_free(str);
262 return -EINVAL;
263}
264
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200265
266static int audio_ip_from_sdp(struct osmo_sockaddr *dst_addr, char *sdp)
267{
268 bool is_ipv6;
269 char ipbuf[INET6_ADDRSTRLEN];
270 if (strncmp("c=IN IP", sdp, 7) != 0)
271 return -1;
272 sdp += 7;
273 if (*sdp == '6')
274 is_ipv6 = true;
275 else if (*sdp == '4')
276 is_ipv6 = false;
277 else
278 return -1;
279 sdp++;
280 if (*sdp != ' ')
281 return -1;
282 sdp++;
283 if (is_ipv6) {
284 /* 45 = INET6_ADDRSTRLEN -1 */
285 if (sscanf(sdp, "%45s", ipbuf) != 1)
286 return -1;
287 if (inet_pton(AF_INET6, ipbuf, &dst_addr->u.sin6.sin6_addr) != 1)
288 return -1;
289 dst_addr->u.sa.sa_family = AF_INET6;
290 } else {
291 /* 15 = INET_ADDRSTRLEN -1 */
292 if (sscanf(sdp, "%15s", ipbuf) != 1)
293 return -1;
294 if (inet_pton(AF_INET, ipbuf, &dst_addr->u.sin.sin_addr) != 1)
295 return -1;
296 dst_addr->u.sa.sa_family = AF_INET;
297 }
298 return 0;
299}
300
Philipp Maier228e5912019-03-05 13:56:59 +0100301/* Pick optional fmtp parameters by payload type, if there are no fmtp
302 * parameters, a nullpointer is returned */
303static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
304{
305 unsigned int i;
306
307 for (i = 0; i < fmtp_params_len; i++) {
308 if (fmtp_params[i].payload_type == pt)
309 return &fmtp_params[i].param;
310 }
311
312 return NULL;
313}
314
Philipp Maier8970c492017-10-11 13:33:42 +0200315/*! Analyze SDP input string.
316 * \param[in] endp trunk endpoint.
317 * \param[out] conn associated rtp connection.
318 * \param[out] caller provided memory to store the parsing results.
Philipp Maier8970c492017-10-11 13:33:42 +0200319 *
320 * Note: In conn (conn->end) the function returns the packet duration,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200321 * rtp port, rtcp port and the codec information.
322 * \returns 0 on success, -1 on failure. */
Philipp Maier8970c492017-10-11 13:33:42 +0200323int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200324 struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200325{
Philipp Maierbc0346e2018-06-07 09:52:16 +0200326 struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
327 unsigned int codecs_used = 0;
Philipp Maier228e5912019-03-05 13:56:59 +0100328 struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
329 unsigned int fmtp_used = 0;
330 struct mgcp_codec_param *codec_param;
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200331 char ipbuf[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200332 char *line;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200333 unsigned int i;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200334 void *tmp_ctx = talloc_new(NULL);
Philipp Maier8970c492017-10-11 13:33:42 +0200335 struct mgcp_rtp_end *rtp;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200336
Pau Espin Pedrol8029b122021-12-23 16:22:30 +0100337 int payload_type;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200338 int ptime, ptime2 = 0;
339 char audio_name[64];
340 int port, rc;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200341
Philipp Maier8970c492017-10-11 13:33:42 +0200342 OSMO_ASSERT(endp);
343 OSMO_ASSERT(conn);
344 OSMO_ASSERT(p);
345
346 rtp = &conn->end;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200347 memset(&codecs, 0, sizeof(codecs));
348
349 for_each_line(line, p->save) {
350 switch (line[0]) {
351 case 'o':
352 case 's':
353 case 't':
354 case 'v':
355 /* skip these SDP attributes */
356 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200357 case 'a':
Pau Espin Pedrol8029b122021-12-23 16:22:30 +0100358 if (sscanf(line, "a=rtpmap:%d %63s", &payload_type, audio_name) == 2) {
359 codecs_update(tmp_ctx, codecs, codecs_used, payload_type, audio_name);
Philipp Maiere7ae69a2019-03-05 15:17:36 +0100360 break;
361 }
362
363 if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200364 if (ptime2 > 0 && ptime2 != ptime)
365 rtp->packet_duration_ms = 0;
366 else
367 rtp->packet_duration_ms = ptime;
Philipp Maiere7ae69a2019-03-05 15:17:36 +0100368 break;
369 }
370
371 if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
Philipp Maierbc0346e2018-06-07 09:52:16 +0200372 rtp->maximum_packet_time = ptime2;
Philipp Maiere7ae69a2019-03-05 15:17:36 +0100373 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200374 }
Philipp Maier228e5912019-03-05 13:56:59 +0100375
376 if (strncmp("a=fmtp:", line, 6) == 0) {
377 rc = fmtp_from_sdp(conn->conn, &fmtp_params[fmtp_used], line);
378 if (rc >= 0)
379 fmtp_used++;
380 break;
381 }
382
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200383 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200384 case 'm':
Philipp Maierbc0346e2018-06-07 09:52:16 +0200385 rc = sscanf(line, "m=audio %d RTP/AVP", &port);
386 if (rc == 1) {
Pau Espin Pedrol5ffd1272022-10-04 13:45:48 +0200387 osmo_sockaddr_set_port(&rtp->addr.u.sa, port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200388 rtp->rtcp_port = htons(port + 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200389 }
Philipp Maierbc0346e2018-06-07 09:52:16 +0200390
391 rc = pt_from_sdp(conn->conn, codecs,
392 ARRAY_SIZE(codecs), line);
393 if (rc > 0)
394 codecs_used = rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200395 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200396 case 'c':
Erice303fa92021-08-06 01:40:14 +0200397 if (audio_ip_from_sdp(&rtp->addr, line) < 0) {
398 talloc_free(tmp_ctx);
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200399 return -1;
Erice303fa92021-08-06 01:40:14 +0200400 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200401 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200402 default:
Philipp Maier8dc35972021-07-14 11:20:16 +0200403 if (endp)
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200404 /* TODO: Check spec: We used the bare endpoint number before,
405 * now we use the endpoint name as a whole? Is this allowed? */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200406 LOGP(DLMGCP, LOGL_NOTICE,
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200407 "Unhandled SDP option: '%c'/%d on %s\n",
408 line[0], line[0], endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200409 else
410 LOGP(DLMGCP, LOGL_NOTICE,
411 "Unhandled SDP option: '%c'/%d\n",
412 line[0], line[0]);
413 break;
414 }
415 }
Philipp Maierbc0346e2018-06-07 09:52:16 +0200416 OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200417
Philipp Maierbc0346e2018-06-07 09:52:16 +0200418 /* So far we have only set the payload type in the codec struct. Now we
419 * fill up the remaining fields of the codec description with some default
420 * information */
421 codecs_initialize(tmp_ctx, codecs, codecs_used);
422
423 /* Store parsed codec information */
Philipp Maierdbd70c72018-05-25 11:07:31 +0200424 for (i = 0; i < codecs_used; i++) {
Philipp Maier228e5912019-03-05 13:56:59 +0100425 codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
426 rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200427 if (rc < 0)
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200428 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200429 }
430
431 talloc_free(tmp_ctx);
Philipp Maierdbd70c72018-05-25 11:07:31 +0200432
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200433 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200434 "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
Pau Espin Pedrol5ffd1272022-10-04 13:45:48 +0200435 osmo_sockaddr_port(&rtp->addr.u.sa), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
Philipp Maierbc0346e2018-06-07 09:52:16 +0200436 rtp->packet_duration_ms);
437 if (codecs_used == 0)
438 LOGPC(DLMGCP, LOGL_NOTICE, "none");
439 for (i = 0; i < codecs_used; i++) {
440 LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
441 rtp->codecs[i].payload_type,
Eric2ebcf5c2021-08-09 23:26:48 +0200442 strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown");
Philipp Maierbc0346e2018-06-07 09:52:16 +0200443 LOGPC(DLMGCP, LOGL_NOTICE, " ");
444 }
445 LOGPC(DLMGCP, LOGL_NOTICE, "\n");
446
Philipp Maierdbd70c72018-05-25 11:07:31 +0200447 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200448}
449
Philipp Maier8482e832018-08-02 17:22:40 +0200450
451/* Add rtpmap string to the sdp payload, but only when the payload type falls
452 * into the dynamic payload type range */
453static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name)
454{
455 int rc;
456
457 if (payload_type >= 96 && payload_type <= 127) {
458 if (!audio_name)
459 return -EINVAL;
460 rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name);
461 if (rc < 0)
462 return -EINVAL;
463 }
464
465 return 0;
466}
467
Philipp Maier217d31d2019-03-05 14:48:56 +0100468/* Add audio strings to sdp payload */
Philipp Maier910189d2018-08-02 17:45:42 +0200469static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
470{
471 int rc;
472 unsigned int i;
473
Philipp Maier910189d2018-08-02 17:45:42 +0200474 rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
475 if (rc < 0)
476 return -EINVAL;
477
478 for (i = 0; i < payload_types_len; i++) {
479 rc = msgb_printf(sdp, " %d", payload_types[i]);
480 if (rc < 0)
481 return -EINVAL;
482 }
483
484 rc = msgb_printf(sdp, "\r\n");
485 if (rc < 0)
486 return -EINVAL;
487
488 return 0;
489}
490
Philipp Maier228e5912019-03-05 13:56:59 +0100491/* Add fmtp strings to sdp payload */
492static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len,
493 const char *fmtp_extra)
494{
495 unsigned int i;
496 int rc;
497 int fmtp_extra_pt = -1;
498 char *fmtp_extra_pars = "";
499
500 /* When no fmtp parameters ara available but an fmtp extra string
501 * is configured, just add the fmtp extra string */
502 if (fmtp_params_len == 0 && fmtp_extra) {
503 return msgb_printf(sdp, "%s\r\n", fmtp_extra);
504 }
505
506 /* When there is fmtp extra configured we dissect it in order to drop
507 * in the configured extra parameters at the right place when
508 * generating the fmtp strings. */
509 if (fmtp_extra) {
510 if (sscanf(fmtp_extra, "a=fmtp:%d ", &fmtp_extra_pt) != 1)
511 fmtp_extra_pt = -1;
512
513 fmtp_extra_pars = strstr(fmtp_extra, " ");
514
515 if (!fmtp_extra_pars)
516 fmtp_extra_pars = "";
517 else
518 fmtp_extra_pars++;
519 }
520
521 for (i = 0; i < fmtp_params_len; i++) {
522 rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
Vadim Yanitskiy13fae782020-07-07 14:12:26 +0700523 if (rc < 0)
524 return -EINVAL;
Philipp Maier228e5912019-03-05 13:56:59 +0100525
526 /* Add amr octet align parameter */
527 if (fmtp_params[i].param.amr_octet_aligned_present) {
528 if (fmtp_params[i].param.amr_octet_aligned)
529 rc = msgb_printf(sdp, " octet-align=1");
530 else
531 rc = msgb_printf(sdp, " octet-align=0");
532 if (rc < 0)
533 return -EINVAL;
534 }
535
536 /* Append extra parameters from fmtp extra */
537 if (fmtp_params[i].payload_type == fmtp_extra_pt) {
538 rc = msgb_printf(sdp, " %s", fmtp_extra_pars);
539 if (rc < 0)
540 return -EINVAL;
541 }
542
Vadim Yanitskiyb7d395d2020-07-07 14:09:45 +0700543 rc = msgb_printf(sdp, "\r\n");
Philipp Maier228e5912019-03-05 13:56:59 +0100544 if (rc < 0)
545 return -EINVAL;
546 }
547
548 return 0;
549}
550
Philipp Maier8970c492017-10-11 13:33:42 +0200551/*! Generate SDP response string.
552 * \param[in] endp trunk endpoint.
553 * \param[in] conn associated rtp connection.
554 * \param[out] sdp msg buffer to append resulting SDP string data.
555 * \param[in] addr IPV4 address string (e.g. 192.168.100.1).
556 * \returns 0 on success, -1 on failure. */
557int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
558 const struct mgcp_conn_rtp *conn, struct msgb *sdp,
559 const char *addr)
560{
Philipp Maier58128252019-03-06 11:28:18 +0100561 const struct mgcp_rtp_codec *codec;
Philipp Maier8970c492017-10-11 13:33:42 +0200562 const char *fmtp_extra;
563 const char *audio_name;
564 int payload_type;
Philipp Maier228e5912019-03-05 13:56:59 +0100565 struct sdp_fmtp_param fmtp_param;
Philipp Maier8970c492017-10-11 13:33:42 +0200566 int rc;
Philipp Maier910189d2018-08-02 17:45:42 +0200567 int payload_types[1];
Pau Espin Pedrola93c6e92019-05-06 15:23:57 +0200568 int local_port;
Philipp Maier228e5912019-03-05 13:56:59 +0100569 struct sdp_fmtp_param fmtp_params[1];
570 unsigned int fmtp_params_len = 0;
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200571 bool addr_is_v6;
Philipp Maier8970c492017-10-11 13:33:42 +0200572
573 OSMO_ASSERT(endp);
574 OSMO_ASSERT(conn);
575 OSMO_ASSERT(sdp);
576 OSMO_ASSERT(addr);
577
578 /* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
Ericfbf78d12021-08-23 22:31:39 +0200579 endp->trunk->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
Philipp Maier58128252019-03-06 11:28:18 +0100580 &codec, &fmtp_extra,
Philipp Maier8970c492017-10-11 13:33:42 +0200581 (struct mgcp_conn_rtp *)conn);
582
Philipp Maier58128252019-03-06 11:28:18 +0100583 audio_name = codec->audio_name;
584 payload_type = codec->payload_type;
585
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200586 addr_is_v6 = osmo_ip_str_type(addr) == AF_INET6;
587
Philipp Maier8970c492017-10-11 13:33:42 +0200588 rc = msgb_printf(sdp,
589 "v=0\r\n"
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200590 "o=- %s 23 IN IP%c %s\r\n"
Philipp Maier8970c492017-10-11 13:33:42 +0200591 "s=-\r\n"
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200592 "c=IN IP%c %s\r\n"
593 "t=0 0\r\n", conn->conn->id,
594 addr_is_v6 ? '6' : '4', addr,
595 addr_is_v6 ? '6' : '4', addr);
Philipp Maier8970c492017-10-11 13:33:42 +0200596
597 if (rc < 0)
598 goto buffer_too_small;
599
600 if (payload_type >= 0) {
Philipp Maier910189d2018-08-02 17:45:42 +0200601
602 payload_types[0] = payload_type;
Pau Espin Pedrola93c6e92019-05-06 15:23:57 +0200603 if (mgcp_conn_rtp_is_osmux(conn))
Pau Espin Pedrol36413c02022-10-12 17:58:17 +0200604 local_port = endp->trunk->cfg->osmux.local_port;
Pau Espin Pedrola93c6e92019-05-06 15:23:57 +0200605 else
606 local_port = conn->end.local_port;
607 rc = add_audio(sdp, payload_types, 1, local_port);
Philipp Maier8970c492017-10-11 13:33:42 +0200608 if (rc < 0)
609 goto buffer_too_small;
610
Philipp Maier14b27a82020-06-02 20:15:30 +0200611 if (endp->trunk->audio_send_name) {
Philipp Maier8482e832018-08-02 17:22:40 +0200612 rc = add_rtpmap(sdp, payload_type, audio_name);
Philipp Maier8970c492017-10-11 13:33:42 +0200613 if (rc < 0)
614 goto buffer_too_small;
615 }
616
Philipp Maier228e5912019-03-05 13:56:59 +0100617 if (codec->param_present) {
618 fmtp_param.payload_type = payload_type;
619 fmtp_param.param = codec->param;
620 fmtp_params[0] = fmtp_param;
621 fmtp_params_len = 1;
Philipp Maier8970c492017-10-11 13:33:42 +0200622 }
Philipp Maier228e5912019-03-05 13:56:59 +0100623 rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
624 if (rc < 0)
625 goto buffer_too_small;
Philipp Maier8970c492017-10-11 13:33:42 +0200626 }
Philipp Maier14b27a82020-06-02 20:15:30 +0200627 if (conn->end.packet_duration_ms > 0 && endp->trunk->audio_send_ptime) {
Philipp Maier8970c492017-10-11 13:33:42 +0200628 rc = msgb_printf(sdp, "a=ptime:%u\r\n",
629 conn->end.packet_duration_ms);
630 if (rc < 0)
631 goto buffer_too_small;
632 }
633
634 return 0;
635
636buffer_too_small:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200637 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
Philipp Maier8970c492017-10-11 13:33:42 +0200638 return -1;
639}