blob: 10822e5f8a04aabda6eb7931dbd29bf2b703a103 [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
Philipp Maier057decf2023-02-07 12:00:33 +0100242 /* AMR octet aligned parameter (see also RFC 3267, section 8.3) */
Philipp Maier228e5912019-03-05 13:56:59 +0100243 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;
Philipp Maier228e5912019-03-05 13:56:59 +0100248 }
249
250 param_str = strtok(NULL, " ");
251 if (!param_str)
252 break;
253 count++;
254 }
255
256exit:
257 talloc_free(str);
258 return 0;
259error:
260 talloc_free(str);
261 return -EINVAL;
262}
263
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200264
265static int audio_ip_from_sdp(struct osmo_sockaddr *dst_addr, char *sdp)
266{
267 bool is_ipv6;
268 char ipbuf[INET6_ADDRSTRLEN];
269 if (strncmp("c=IN IP", sdp, 7) != 0)
270 return -1;
271 sdp += 7;
272 if (*sdp == '6')
273 is_ipv6 = true;
274 else if (*sdp == '4')
275 is_ipv6 = false;
276 else
277 return -1;
278 sdp++;
279 if (*sdp != ' ')
280 return -1;
281 sdp++;
282 if (is_ipv6) {
283 /* 45 = INET6_ADDRSTRLEN -1 */
284 if (sscanf(sdp, "%45s", ipbuf) != 1)
285 return -1;
286 if (inet_pton(AF_INET6, ipbuf, &dst_addr->u.sin6.sin6_addr) != 1)
287 return -1;
288 dst_addr->u.sa.sa_family = AF_INET6;
289 } else {
290 /* 15 = INET_ADDRSTRLEN -1 */
291 if (sscanf(sdp, "%15s", ipbuf) != 1)
292 return -1;
293 if (inet_pton(AF_INET, ipbuf, &dst_addr->u.sin.sin_addr) != 1)
294 return -1;
295 dst_addr->u.sa.sa_family = AF_INET;
296 }
297 return 0;
298}
299
Philipp Maier228e5912019-03-05 13:56:59 +0100300/* Pick optional fmtp parameters by payload type, if there are no fmtp
301 * parameters, a nullpointer is returned */
302static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
303{
304 unsigned int i;
305
306 for (i = 0; i < fmtp_params_len; i++) {
307 if (fmtp_params[i].payload_type == pt)
308 return &fmtp_params[i].param;
309 }
310
311 return NULL;
312}
313
Philipp Maier8970c492017-10-11 13:33:42 +0200314/*! Analyze SDP input string.
315 * \param[in] endp trunk endpoint.
316 * \param[out] conn associated rtp connection.
317 * \param[out] caller provided memory to store the parsing results.
Philipp Maier8970c492017-10-11 13:33:42 +0200318 *
319 * Note: In conn (conn->end) the function returns the packet duration,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200320 * rtp port, rtcp port and the codec information.
321 * \returns 0 on success, -1 on failure. */
Philipp Maier8970c492017-10-11 13:33:42 +0200322int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200323 struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200324{
Philipp Maierbc0346e2018-06-07 09:52:16 +0200325 struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
326 unsigned int codecs_used = 0;
Philipp Maier228e5912019-03-05 13:56:59 +0100327 struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
328 unsigned int fmtp_used = 0;
329 struct mgcp_codec_param *codec_param;
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200330 char ipbuf[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200331 char *line;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200332 unsigned int i;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200333 void *tmp_ctx = talloc_new(NULL);
Philipp Maier8970c492017-10-11 13:33:42 +0200334 struct mgcp_rtp_end *rtp;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200335
Pau Espin Pedrol8029b122021-12-23 16:22:30 +0100336 int payload_type;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200337 int ptime, ptime2 = 0;
338 char audio_name[64];
339 int port, rc;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200340
Philipp Maier8970c492017-10-11 13:33:42 +0200341 OSMO_ASSERT(endp);
342 OSMO_ASSERT(conn);
343 OSMO_ASSERT(p);
344
345 rtp = &conn->end;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200346 memset(&codecs, 0, sizeof(codecs));
347
348 for_each_line(line, p->save) {
349 switch (line[0]) {
350 case 'o':
351 case 's':
352 case 't':
353 case 'v':
354 /* skip these SDP attributes */
355 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200356 case 'a':
Pau Espin Pedrol8029b122021-12-23 16:22:30 +0100357 if (sscanf(line, "a=rtpmap:%d %63s", &payload_type, audio_name) == 2) {
358 codecs_update(tmp_ctx, codecs, codecs_used, payload_type, audio_name);
Philipp Maiere7ae69a2019-03-05 15:17:36 +0100359 break;
360 }
361
362 if (sscanf(line, "a=ptime:%d-%d", &ptime, &ptime2) >= 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200363 if (ptime2 > 0 && ptime2 != ptime)
364 rtp->packet_duration_ms = 0;
365 else
366 rtp->packet_duration_ms = ptime;
Philipp Maiere7ae69a2019-03-05 15:17:36 +0100367 break;
368 }
369
370 if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
Philipp Maierbc0346e2018-06-07 09:52:16 +0200371 rtp->maximum_packet_time = ptime2;
Philipp Maiere7ae69a2019-03-05 15:17:36 +0100372 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200373 }
Philipp Maier228e5912019-03-05 13:56:59 +0100374
375 if (strncmp("a=fmtp:", line, 6) == 0) {
376 rc = fmtp_from_sdp(conn->conn, &fmtp_params[fmtp_used], line);
377 if (rc >= 0)
378 fmtp_used++;
379 break;
380 }
381
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200382 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200383 case 'm':
Philipp Maierbc0346e2018-06-07 09:52:16 +0200384 rc = sscanf(line, "m=audio %d RTP/AVP", &port);
385 if (rc == 1) {
Pau Espin Pedrol5ffd1272022-10-04 13:45:48 +0200386 osmo_sockaddr_set_port(&rtp->addr.u.sa, port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200387 rtp->rtcp_port = htons(port + 1);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200388 }
Philipp Maierbc0346e2018-06-07 09:52:16 +0200389
390 rc = pt_from_sdp(conn->conn, codecs,
391 ARRAY_SIZE(codecs), line);
392 if (rc > 0)
393 codecs_used = rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200394 break;
Philipp Maierc7a228a2017-10-18 11:42:25 +0200395 case 'c':
Erice303fa92021-08-06 01:40:14 +0200396 if (audio_ip_from_sdp(&rtp->addr, line) < 0) {
397 talloc_free(tmp_ctx);
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200398 return -1;
Erice303fa92021-08-06 01:40:14 +0200399 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200400 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200401 default:
Philipp Maier8dc35972021-07-14 11:20:16 +0200402 if (endp)
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200403 /* TODO: Check spec: We used the bare endpoint number before,
404 * now we use the endpoint name as a whole? Is this allowed? */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200405 LOGP(DLMGCP, LOGL_NOTICE,
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200406 "Unhandled SDP option: '%c'/%d on %s\n",
407 line[0], line[0], endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200408 else
409 LOGP(DLMGCP, LOGL_NOTICE,
410 "Unhandled SDP option: '%c'/%d\n",
411 line[0], line[0]);
412 break;
413 }
414 }
Philipp Maierbc0346e2018-06-07 09:52:16 +0200415 OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200416
Philipp Maierbc0346e2018-06-07 09:52:16 +0200417 /* So far we have only set the payload type in the codec struct. Now we
418 * fill up the remaining fields of the codec description with some default
419 * information */
420 codecs_initialize(tmp_ctx, codecs, codecs_used);
421
422 /* Store parsed codec information */
Philipp Maierdbd70c72018-05-25 11:07:31 +0200423 for (i = 0; i < codecs_used; i++) {
Philipp Maier228e5912019-03-05 13:56:59 +0100424 codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
425 rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200426 if (rc < 0)
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200427 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "failed to add codec\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200428 }
429
430 talloc_free(tmp_ctx);
Philipp Maierdbd70c72018-05-25 11:07:31 +0200431
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200432 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200433 "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
Pau Espin Pedrol5ffd1272022-10-04 13:45:48 +0200434 osmo_sockaddr_port(&rtp->addr.u.sa), osmo_sockaddr_ntop(&rtp->addr.u.sa, ipbuf),
Philipp Maierbc0346e2018-06-07 09:52:16 +0200435 rtp->packet_duration_ms);
436 if (codecs_used == 0)
437 LOGPC(DLMGCP, LOGL_NOTICE, "none");
438 for (i = 0; i < codecs_used; i++) {
439 LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
440 rtp->codecs[i].payload_type,
Eric2ebcf5c2021-08-09 23:26:48 +0200441 strlen(rtp->codecs[i].subtype_name) ? rtp->codecs[i].subtype_name : "unknown");
Philipp Maierbc0346e2018-06-07 09:52:16 +0200442 LOGPC(DLMGCP, LOGL_NOTICE, " ");
443 }
444 LOGPC(DLMGCP, LOGL_NOTICE, "\n");
445
Philipp Maierdbd70c72018-05-25 11:07:31 +0200446 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200447}
448
Philipp Maier8482e832018-08-02 17:22:40 +0200449
450/* Add rtpmap string to the sdp payload, but only when the payload type falls
451 * into the dynamic payload type range */
452static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name)
453{
454 int rc;
455
456 if (payload_type >= 96 && payload_type <= 127) {
457 if (!audio_name)
458 return -EINVAL;
459 rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name);
460 if (rc < 0)
461 return -EINVAL;
462 }
463
464 return 0;
465}
466
Philipp Maier217d31d2019-03-05 14:48:56 +0100467/* Add audio strings to sdp payload */
Philipp Maier910189d2018-08-02 17:45:42 +0200468static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
469{
470 int rc;
471 unsigned int i;
472
Philipp Maier910189d2018-08-02 17:45:42 +0200473 rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
474 if (rc < 0)
475 return -EINVAL;
476
477 for (i = 0; i < payload_types_len; i++) {
478 rc = msgb_printf(sdp, " %d", payload_types[i]);
479 if (rc < 0)
480 return -EINVAL;
481 }
482
483 rc = msgb_printf(sdp, "\r\n");
484 if (rc < 0)
485 return -EINVAL;
486
487 return 0;
488}
489
Philipp Maier228e5912019-03-05 13:56:59 +0100490/* Add fmtp strings to sdp payload */
491static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len,
492 const char *fmtp_extra)
493{
494 unsigned int i;
495 int rc;
496 int fmtp_extra_pt = -1;
497 char *fmtp_extra_pars = "";
498
499 /* When no fmtp parameters ara available but an fmtp extra string
500 * is configured, just add the fmtp extra string */
501 if (fmtp_params_len == 0 && fmtp_extra) {
502 return msgb_printf(sdp, "%s\r\n", fmtp_extra);
503 }
504
505 /* When there is fmtp extra configured we dissect it in order to drop
506 * in the configured extra parameters at the right place when
507 * generating the fmtp strings. */
508 if (fmtp_extra) {
509 if (sscanf(fmtp_extra, "a=fmtp:%d ", &fmtp_extra_pt) != 1)
510 fmtp_extra_pt = -1;
511
512 fmtp_extra_pars = strstr(fmtp_extra, " ");
513
514 if (!fmtp_extra_pars)
515 fmtp_extra_pars = "";
516 else
517 fmtp_extra_pars++;
518 }
519
520 for (i = 0; i < fmtp_params_len; i++) {
521 rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
Vadim Yanitskiy13fae782020-07-07 14:12:26 +0700522 if (rc < 0)
523 return -EINVAL;
Philipp Maier228e5912019-03-05 13:56:59 +0100524
525 /* Add amr octet align parameter */
526 if (fmtp_params[i].param.amr_octet_aligned_present) {
527 if (fmtp_params[i].param.amr_octet_aligned)
528 rc = msgb_printf(sdp, " octet-align=1");
529 else
530 rc = msgb_printf(sdp, " octet-align=0");
531 if (rc < 0)
532 return -EINVAL;
533 }
534
535 /* Append extra parameters from fmtp extra */
536 if (fmtp_params[i].payload_type == fmtp_extra_pt) {
537 rc = msgb_printf(sdp, " %s", fmtp_extra_pars);
538 if (rc < 0)
539 return -EINVAL;
540 }
541
Vadim Yanitskiyb7d395d2020-07-07 14:09:45 +0700542 rc = msgb_printf(sdp, "\r\n");
Philipp Maier228e5912019-03-05 13:56:59 +0100543 if (rc < 0)
544 return -EINVAL;
545 }
546
547 return 0;
548}
549
Philipp Maier8970c492017-10-11 13:33:42 +0200550/*! Generate SDP response string.
551 * \param[in] endp trunk endpoint.
552 * \param[in] conn associated rtp connection.
553 * \param[out] sdp msg buffer to append resulting SDP string data.
554 * \param[in] addr IPV4 address string (e.g. 192.168.100.1).
555 * \returns 0 on success, -1 on failure. */
556int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
557 const struct mgcp_conn_rtp *conn, struct msgb *sdp,
558 const char *addr)
559{
Philipp Maier58128252019-03-06 11:28:18 +0100560 const struct mgcp_rtp_codec *codec;
Philipp Maier8970c492017-10-11 13:33:42 +0200561 const char *fmtp_extra;
562 const char *audio_name;
563 int payload_type;
Philipp Maier228e5912019-03-05 13:56:59 +0100564 struct sdp_fmtp_param fmtp_param;
Philipp Maier8970c492017-10-11 13:33:42 +0200565 int rc;
Philipp Maier910189d2018-08-02 17:45:42 +0200566 int payload_types[1];
Pau Espin Pedrola93c6e92019-05-06 15:23:57 +0200567 int local_port;
Philipp Maier228e5912019-03-05 13:56:59 +0100568 struct sdp_fmtp_param fmtp_params[1];
569 unsigned int fmtp_params_len = 0;
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200570 bool addr_is_v6;
Philipp Maier8970c492017-10-11 13:33:42 +0200571
572 OSMO_ASSERT(endp);
573 OSMO_ASSERT(conn);
574 OSMO_ASSERT(sdp);
575 OSMO_ASSERT(addr);
576
577 /* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
Ericfbf78d12021-08-23 22:31:39 +0200578 endp->trunk->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
Philipp Maier58128252019-03-06 11:28:18 +0100579 &codec, &fmtp_extra,
Philipp Maier8970c492017-10-11 13:33:42 +0200580 (struct mgcp_conn_rtp *)conn);
581
Philipp Maier58128252019-03-06 11:28:18 +0100582 audio_name = codec->audio_name;
583 payload_type = codec->payload_type;
584
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200585 addr_is_v6 = osmo_ip_str_type(addr) == AF_INET6;
586
Philipp Maier8970c492017-10-11 13:33:42 +0200587 rc = msgb_printf(sdp,
588 "v=0\r\n"
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200589 "o=- %s 23 IN IP%c %s\r\n"
Philipp Maier8970c492017-10-11 13:33:42 +0200590 "s=-\r\n"
Pau Espin Pedrola790f0c2020-08-31 13:29:11 +0200591 "c=IN IP%c %s\r\n"
592 "t=0 0\r\n", conn->conn->id,
593 addr_is_v6 ? '6' : '4', addr,
594 addr_is_v6 ? '6' : '4', addr);
Philipp Maier8970c492017-10-11 13:33:42 +0200595
596 if (rc < 0)
597 goto buffer_too_small;
598
599 if (payload_type >= 0) {
Philipp Maier910189d2018-08-02 17:45:42 +0200600
601 payload_types[0] = payload_type;
Pau Espin Pedrola93c6e92019-05-06 15:23:57 +0200602 if (mgcp_conn_rtp_is_osmux(conn))
Pau Espin Pedrol36413c02022-10-12 17:58:17 +0200603 local_port = endp->trunk->cfg->osmux.local_port;
Pau Espin Pedrola93c6e92019-05-06 15:23:57 +0200604 else
605 local_port = conn->end.local_port;
606 rc = add_audio(sdp, payload_types, 1, local_port);
Philipp Maier8970c492017-10-11 13:33:42 +0200607 if (rc < 0)
608 goto buffer_too_small;
609
Philipp Maier14b27a82020-06-02 20:15:30 +0200610 if (endp->trunk->audio_send_name) {
Philipp Maier8482e832018-08-02 17:22:40 +0200611 rc = add_rtpmap(sdp, payload_type, audio_name);
Philipp Maier8970c492017-10-11 13:33:42 +0200612 if (rc < 0)
613 goto buffer_too_small;
614 }
615
Philipp Maier228e5912019-03-05 13:56:59 +0100616 if (codec->param_present) {
617 fmtp_param.payload_type = payload_type;
618 fmtp_param.param = codec->param;
619 fmtp_params[0] = fmtp_param;
620 fmtp_params_len = 1;
Philipp Maier8970c492017-10-11 13:33:42 +0200621 }
Philipp Maier228e5912019-03-05 13:56:59 +0100622 rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
623 if (rc < 0)
624 goto buffer_too_small;
Philipp Maier8970c492017-10-11 13:33:42 +0200625 }
Philipp Maier14b27a82020-06-02 20:15:30 +0200626 if (conn->end.packet_duration_ms > 0 && endp->trunk->audio_send_ptime) {
Philipp Maier8970c492017-10-11 13:33:42 +0200627 rc = msgb_printf(sdp, "a=ptime:%u\r\n",
628 conn->end.packet_duration_ms);
629 if (rc < 0)
630 goto buffer_too_small;
631 }
632
633 return 0;
634
635buffer_too_small:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200636 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
Philipp Maier8970c492017-10-11 13:33:42 +0200637 return -1;
638}