blob: 1910a9f353ec499716d6f0a2f365c5ff117d61c3 [file] [log] [blame]
Neels Hofmeyr2188a772016-05-20 21:59:55 +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/select.h>
22#include <osmocom/core/write_queue.h>
23#include <osmocom/core/msgb.h>
24#include <osmocom/core/logging.h>
25
26#include <openbsc/mgcpgw_client.h>
27#include <openbsc/mgcp.h>
28#include <openbsc/mgcp_internal.h>
29#include <openbsc/debug.h>
30
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 Hofmeyr2188a772016-05-20 21:59:55 +020038void mgcpgw_client_conf_init(struct mgcpgw_client_conf *conf)
39{
40 /* NULL and -1 default to MGCPGW_CLIENT_*_DEFAULT values */
41 *conf = (struct mgcpgw_client_conf){
42 .local_addr = NULL,
43 .local_port = -1,
44 .remote_addr = NULL,
45 .remote_port = -1,
Philipp Maierefe85d32017-04-09 12:32:51 +020046 .first_endpoint = 0,
47 .last_endpoint = 0,
48 .bts_base = 0,
Neels Hofmeyr2188a772016-05-20 21:59:55 +020049 };
50}
51
Philipp Maierefe85d32017-04-09 12:32:51 +020052/* Test if a given endpoint id is currently in use */
53static bool endpoint_in_use(uint16_t id, struct mgcpgw_client *client)
Neels Hofmeyr2188a772016-05-20 21:59:55 +020054{
Philipp Maierefe85d32017-04-09 12:32:51 +020055 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 */
65int mgcpgw_client_next_endpoint(struct mgcpgw_client *client)
66{
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 */
98void mgcpgw_client_release_endpoint(uint16_t id, struct mgcpgw_client *client)
99{
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 }
Neels Hofmeyr2188a772016-05-20 21:59:55 +0200108}
109
110static void mgcpgw_client_handle_response(struct mgcpgw_client *mgcp,
111 struct mgcp_response_pending *pending,
112 struct mgcp_response *response)
113{
114 if (!pending) {
115 LOGP(DMGCP, 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(DMGCP, 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
136 if (sscanf(r->body, "%3d %u %n",
137 &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(DMGCP, 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(DMGCP, 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" */
177static int mgcp_parse_audio(struct mgcp_response *r, const char *line)
178{
179 if (sscanf(line, "m=audio %hu",
180 &r->audio_port) != 1)
181 goto response_parse_failure;
182
183 return 0;
184
185response_parse_failure:
186 LOGP(DMGCP, LOGL_ERROR,
187 "Failed to parse MGCP response header\n");
188 return -EINVAL;
189}
190
191int mgcp_response_parse_params(struct mgcp_response *r)
192{
193 char *line;
194 int rc;
195 OSMO_ASSERT(r->body);
196 char *data = strstr(r->body, "\n\n");
197
198 if (!data) {
199 LOGP(DMGCP, LOGL_ERROR,
200 "MGCP response: cannot find start of parameters\n");
201 return -EINVAL;
202 }
203
204 /* Advance to after the \n\n, replace the second \n with \0. That's
205 * where the parameters start. */
206 data ++;
207 *data = '\0';
208 data ++;
209
210 for_each_line(line, data) {
211 if (!mgcp_line_is_valid(line))
212 return -EINVAL;
213
214 switch (line[0]) {
215 case 'm':
216 rc = mgcp_parse_audio(r, line);
217 if (rc)
218 return rc;
219 break;
220 default:
221 /* skip unhandled parameters */
222 break;
223 }
224 }
225 return 0;
226}
227
228static struct mgcp_response_pending *mgcpgw_client_response_pending_get(
229 struct mgcpgw_client *mgcp,
230 struct mgcp_response *r)
231{
232 struct mgcp_response_pending *pending;
233 if (!r)
234 return NULL;
235 llist_for_each_entry(pending, &mgcp->responses_pending, entry) {
236 if (pending->trans_id == r->head.trans_id) {
237 llist_del(&pending->entry);
238 return pending;
239 }
240 }
241 return NULL;
242}
243
244/* Feed an MGCP message into the receive processing.
245 * Parse the head and call any callback registered for the transaction id found
246 * in the MGCP message. This is normally called directly from the internal
247 * mgcp_do_read that reads from the socket connected to the MGCP gateway. This
248 * function is published mainly to be able to feed data from the test suite.
249 */
250int mgcpgw_client_rx(struct mgcpgw_client *mgcp, struct msgb *msg)
251{
252 struct mgcp_response r = { 0 };
253 struct mgcp_response_pending *pending;
254 int rc;
255
256 rc = mgcp_response_parse_head(&r, msg);
257 if (rc) {
258 LOGP(DMGCP, LOGL_ERROR, "Cannot parse MGCP response\n");
259 return -1;
260 }
261
262 pending = mgcpgw_client_response_pending_get(mgcp, &r);
263 if (!pending) {
264 LOGP(DMGCP, LOGL_ERROR,
265 "Cannot find matching MGCP transaction for trans_id %d\n",
266 r.head.trans_id);
267 return -1;
268 }
269
270 mgcpgw_client_handle_response(mgcp, pending, &r);
271 return 0;
272}
273
274static int mgcp_do_read(struct osmo_fd *fd)
275{
276 struct mgcpgw_client *mgcp = fd->data;
277 struct msgb *msg;
278 int ret;
279
280 msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
281 if (!msg) {
282 LOGP(DMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n");
283 return -1;
284 }
285
286 ret = read(fd->fd, msg->data, 4096 - 128);
287 if (ret <= 0) {
288 LOGP(DMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno));
289 msgb_free(msg);
290 return -1;
291 } else if (ret > 4096 - 128) {
292 LOGP(DMGCP, LOGL_ERROR, "Too much data: %d\n", ret);
293 msgb_free(msg);
294 return -1;
295 }
296
297 msg->l2h = msgb_put(msg, ret);
298 ret = mgcpgw_client_rx(mgcp, msg);
299 talloc_free(msg);
300 return ret;
301}
302
303static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
304{
305 int ret;
306 static char strbuf[4096];
Philipp Maierefe85d32017-04-09 12:32:51 +0200307 unsigned int l = msg->len < sizeof(strbuf) ? msg->len : sizeof(strbuf);
308 unsigned int i;
309
Neels Hofmeyr2188a772016-05-20 21:59:55 +0200310 strncpy(strbuf, (const char*)msg->data, l);
Philipp Maierefe85d32017-04-09 12:32:51 +0200311 for (i = 0; i < sizeof(strbuf); i++) {
312 if (strbuf[i] == '\n' || strbuf[i] == '\r') {
313 strbuf[i] = '\0';
314 break;
315 }
316 }
Neels Hofmeyr2188a772016-05-20 21:59:55 +0200317 DEBUGP(DMGCP, "Tx MGCP msg to MGCP GW: '%s'\n", strbuf);
318
319 LOGP(DMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len);
320
321 ret = write(fd->fd, msg->data, msg->len);
322 if (ret != msg->len)
323 LOGP(DMGCP, LOGL_ERROR, "Failed to forward message to MGCP"
324 " GW: %s\n", strerror(errno));
325
326 return ret;
327}
328
329struct mgcpgw_client *mgcpgw_client_init(void *ctx,
330 struct mgcpgw_client_conf *conf)
331{
332 struct mgcpgw_client *mgcp;
333
334 mgcp = talloc_zero(ctx, struct mgcpgw_client);
335
336 INIT_LLIST_HEAD(&mgcp->responses_pending);
Philipp Maierefe85d32017-04-09 12:32:51 +0200337 INIT_LLIST_HEAD(&mgcp->inuse_endpoints);
Neels Hofmeyr2188a772016-05-20 21:59:55 +0200338
339 mgcp->next_trans_id = 1;
Neels Hofmeyr2188a772016-05-20 21:59:55 +0200340
341 mgcp->actual.local_addr = conf->local_addr ? conf->local_addr :
342 MGCPGW_CLIENT_LOCAL_ADDR_DEFAULT;
343 mgcp->actual.local_port = conf->local_port >= 0 ? (uint16_t)conf->local_port :
344 MGCPGW_CLIENT_LOCAL_PORT_DEFAULT;
345
346 mgcp->actual.remote_addr = conf->remote_addr ? conf->remote_addr :
347 MGCPGW_CLIENT_REMOTE_ADDR_DEFAULT;
348 mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
349 MGCPGW_CLIENT_REMOTE_PORT_DEFAULT;
350
Philipp Maierefe85d32017-04-09 12:32:51 +0200351 mgcp->actual.first_endpoint = conf->first_endpoint > 0 ? (uint16_t)conf->first_endpoint : 0;
352 mgcp->actual.last_endpoint = conf->last_endpoint > 0 ? (uint16_t)conf->last_endpoint : 0;
353 mgcp->actual.bts_base = conf->bts_base > 0 ? (uint16_t)conf->bts_base : 4000;
354
Neels Hofmeyr2188a772016-05-20 21:59:55 +0200355 return mgcp;
356}
357
358int mgcpgw_client_connect(struct mgcpgw_client *mgcp)
359{
360 int on;
361 struct sockaddr_in addr;
362 struct osmo_wqueue *wq;
363 int rc;
364
365 if (!mgcp) {
366 LOGP(DMGCP, LOGL_FATAL, "MGCPGW client not initialized properly\n");
367 return -EINVAL;
368 }
369
370 wq = &mgcp->wq;
371
372 wq->bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
373 if (wq->bfd.fd < 0) {
374 LOGP(DMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno);
375 return -errno;
376 }
377
378 on = 1;
379 if (setsockopt(wq->bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
380 LOGP(DMGCP, LOGL_FATAL,
381 "Failed to initialize socket for MGCP GW: %s\n",
382 strerror(errno));
383 rc = -errno;
384 goto error_close_fd;
385 }
386
387 /* bind socket */
388 memset(&addr, 0, sizeof(addr));
389 addr.sin_family = AF_INET;
390 inet_aton(mgcp->actual.local_addr, &addr.sin_addr);
391 addr.sin_port = htons(mgcp->actual.local_port);
392 if (bind(wq->bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
393 LOGP(DMGCP, LOGL_FATAL,
394 "Failed to bind for MGCP GW to %s %u\n",
395 mgcp->actual.local_addr, mgcp->actual.local_port);
396 rc = -errno;
397 goto error_close_fd;
398 }
399
400 /* connect to the remote */
401 inet_aton(mgcp->actual.remote_addr, &addr.sin_addr);
402 addr.sin_port = htons(mgcp->actual.remote_port);
403 if (connect(wq->bfd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
404 LOGP(DMGCP, LOGL_FATAL,
405 "Failed to connect to MGCP GW at %s %u: %s\n",
406 mgcp->actual.remote_addr, mgcp->actual.remote_port,
407 strerror(errno));
408 rc = -errno;
409 goto error_close_fd;
410 }
411
412 mgcp->remote_addr = htonl(addr.sin_addr.s_addr);
413
414 osmo_wqueue_init(wq, 10);
415 wq->bfd.when = BSC_FD_READ;
416 wq->bfd.data = mgcp;
417 wq->read_cb = mgcp_do_read;
418 wq->write_cb = mgcp_do_write;
419
420 if (osmo_fd_register(&wq->bfd) != 0) {
421 LOGP(DMGCP, LOGL_FATAL, "Failed to register BFD\n");
422 rc = -EIO;
423 goto error_close_fd;
424 }
425 LOGP(DMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n",
426 mgcp->actual.local_addr, mgcp->actual.local_port,
427 mgcp->actual.remote_addr, mgcp->actual.remote_port);
428
429 return 0;
430error_close_fd:
431 close(wq->bfd.fd);
432 wq->bfd.fd = -1;
433 return rc;
434}
435
436const char *mgcpgw_client_remote_addr_str(struct mgcpgw_client *mgcp)
437{
438 return mgcp->actual.remote_addr;
439}
440
441uint16_t mgcpgw_client_remote_port(struct mgcpgw_client *mgcp)
442{
443 return mgcp->actual.remote_port;
444}
445
446/* Return the MGCP GW binary IPv4 address in network byte order. */
447uint32_t mgcpgw_client_remote_addr_n(struct mgcpgw_client *mgcp)
448{
449 return mgcp->remote_addr;
450}
451
452struct mgcp_response_pending * mgcpgw_client_pending_add(
453 struct mgcpgw_client *mgcp,
454 mgcp_trans_id_t trans_id,
455 mgcp_response_cb_t response_cb,
456 void *priv)
457{
458 struct mgcp_response_pending *pending;
459
460 pending = talloc_zero(mgcp, struct mgcp_response_pending);
461 pending->trans_id = trans_id;
462 pending->response_cb = response_cb;
463 pending->priv = priv;
464 llist_add_tail(&pending->entry, &mgcp->responses_pending);
465
466 return pending;
467}
468
469/* Send the MGCP message in msg to the MGCP GW and handle a response with
470 * response_cb. NOTE: the response_cb still needs to call
471 * mgcp_response_parse_params(response) to get the parsed parameters -- to
472 * potentially save some CPU cycles, only the head line has been parsed when
473 * the response_cb is invoked. */
474int mgcpgw_client_tx(struct mgcpgw_client *mgcp, struct msgb *msg,
475 mgcp_response_cb_t response_cb, void *priv)
476{
477 struct mgcp_response_pending *pending;
478 mgcp_trans_id_t trans_id;
479 int rc;
480
481 trans_id = msg->cb[MSGB_CB_MGCP_TRANS_ID];
482 if (!trans_id) {
483 LOGP(DMGCP, LOGL_ERROR,
484 "Unset transaction id in mgcp send request\n");
485 talloc_free(msg);
486 return -EINVAL;
487 }
488
489 pending = mgcpgw_client_pending_add(mgcp, trans_id, response_cb, priv);
490
491 if (msgb_l2len(msg) > 4096) {
492 LOGP(DMGCP, LOGL_ERROR,
493 "Cannot send, MGCP message too large: %u\n",
494 msgb_l2len(msg));
495 msgb_free(msg);
496 rc = -EINVAL;
497 goto mgcp_tx_error;
498 }
499
500 rc = osmo_wqueue_enqueue(&mgcp->wq, msg);
501 if (rc) {
502 LOGP(DMGCP, LOGL_FATAL, "Could not queue message to MGCP GW\n");
503 msgb_free(msg);
504 goto mgcp_tx_error;
505 } else
506 LOGP(DMGCP, LOGL_INFO, "Queued %u bytes for MGCP GW\n",
507 msgb_l2len(msg));
508 return 0;
509
510mgcp_tx_error:
511 /* Pass NULL to response cb to indicate an error */
512 mgcpgw_client_handle_response(mgcp, pending, NULL);
513 return -1;
514}
515
516static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id,
517 const char *buf, int len)
518{
519 struct msgb *msg;
520
521 if (len > (4096 - 128)) {
522 LOGP(DMGCP, LOGL_ERROR, "Cannot send to MGCP GW:"
523 " message too large: %d\n", len);
524 return NULL;
525 }
526
527 msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
528 OSMO_ASSERT(msg);
529
530 char *dst = (char*)msgb_put(msg, len);
531 memcpy(dst, buf, len);
532 msg->l2h = msg->data;
533 msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
534
535 return msg;
536}
537
538static struct msgb *mgcp_msg_from_str(mgcp_trans_id_t trans_id,
539 const char *fmt, ...)
540{
541 static char compose[4096 - 128];
542 va_list ap;
543 int len;
544 OSMO_ASSERT(fmt);
545
546 va_start(ap, fmt);
547 len = vsnprintf(compose, sizeof(compose), fmt, ap);
548 va_end(ap);
549 if (len >= sizeof(compose)) {
550 LOGP(DMGCP, LOGL_ERROR,
551 "Message too large: trans_id=%u len=%d\n",
552 trans_id, len);
553 return NULL;
554 }
555 if (len < 1) {
556 LOGP(DMGCP, LOGL_ERROR,
557 "Failed to compose message: trans_id=%u len=%d\n",
558 trans_id, len);
559 return NULL;
560 }
561 return mgcp_msg_from_buf(trans_id, compose, len);
562}
563
564static mgcp_trans_id_t mgcpgw_client_next_trans_id(struct mgcpgw_client *mgcp)
565{
566 /* avoid zero trans_id to distinguish from unset trans_id */
567 if (!mgcp->next_trans_id)
568 mgcp->next_trans_id ++;
569 return mgcp->next_trans_id ++;
570}
571
572struct msgb *mgcp_msg_crcx(struct mgcpgw_client *mgcp,
573 uint16_t rtp_endpoint, unsigned int call_id,
574 enum mgcp_connection_mode mode)
575{
576 mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp);
577 return mgcp_msg_from_str(trans_id,
578 "CRCX %u %x@mgw MGCP 1.0\r\n"
579 "C: %x\r\n"
580 "L: p:20, a:AMR, nt:IN\r\n"
581 "M: %s\r\n"
582 ,
583 trans_id,
584 rtp_endpoint,
585 call_id,
586 mgcp_cmode_name(mode));
587}
588
589struct msgb *mgcp_msg_mdcx(struct mgcpgw_client *mgcp,
590 uint16_t rtp_endpoint, const char *rtp_conn_addr,
591 uint16_t rtp_port, enum mgcp_connection_mode mode)
592
593{
594 mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp);
595 return mgcp_msg_from_str(trans_id,
596 "MDCX %u %x@mgw MGCP 1.0\r\n"
597 "M: %s\r\n"
598 "\r\n"
599 "c=IN IP4 %s\r\n"
600 "m=audio %u RTP/AVP 255\r\n"
601 ,
602 trans_id,
603 rtp_endpoint,
604 mgcp_cmode_name(mode),
605 rtp_conn_addr,
606 rtp_port);
607}
Philipp Maierefe85d32017-04-09 12:32:51 +0200608
609struct msgb *mgcp_msg_dlcx(struct mgcpgw_client *mgcp, uint16_t rtp_endpoint,
610 unsigned int call_id)
611{
612 mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp);
613 return mgcp_msg_from_str(trans_id,
614 "DLCX %u %x@mgw MGCP 1.0\r\n"
615 "C: %x\r\n", trans_id, rtp_endpoint, call_id);
616}