blob: 20476375e284cbdfa23b29f87010c542febfb2d4 [file] [log] [blame]
Neels Hofmeyre9920f22017-07-10 15:07:22 +02001/* mgcp_utils - common functions to setup an MGCP connection
2 */
3/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
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 */
20
21#include <osmocom/core/linuxlist.h>
22#include <osmocom/core/select.h>
23#include <osmocom/core/write_queue.h>
24#include <osmocom/core/msgb.h>
25#include <osmocom/core/logging.h>
Philipp Maier1dc6be62017-10-05 18:25:37 +020026#include <osmocom/core/byteswap.h>
Neels Hofmeyre9920f22017-07-10 15:07:22 +020027
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +020028#include <osmocom/mgcp_client/mgcp_client.h>
29#include <osmocom/mgcp_client/mgcp_client_internal.h>
Neels Hofmeyre9920f22017-07-10 15:07:22 +020030
31#include <netinet/in.h>
32#include <arpa/inet.h>
33
34#include <errno.h>
35#include <unistd.h>
36#include <string.h>
37
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +020038void mgcp_client_conf_init(struct mgcp_client_conf *conf)
Neels Hofmeyre9920f22017-07-10 15:07:22 +020039{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +020040 /* NULL and -1 default to MGCP_CLIENT_*_DEFAULT values */
41 *conf = (struct mgcp_client_conf){
Neels Hofmeyre9920f22017-07-10 15:07:22 +020042 .local_addr = NULL,
43 .local_port = -1,
44 .remote_addr = NULL,
45 .remote_port = -1,
46 .first_endpoint = 0,
47 .last_endpoint = 0,
48 .bts_base = 0,
49 };
50}
51
52/* Test if a given endpoint id is currently in use */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +020053static bool endpoint_in_use(uint16_t id, struct mgcp_client *client)
Neels Hofmeyre9920f22017-07-10 15:07:22 +020054{
55 struct mgcp_inuse_endpoint *endpoint;
56 llist_for_each_entry(endpoint, &client->inuse_endpoints, entry) {
57 if (endpoint->id == id)
58 return true;
59 }
60
61 return false;
62}
63
64/* Find and seize an unsused endpoint id */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +020065int mgcp_client_next_endpoint(struct mgcp_client *client)
Neels Hofmeyre9920f22017-07-10 15:07:22 +020066{
67 int i;
68 uint16_t first_endpoint = client->actual.first_endpoint;
69 uint16_t last_endpoint = client->actual.last_endpoint;
70 struct mgcp_inuse_endpoint *endpoint;
71
72 /* Use the maximum permitted range if the VTY
73 * configuration does not specify a range */
74 if (client->actual.last_endpoint == 0) {
75 first_endpoint = 1;
76 last_endpoint = 65534;
77 }
78
79 /* Test the permitted endpoint range for an endpoint
80 * number that is not in use. When a suitable endpoint
81 * number can be found, seize it by adding it to the
82 * inuse list. */
83 for (i=first_endpoint;i<last_endpoint;i++)
84 {
85 if (endpoint_in_use(i,client) == false) {
86 endpoint = talloc_zero(client, struct mgcp_inuse_endpoint);
87 endpoint->id = i;
88 llist_add_tail(&endpoint->entry, &client->inuse_endpoints);
89 return endpoint->id;
90 }
91 }
92
93 /* All endpoints are busy! */
94 return -EINVAL;
95}
96
97/* Release a seized endpoint id to make it available again for other calls */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +020098void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client)
Neels Hofmeyre9920f22017-07-10 15:07:22 +020099{
100 struct mgcp_inuse_endpoint *endpoint;
101 struct mgcp_inuse_endpoint *endpoint_tmp;
102 llist_for_each_entry_safe(endpoint, endpoint_tmp, &client->inuse_endpoints, entry) {
103 if (endpoint->id == id) {
104 llist_del(&endpoint->entry);
105 talloc_free(endpoint);
106 }
107 }
108}
109
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200110static void mgcp_client_handle_response(struct mgcp_client *mgcp,
111 struct mgcp_response_pending *pending,
112 struct mgcp_response *response)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200113{
114 if (!pending) {
115 LOGP(DLMGCP, LOGL_ERROR,
116 "Cannot handle NULL response\n");
117 return;
118 }
119 if (pending->response_cb)
120 pending->response_cb(response, pending->priv);
121 else
122 LOGP(DLMGCP, LOGL_INFO, "MGCP response ignored (NULL cb)\n");
123 talloc_free(pending);
124}
125
126static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
127{
128 int comment_pos;
129 char *end;
130
131 if (mgcp_msg_terminate_nul(msg))
132 goto response_parse_failure;
133
134 r->body = (char *)msg->data;
135
Harald Welte9bf7c532017-11-17 14:14:31 +0100136 if (sscanf(r->body, "%3d %u %n",
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200137 &r->head.response_code, &r->head.trans_id,
138 &comment_pos) != 2)
139 goto response_parse_failure;
140
141 r->head.comment = r->body + comment_pos;
142 end = strchr(r->head.comment, '\r');
143 if (!end)
144 goto response_parse_failure;
145 /* Mark the end of the comment */
146 *end = '\0';
147 r->body = end + 1;
148 if (r->body[0] == '\n')
149 r->body ++;
150 return 0;
151
152response_parse_failure:
153 LOGP(DLMGCP, LOGL_ERROR,
154 "Failed to parse MGCP response header\n");
155 return -EINVAL;
156}
157
158/* TODO undup against mgcp_protocol.c:mgcp_check_param() */
159static bool mgcp_line_is_valid(const char *line)
160{
161 const size_t line_len = strlen(line);
162 if (line[0] == '\0')
163 return true;
164
165 if (line_len < 2
166 || line[1] != '=') {
167 LOGP(DLMGCP, LOGL_ERROR,
168 "Wrong MGCP option format: '%s'\n",
169 line);
170 return false;
171 }
172
173 return true;
174}
175
176/* Parse a line like "m=audio 16002 RTP/AVP 98" */
Philipp Maier06da85e2017-10-05 18:49:24 +0200177static int mgcp_parse_audio_port(struct mgcp_response *r, const char *line)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200178{
Harald Welte9bf7c532017-11-17 14:14:31 +0100179 if (sscanf(line, "m=audio %hu",
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200180 &r->audio_port) != 1)
181 goto response_parse_failure;
182
183 return 0;
184
185response_parse_failure:
186 LOGP(DLMGCP, LOGL_ERROR,
Philipp Maier06da85e2017-10-05 18:49:24 +0200187 "Failed to parse MGCP response header (audio port)\n");
188 return -EINVAL;
189}
190
191/* Parse a line like "c=IN IP4 10.11.12.13" */
192static int mgcp_parse_audio_ip(struct mgcp_response *r, const char *line)
193{
194 struct in_addr ip_test;
195
196 if (strlen(line) < 16)
197 goto response_parse_failure;
198
199 /* The current implementation strictly supports IPV4 only ! */
200 if (memcmp("c=IN IP4 ", line, 9) != 0)
201 goto response_parse_failure;
202
203 /* Extract IP-Address */
204 strncpy(r->audio_ip, line + 9, sizeof(r->audio_ip));
205 r->audio_ip[sizeof(r->audio_ip) - 1] = '\0';
206
207 /* Check IP-Address */
208 if (inet_aton(r->audio_ip, &ip_test) == 0)
209 goto response_parse_failure;
210
211 return 0;
212
213response_parse_failure:
214 LOGP(DLMGCP, LOGL_ERROR,
215 "Failed to parse MGCP response header (audio ip)\n");
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200216 return -EINVAL;
217}
218
219int mgcp_response_parse_params(struct mgcp_response *r)
220{
221 char *line;
222 int rc;
223 OSMO_ASSERT(r->body);
224 char *data = strstr(r->body, "\n\n");
225
226 if (!data) {
227 LOGP(DLMGCP, LOGL_ERROR,
228 "MGCP response: cannot find start of parameters\n");
229 return -EINVAL;
230 }
231
232 /* Advance to after the \n\n, replace the second \n with \0. That's
233 * where the parameters start. */
234 data ++;
235 *data = '\0';
236 data ++;
237
Neels Hofmeyrd95ab1e2017-09-22 00:52:54 +0200238 for_each_non_empty_line(line, data) {
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200239 if (!mgcp_line_is_valid(line))
240 return -EINVAL;
241
242 switch (line[0]) {
243 case 'm':
Philipp Maier06da85e2017-10-05 18:49:24 +0200244 rc = mgcp_parse_audio_port(r, line);
245 if (rc)
246 return rc;
247 break;
248 case 'c':
249 rc = mgcp_parse_audio_ip(r, line);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200250 if (rc)
251 return rc;
252 break;
253 default:
254 /* skip unhandled parameters */
255 break;
256 }
257 }
258 return 0;
259}
260
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200261static struct mgcp_response_pending *mgcp_client_response_pending_get(
262 struct mgcp_client *mgcp,
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200263 struct mgcp_response *r)
264{
265 struct mgcp_response_pending *pending;
266 if (!r)
267 return NULL;
268 llist_for_each_entry(pending, &mgcp->responses_pending, entry) {
269 if (pending->trans_id == r->head.trans_id) {
270 llist_del(&pending->entry);
271 return pending;
272 }
273 }
274 return NULL;
275}
276
277/* Feed an MGCP message into the receive processing.
278 * Parse the head and call any callback registered for the transaction id found
279 * in the MGCP message. This is normally called directly from the internal
280 * mgcp_do_read that reads from the socket connected to the MGCP gateway. This
281 * function is published mainly to be able to feed data from the test suite.
282 */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200283int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200284{
285 struct mgcp_response r = { 0 };
286 struct mgcp_response_pending *pending;
287 int rc;
288
289 rc = mgcp_response_parse_head(&r, msg);
290 if (rc) {
291 LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response\n");
292 return -1;
293 }
294
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200295 pending = mgcp_client_response_pending_get(mgcp, &r);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200296 if (!pending) {
297 LOGP(DLMGCP, LOGL_ERROR,
298 "Cannot find matching MGCP transaction for trans_id %d\n",
299 r.head.trans_id);
300 return -1;
301 }
302
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200303 mgcp_client_handle_response(mgcp, pending, &r);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200304 return 0;
305}
306
307static int mgcp_do_read(struct osmo_fd *fd)
308{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200309 struct mgcp_client *mgcp = fd->data;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200310 struct msgb *msg;
311 int ret;
312
313 msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
314 if (!msg) {
315 LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n");
316 return -1;
317 }
318
319 ret = read(fd->fd, msg->data, 4096 - 128);
320 if (ret <= 0) {
321 LOGP(DLMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno));
322 msgb_free(msg);
323 return -1;
324 } else if (ret > 4096 - 128) {
325 LOGP(DLMGCP, LOGL_ERROR, "Too much data: %d\n", ret);
326 msgb_free(msg);
327 return -1;
Harald Welte9bf7c532017-11-17 14:14:31 +0100328 }
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200329
330 msg->l2h = msgb_put(msg, ret);
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200331 ret = mgcp_client_rx(mgcp, msg);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200332 talloc_free(msg);
333 return ret;
334}
335
336static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
337{
338 int ret;
339 static char strbuf[4096];
340 unsigned int l = msg->len < sizeof(strbuf) ? msg->len : sizeof(strbuf);
341 unsigned int i;
342
343 strncpy(strbuf, (const char*)msg->data, l);
344 for (i = 0; i < sizeof(strbuf); i++) {
345 if (strbuf[i] == '\n' || strbuf[i] == '\r') {
346 strbuf[i] = '\0';
347 break;
348 }
349 }
350 DEBUGP(DLMGCP, "Tx MGCP msg to MGCP GW: '%s'\n", strbuf);
351
352 LOGP(DLMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len);
353
354 ret = write(fd->fd, msg->data, msg->len);
355 if (ret != msg->len)
356 LOGP(DLMGCP, LOGL_ERROR, "Failed to forward message to MGCP"
357 " GW: %s\n", strerror(errno));
358
359 return ret;
360}
361
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200362struct mgcp_client *mgcp_client_init(void *ctx,
363 struct mgcp_client_conf *conf)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200364{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200365 struct mgcp_client *mgcp;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200366
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200367 mgcp = talloc_zero(ctx, struct mgcp_client);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200368
369 INIT_LLIST_HEAD(&mgcp->responses_pending);
370 INIT_LLIST_HEAD(&mgcp->inuse_endpoints);
371
372 mgcp->next_trans_id = 1;
373
374 mgcp->actual.local_addr = conf->local_addr ? conf->local_addr :
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200375 MGCP_CLIENT_LOCAL_ADDR_DEFAULT;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200376 mgcp->actual.local_port = conf->local_port >= 0 ? (uint16_t)conf->local_port :
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200377 MGCP_CLIENT_LOCAL_PORT_DEFAULT;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200378
379 mgcp->actual.remote_addr = conf->remote_addr ? conf->remote_addr :
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200380 MGCP_CLIENT_REMOTE_ADDR_DEFAULT;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200381 mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200382 MGCP_CLIENT_REMOTE_PORT_DEFAULT;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200383
384 mgcp->actual.first_endpoint = conf->first_endpoint > 0 ? (uint16_t)conf->first_endpoint : 0;
385 mgcp->actual.last_endpoint = conf->last_endpoint > 0 ? (uint16_t)conf->last_endpoint : 0;
386 mgcp->actual.bts_base = conf->bts_base > 0 ? (uint16_t)conf->bts_base : 4000;
387
388 return mgcp;
389}
390
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200391int mgcp_client_connect(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200392{
393 int on;
394 struct sockaddr_in addr;
395 struct osmo_wqueue *wq;
396 int rc;
397
398 if (!mgcp) {
399 LOGP(DLMGCP, LOGL_FATAL, "MGCPGW client not initialized properly\n");
400 return -EINVAL;
401 }
402
403 wq = &mgcp->wq;
404
405 wq->bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
406 if (wq->bfd.fd < 0) {
407 LOGP(DLMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno);
408 return -errno;
409 }
410
411 on = 1;
412 if (setsockopt(wq->bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
413 LOGP(DLMGCP, LOGL_FATAL,
414 "Failed to initialize socket for MGCP GW: %s\n",
415 strerror(errno));
416 rc = -errno;
417 goto error_close_fd;
418 }
419
420 /* bind socket */
421 memset(&addr, 0, sizeof(addr));
422 addr.sin_family = AF_INET;
423 inet_aton(mgcp->actual.local_addr, &addr.sin_addr);
424 addr.sin_port = htons(mgcp->actual.local_port);
425 if (bind(wq->bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
426 LOGP(DLMGCP, LOGL_FATAL,
427 "Failed to bind for MGCP GW to %s %u\n",
428 mgcp->actual.local_addr, mgcp->actual.local_port);
429 rc = -errno;
430 goto error_close_fd;
431 }
432
433 /* connect to the remote */
434 inet_aton(mgcp->actual.remote_addr, &addr.sin_addr);
435 addr.sin_port = htons(mgcp->actual.remote_port);
436 if (connect(wq->bfd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
437 LOGP(DLMGCP, LOGL_FATAL,
438 "Failed to connect to MGCP GW at %s %u: %s\n",
439 mgcp->actual.remote_addr, mgcp->actual.remote_port,
440 strerror(errno));
441 rc = -errno;
442 goto error_close_fd;
443 }
444
445 mgcp->remote_addr = htonl(addr.sin_addr.s_addr);
446
447 osmo_wqueue_init(wq, 10);
448 wq->bfd.when = BSC_FD_READ;
449 wq->bfd.data = mgcp;
450 wq->read_cb = mgcp_do_read;
451 wq->write_cb = mgcp_do_write;
452
453 if (osmo_fd_register(&wq->bfd) != 0) {
454 LOGP(DLMGCP, LOGL_FATAL, "Failed to register BFD\n");
455 rc = -EIO;
456 goto error_close_fd;
457 }
458 LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n",
459 mgcp->actual.local_addr, mgcp->actual.local_port,
460 mgcp->actual.remote_addr, mgcp->actual.remote_port);
461
462 return 0;
463error_close_fd:
464 close(wq->bfd.fd);
465 wq->bfd.fd = -1;
466 return rc;
467}
468
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200469const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200470{
471 return mgcp->actual.remote_addr;
472}
473
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200474uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200475{
476 return mgcp->actual.remote_port;
477}
478
479/* Return the MGCP GW binary IPv4 address in network byte order. */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200480uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200481{
482 return mgcp->remote_addr;
483}
484
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200485struct mgcp_response_pending * mgcp_client_pending_add(
486 struct mgcp_client *mgcp,
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200487 mgcp_trans_id_t trans_id,
488 mgcp_response_cb_t response_cb,
489 void *priv)
490{
491 struct mgcp_response_pending *pending;
492
493 pending = talloc_zero(mgcp, struct mgcp_response_pending);
494 pending->trans_id = trans_id;
495 pending->response_cb = response_cb;
496 pending->priv = priv;
497 llist_add_tail(&pending->entry, &mgcp->responses_pending);
498
499 return pending;
500}
501
502/* Send the MGCP message in msg to the MGCP GW and handle a response with
503 * response_cb. NOTE: the response_cb still needs to call
504 * mgcp_response_parse_params(response) to get the parsed parameters -- to
505 * potentially save some CPU cycles, only the head line has been parsed when
506 * the response_cb is invoked. */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200507int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
508 mgcp_response_cb_t response_cb, void *priv)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200509{
510 struct mgcp_response_pending *pending;
511 mgcp_trans_id_t trans_id;
512 int rc;
513
514 trans_id = msg->cb[MSGB_CB_MGCP_TRANS_ID];
515 if (!trans_id) {
516 LOGP(DLMGCP, LOGL_ERROR,
517 "Unset transaction id in mgcp send request\n");
518 talloc_free(msg);
519 return -EINVAL;
520 }
521
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200522 pending = mgcp_client_pending_add(mgcp, trans_id, response_cb, priv);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200523
524 if (msgb_l2len(msg) > 4096) {
525 LOGP(DLMGCP, LOGL_ERROR,
526 "Cannot send, MGCP message too large: %u\n",
527 msgb_l2len(msg));
528 msgb_free(msg);
529 rc = -EINVAL;
530 goto mgcp_tx_error;
531 }
532
533 rc = osmo_wqueue_enqueue(&mgcp->wq, msg);
534 if (rc) {
535 LOGP(DLMGCP, LOGL_FATAL, "Could not queue message to MGCP GW\n");
536 msgb_free(msg);
537 goto mgcp_tx_error;
538 } else
539 LOGP(DLMGCP, LOGL_INFO, "Queued %u bytes for MGCP GW\n",
540 msgb_l2len(msg));
541 return 0;
542
543mgcp_tx_error:
544 /* Pass NULL to response cb to indicate an error */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200545 mgcp_client_handle_response(mgcp, pending, NULL);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200546 return -1;
547}
548
549static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id,
550 const char *buf, int len)
551{
552 struct msgb *msg;
553
554 if (len > (4096 - 128)) {
555 LOGP(DLMGCP, LOGL_ERROR, "Cannot send to MGCP GW:"
556 " message too large: %d\n", len);
557 return NULL;
558 }
559
560 msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
561 OSMO_ASSERT(msg);
562
563 char *dst = (char*)msgb_put(msg, len);
564 memcpy(dst, buf, len);
565 msg->l2h = msg->data;
566 msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
567
568 return msg;
569}
570
571static struct msgb *mgcp_msg_from_str(mgcp_trans_id_t trans_id,
572 const char *fmt, ...)
573{
574 static char compose[4096 - 128];
575 va_list ap;
576 int len;
577 OSMO_ASSERT(fmt);
578
579 va_start(ap, fmt);
580 len = vsnprintf(compose, sizeof(compose), fmt, ap);
581 va_end(ap);
582 if (len >= sizeof(compose)) {
583 LOGP(DLMGCP, LOGL_ERROR,
584 "Message too large: trans_id=%u len=%d\n",
585 trans_id, len);
586 return NULL;
587 }
588 if (len < 1) {
589 LOGP(DLMGCP, LOGL_ERROR,
590 "Failed to compose message: trans_id=%u len=%d\n",
591 trans_id, len);
592 return NULL;
593 }
594 return mgcp_msg_from_buf(trans_id, compose, len);
595}
596
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200597static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200598{
599 /* avoid zero trans_id to distinguish from unset trans_id */
600 if (!mgcp->next_trans_id)
601 mgcp->next_trans_id ++;
602 return mgcp->next_trans_id ++;
603}
604
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200605struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200606 uint16_t rtp_endpoint, unsigned int call_id,
607 enum mgcp_connection_mode mode)
608{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200609 mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200610 return mgcp_msg_from_str(trans_id,
611 "CRCX %u %x@mgw MGCP 1.0\r\n"
612 "C: %x\r\n"
613 "L: p:20, a:AMR, nt:IN\r\n"
614 "M: %s\r\n"
615 ,
616 trans_id,
617 rtp_endpoint,
618 call_id,
Neels Hofmeyrd95ab1e2017-09-22 00:52:54 +0200619 mgcp_client_cmode_name(mode));
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200620}
621
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200622struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200623 uint16_t rtp_endpoint, const char *rtp_conn_addr,
624 uint16_t rtp_port, enum mgcp_connection_mode mode)
625
626{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200627 mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200628 return mgcp_msg_from_str(trans_id,
629 "MDCX %u %x@mgw MGCP 1.0\r\n"
630 "M: %s\r\n"
631 "\r\n"
632 "c=IN IP4 %s\r\n"
633 "m=audio %u RTP/AVP 255\r\n"
634 ,
635 trans_id,
636 rtp_endpoint,
Neels Hofmeyrd95ab1e2017-09-22 00:52:54 +0200637 mgcp_client_cmode_name(mode),
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200638 rtp_conn_addr,
639 rtp_port);
640}
641
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200642struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200643 unsigned int call_id)
644{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200645 mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200646 return mgcp_msg_from_str(trans_id,
647 "DLCX %u %x@mgw MGCP 1.0\r\n"
648 "C: %x\r\n", trans_id, rtp_endpoint, call_id);
649}
650
Philipp Maier1dc6be62017-10-05 18:25:37 +0200651#define MGCP_CRCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
652 MGCP_MSG_PRESENCE_CALL_ID | \
653 MGCP_MSG_PRESENCE_CONN_ID | \
654 MGCP_MSG_PRESENCE_CONN_MODE)
655#define MGCP_MDCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT | \
656 MGCP_MSG_PRESENCE_CONN_ID)
657#define MGCP_DLCX_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
658#define MGCP_AUEP_MANDATORY (MGCP_MSG_PRESENCE_ENDPOINT)
659#define MGCP_RSIP_MANDATORY 0 /* none */
660
661struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg)
662{
663 mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
664 uint32_t mandatory_mask;
665 struct msgb *msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
666 int rc = 0;
667
668 msg->l2h = msg->data;
669 msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
670
671 /* Add command verb */
672 switch (mgcp_msg->verb) {
673 case MGCP_VERB_CRCX:
674 mandatory_mask = MGCP_CRCX_MANDATORY;
675 rc += msgb_printf(msg, "CRCX %u", trans_id);
676 break;
677 case MGCP_VERB_MDCX:
678 mandatory_mask = MGCP_MDCX_MANDATORY;
679 rc += msgb_printf(msg, "MDCX %u", trans_id);
680 break;
681 case MGCP_VERB_DLCX:
682 mandatory_mask = MGCP_DLCX_MANDATORY;
683 rc += msgb_printf(msg, "DLCX %u", trans_id);
684 break;
685 case MGCP_VERB_AUEP:
686 mandatory_mask = MGCP_AUEP_MANDATORY;
687 rc += msgb_printf(msg, "AUEP %u", trans_id);
688 break;
689 case MGCP_VERB_RSIP:
690 mandatory_mask = MGCP_RSIP_MANDATORY;
691 rc += msgb_printf(msg, "RSIP %u", trans_id);
692 break;
693 default:
694 LOGP(DLMGCP, LOGL_ERROR,
695 "Invalid command verb, can not generate MGCP message\n");
696 msgb_free(msg);
697 return NULL;
698 }
699
700 /* Check if mandatory fields are missing */
701 if (!((mgcp_msg->presence & mandatory_mask) == mandatory_mask)) {
702 LOGP(DLMGCP, LOGL_ERROR,
703 "One or more missing mandatory fields, can not generate MGCP message\n");
704 msgb_free(msg);
705 return NULL;
706 }
707
708 /* Add endpoint name */
709 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_ENDPOINT)
710 rc += msgb_printf(msg, " %s", mgcp_msg->endpoint);
711
712 /* Add protocol version */
713 rc += msgb_printf(msg, " MGCP 1.0\r\n");
714
715 /* Add call id */
716 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CALL_ID)
717 rc += msgb_printf(msg, "C: %x\r\n", mgcp_msg->call_id);
718
719 /* Add connection id */
720 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID)
721 rc += msgb_printf(msg, "I: %u\r\n", mgcp_msg->conn_id);
722
723 /* Add local connection options */
724 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_ID
725 && mgcp_msg->verb == MGCP_VERB_CRCX)
726 rc += msgb_printf(msg, "L: p:20, a:AMR, nt:IN\r\n");
727
728 /* Add mode */
729 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_CONN_MODE)
730 rc +=
731 msgb_printf(msg, "M: %s\r\n",
732 mgcp_client_cmode_name(mgcp_msg->conn_mode));
733
734 /* Add RTP address and port (SDP) */
735 if (mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_IP
736 && mgcp_msg->presence & MGCP_MSG_PRESENCE_AUDIO_PORT) {
737 rc += msgb_printf(msg, "\r\n");
738 rc += msgb_printf(msg, "c=IN IP4 %s\r\n", mgcp_msg->audio_ip);
739 rc +=
740 msgb_printf(msg, "m=audio %u RTP/AVP 255\r\n",
741 mgcp_msg->audio_port);
742 }
743
744 if (rc != 0) {
745 LOGP(DLMGCP, LOGL_ERROR,
746 "message buffer to small, can not generate MGCP message\n");
747 msgb_free(msg);
748 msg = NULL;
749 }
750
751 return msg;
752}
753
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200754struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200755{
756 return &mgcp->actual;
757}
Neels Hofmeyrd95ab1e2017-09-22 00:52:54 +0200758
759const struct value_string mgcp_client_connection_mode_strs[] = {
760 { MGCP_CONN_NONE, "none" },
761 { MGCP_CONN_RECV_SEND, "sendrecv" },
762 { MGCP_CONN_SEND_ONLY, "sendonly" },
763 { MGCP_CONN_RECV_ONLY, "recvonly" },
764 { MGCP_CONN_LOOPBACK, "loopback" },
765 { 0, NULL }
766};