blob: 9f0c84de244a732d1d18e9bca966997f4cefb750 [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
38struct mgcpgw_client {
39 struct mgcpgw_client_conf actual;
40 uint32_t remote_addr;
41 struct osmo_wqueue wq;
42 mgcp_trans_id_t next_trans_id;
43 uint16_t next_endpoint;
44 struct llist_head responses_pending;
45};
46
47void mgcpgw_client_conf_init(struct mgcpgw_client_conf *conf)
48{
49 /* NULL and -1 default to MGCPGW_CLIENT_*_DEFAULT values */
50 *conf = (struct mgcpgw_client_conf){
51 .local_addr = NULL,
52 .local_port = -1,
53 .remote_addr = NULL,
54 .remote_port = -1,
55 };
56}
57
58unsigned int mgcpgw_client_next_endpoint(struct mgcpgw_client *client)
59{
60 return client->next_endpoint ++;
61}
62
63static void mgcpgw_client_handle_response(struct mgcpgw_client *mgcp,
64 struct mgcp_response_pending *pending,
65 struct mgcp_response *response)
66{
67 if (!pending) {
68 LOGP(DMGCP, LOGL_ERROR,
69 "Cannot handle NULL response\n");
70 return;
71 }
72 if (pending->response_cb)
73 pending->response_cb(response, pending->priv);
74 else
75 LOGP(DMGCP, LOGL_INFO, "MGCP response ignored (NULL cb)\n");
76 talloc_free(pending);
77}
78
79static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
80{
81 int comment_pos;
82 char *end;
83
84 if (mgcp_msg_terminate_nul(msg))
85 goto response_parse_failure;
86
87 r->body = (char *)msg->data;
88
89 if (sscanf(r->body, "%3d %u %n",
90 &r->head.response_code, &r->head.trans_id,
91 &comment_pos) != 2)
92 goto response_parse_failure;
93
94 r->head.comment = r->body + comment_pos;
95 end = strchr(r->head.comment, '\r');
96 if (!end)
97 goto response_parse_failure;
98 /* Mark the end of the comment */
99 *end = '\0';
100 r->body = end + 1;
101 if (r->body[0] == '\n')
102 r->body ++;
103 return 0;
104
105response_parse_failure:
106 LOGP(DMGCP, LOGL_ERROR,
107 "Failed to parse MGCP response header\n");
108 return -EINVAL;
109}
110
111/* TODO undup against mgcp_protocol.c:mgcp_check_param() */
112static bool mgcp_line_is_valid(const char *line)
113{
114 const size_t line_len = strlen(line);
115 if (line[0] == '\0')
116 return true;
117
118 if (line_len < 2
119 || line[1] != '=') {
120 LOGP(DMGCP, LOGL_ERROR,
121 "Wrong MGCP option format: '%s'\n",
122 line);
123 return false;
124 }
125
126 return true;
127}
128
129/* Parse a line like "m=audio 16002 RTP/AVP 98" */
130static int mgcp_parse_audio(struct mgcp_response *r, const char *line)
131{
132 if (sscanf(line, "m=audio %hu",
133 &r->audio_port) != 1)
134 goto response_parse_failure;
135
136 return 0;
137
138response_parse_failure:
139 LOGP(DMGCP, LOGL_ERROR,
140 "Failed to parse MGCP response header\n");
141 return -EINVAL;
142}
143
144int mgcp_response_parse_params(struct mgcp_response *r)
145{
146 char *line;
147 int rc;
148 OSMO_ASSERT(r->body);
149 char *data = strstr(r->body, "\n\n");
150
151 if (!data) {
152 LOGP(DMGCP, LOGL_ERROR,
153 "MGCP response: cannot find start of parameters\n");
154 return -EINVAL;
155 }
156
157 /* Advance to after the \n\n, replace the second \n with \0. That's
158 * where the parameters start. */
159 data ++;
160 *data = '\0';
161 data ++;
162
163 for_each_line(line, data) {
164 if (!mgcp_line_is_valid(line))
165 return -EINVAL;
166
167 switch (line[0]) {
168 case 'm':
169 rc = mgcp_parse_audio(r, line);
170 if (rc)
171 return rc;
172 break;
173 default:
174 /* skip unhandled parameters */
175 break;
176 }
177 }
178 return 0;
179}
180
181static struct mgcp_response_pending *mgcpgw_client_response_pending_get(
182 struct mgcpgw_client *mgcp,
183 struct mgcp_response *r)
184{
185 struct mgcp_response_pending *pending;
186 if (!r)
187 return NULL;
188 llist_for_each_entry(pending, &mgcp->responses_pending, entry) {
189 if (pending->trans_id == r->head.trans_id) {
190 llist_del(&pending->entry);
191 return pending;
192 }
193 }
194 return NULL;
195}
196
197/* Feed an MGCP message into the receive processing.
198 * Parse the head and call any callback registered for the transaction id found
199 * in the MGCP message. This is normally called directly from the internal
200 * mgcp_do_read that reads from the socket connected to the MGCP gateway. This
201 * function is published mainly to be able to feed data from the test suite.
202 */
203int mgcpgw_client_rx(struct mgcpgw_client *mgcp, struct msgb *msg)
204{
205 struct mgcp_response r = { 0 };
206 struct mgcp_response_pending *pending;
207 int rc;
208
209 rc = mgcp_response_parse_head(&r, msg);
210 if (rc) {
211 LOGP(DMGCP, LOGL_ERROR, "Cannot parse MGCP response\n");
212 return -1;
213 }
214
215 pending = mgcpgw_client_response_pending_get(mgcp, &r);
216 if (!pending) {
217 LOGP(DMGCP, LOGL_ERROR,
218 "Cannot find matching MGCP transaction for trans_id %d\n",
219 r.head.trans_id);
220 return -1;
221 }
222
223 mgcpgw_client_handle_response(mgcp, pending, &r);
224 return 0;
225}
226
227static int mgcp_do_read(struct osmo_fd *fd)
228{
229 struct mgcpgw_client *mgcp = fd->data;
230 struct msgb *msg;
231 int ret;
232
233 msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
234 if (!msg) {
235 LOGP(DMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n");
236 return -1;
237 }
238
239 ret = read(fd->fd, msg->data, 4096 - 128);
240 if (ret <= 0) {
241 LOGP(DMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno));
242 msgb_free(msg);
243 return -1;
244 } else if (ret > 4096 - 128) {
245 LOGP(DMGCP, LOGL_ERROR, "Too much data: %d\n", ret);
246 msgb_free(msg);
247 return -1;
248 }
249
250 msg->l2h = msgb_put(msg, ret);
251 ret = mgcpgw_client_rx(mgcp, msg);
252 talloc_free(msg);
253 return ret;
254}
255
256static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
257{
258 int ret;
259 static char strbuf[4096];
260 unsigned int l = msg->len < sizeof(strbuf)-1 ? msg->len : sizeof(strbuf)-1;
261 strncpy(strbuf, (const char*)msg->data, l);
262 strbuf[l] = '\0';
263 DEBUGP(DMGCP, "Tx MGCP msg to MGCP GW: '%s'\n", strbuf);
264
265 LOGP(DMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len);
266
267 ret = write(fd->fd, msg->data, msg->len);
268 if (ret != msg->len)
269 LOGP(DMGCP, LOGL_ERROR, "Failed to forward message to MGCP"
270 " GW: %s\n", strerror(errno));
271
272 return ret;
273}
274
275struct mgcpgw_client *mgcpgw_client_init(void *ctx,
276 struct mgcpgw_client_conf *conf)
277{
278 struct mgcpgw_client *mgcp;
279
280 mgcp = talloc_zero(ctx, struct mgcpgw_client);
281
282 INIT_LLIST_HEAD(&mgcp->responses_pending);
283
284 mgcp->next_trans_id = 1;
285 mgcp->next_endpoint = 1;
286
287 mgcp->actual.local_addr = conf->local_addr ? conf->local_addr :
288 MGCPGW_CLIENT_LOCAL_ADDR_DEFAULT;
289 mgcp->actual.local_port = conf->local_port >= 0 ? (uint16_t)conf->local_port :
290 MGCPGW_CLIENT_LOCAL_PORT_DEFAULT;
291
292 mgcp->actual.remote_addr = conf->remote_addr ? conf->remote_addr :
293 MGCPGW_CLIENT_REMOTE_ADDR_DEFAULT;
294 mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
295 MGCPGW_CLIENT_REMOTE_PORT_DEFAULT;
296
297 return mgcp;
298}
299
300int mgcpgw_client_connect(struct mgcpgw_client *mgcp)
301{
302 int on;
303 struct sockaddr_in addr;
304 struct osmo_wqueue *wq;
305 int rc;
306
307 if (!mgcp) {
308 LOGP(DMGCP, LOGL_FATAL, "MGCPGW client not initialized properly\n");
309 return -EINVAL;
310 }
311
312 wq = &mgcp->wq;
313
314 wq->bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
315 if (wq->bfd.fd < 0) {
316 LOGP(DMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno);
317 return -errno;
318 }
319
320 on = 1;
321 if (setsockopt(wq->bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
322 LOGP(DMGCP, LOGL_FATAL,
323 "Failed to initialize socket for MGCP GW: %s\n",
324 strerror(errno));
325 rc = -errno;
326 goto error_close_fd;
327 }
328
329 /* bind socket */
330 memset(&addr, 0, sizeof(addr));
331 addr.sin_family = AF_INET;
332 inet_aton(mgcp->actual.local_addr, &addr.sin_addr);
333 addr.sin_port = htons(mgcp->actual.local_port);
334 if (bind(wq->bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
335 LOGP(DMGCP, LOGL_FATAL,
336 "Failed to bind for MGCP GW to %s %u\n",
337 mgcp->actual.local_addr, mgcp->actual.local_port);
338 rc = -errno;
339 goto error_close_fd;
340 }
341
342 /* connect to the remote */
343 inet_aton(mgcp->actual.remote_addr, &addr.sin_addr);
344 addr.sin_port = htons(mgcp->actual.remote_port);
345 if (connect(wq->bfd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
346 LOGP(DMGCP, LOGL_FATAL,
347 "Failed to connect to MGCP GW at %s %u: %s\n",
348 mgcp->actual.remote_addr, mgcp->actual.remote_port,
349 strerror(errno));
350 rc = -errno;
351 goto error_close_fd;
352 }
353
354 mgcp->remote_addr = htonl(addr.sin_addr.s_addr);
355
356 osmo_wqueue_init(wq, 10);
357 wq->bfd.when = BSC_FD_READ;
358 wq->bfd.data = mgcp;
359 wq->read_cb = mgcp_do_read;
360 wq->write_cb = mgcp_do_write;
361
362 if (osmo_fd_register(&wq->bfd) != 0) {
363 LOGP(DMGCP, LOGL_FATAL, "Failed to register BFD\n");
364 rc = -EIO;
365 goto error_close_fd;
366 }
367 LOGP(DMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n",
368 mgcp->actual.local_addr, mgcp->actual.local_port,
369 mgcp->actual.remote_addr, mgcp->actual.remote_port);
370
371 return 0;
372error_close_fd:
373 close(wq->bfd.fd);
374 wq->bfd.fd = -1;
375 return rc;
376}
377
378const char *mgcpgw_client_remote_addr_str(struct mgcpgw_client *mgcp)
379{
380 return mgcp->actual.remote_addr;
381}
382
383uint16_t mgcpgw_client_remote_port(struct mgcpgw_client *mgcp)
384{
385 return mgcp->actual.remote_port;
386}
387
388/* Return the MGCP GW binary IPv4 address in network byte order. */
389uint32_t mgcpgw_client_remote_addr_n(struct mgcpgw_client *mgcp)
390{
391 return mgcp->remote_addr;
392}
393
394struct mgcp_response_pending * mgcpgw_client_pending_add(
395 struct mgcpgw_client *mgcp,
396 mgcp_trans_id_t trans_id,
397 mgcp_response_cb_t response_cb,
398 void *priv)
399{
400 struct mgcp_response_pending *pending;
401
402 pending = talloc_zero(mgcp, struct mgcp_response_pending);
403 pending->trans_id = trans_id;
404 pending->response_cb = response_cb;
405 pending->priv = priv;
406 llist_add_tail(&pending->entry, &mgcp->responses_pending);
407
408 return pending;
409}
410
411/* Send the MGCP message in msg to the MGCP GW and handle a response with
412 * response_cb. NOTE: the response_cb still needs to call
413 * mgcp_response_parse_params(response) to get the parsed parameters -- to
414 * potentially save some CPU cycles, only the head line has been parsed when
415 * the response_cb is invoked. */
416int mgcpgw_client_tx(struct mgcpgw_client *mgcp, struct msgb *msg,
417 mgcp_response_cb_t response_cb, void *priv)
418{
419 struct mgcp_response_pending *pending;
420 mgcp_trans_id_t trans_id;
421 int rc;
422
423 trans_id = msg->cb[MSGB_CB_MGCP_TRANS_ID];
424 if (!trans_id) {
425 LOGP(DMGCP, LOGL_ERROR,
426 "Unset transaction id in mgcp send request\n");
427 talloc_free(msg);
428 return -EINVAL;
429 }
430
431 pending = mgcpgw_client_pending_add(mgcp, trans_id, response_cb, priv);
432
433 if (msgb_l2len(msg) > 4096) {
434 LOGP(DMGCP, LOGL_ERROR,
435 "Cannot send, MGCP message too large: %u\n",
436 msgb_l2len(msg));
437 msgb_free(msg);
438 rc = -EINVAL;
439 goto mgcp_tx_error;
440 }
441
442 rc = osmo_wqueue_enqueue(&mgcp->wq, msg);
443 if (rc) {
444 LOGP(DMGCP, LOGL_FATAL, "Could not queue message to MGCP GW\n");
445 msgb_free(msg);
446 goto mgcp_tx_error;
447 } else
448 LOGP(DMGCP, LOGL_INFO, "Queued %u bytes for MGCP GW\n",
449 msgb_l2len(msg));
450 return 0;
451
452mgcp_tx_error:
453 /* Pass NULL to response cb to indicate an error */
454 mgcpgw_client_handle_response(mgcp, pending, NULL);
455 return -1;
456}
457
458static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id,
459 const char *buf, int len)
460{
461 struct msgb *msg;
462
463 if (len > (4096 - 128)) {
464 LOGP(DMGCP, LOGL_ERROR, "Cannot send to MGCP GW:"
465 " message too large: %d\n", len);
466 return NULL;
467 }
468
469 msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
470 OSMO_ASSERT(msg);
471
472 char *dst = (char*)msgb_put(msg, len);
473 memcpy(dst, buf, len);
474 msg->l2h = msg->data;
475 msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
476
477 return msg;
478}
479
480static struct msgb *mgcp_msg_from_str(mgcp_trans_id_t trans_id,
481 const char *fmt, ...)
482{
483 static char compose[4096 - 128];
484 va_list ap;
485 int len;
486 OSMO_ASSERT(fmt);
487
488 va_start(ap, fmt);
489 len = vsnprintf(compose, sizeof(compose), fmt, ap);
490 va_end(ap);
491 if (len >= sizeof(compose)) {
492 LOGP(DMGCP, LOGL_ERROR,
493 "Message too large: trans_id=%u len=%d\n",
494 trans_id, len);
495 return NULL;
496 }
497 if (len < 1) {
498 LOGP(DMGCP, LOGL_ERROR,
499 "Failed to compose message: trans_id=%u len=%d\n",
500 trans_id, len);
501 return NULL;
502 }
503 return mgcp_msg_from_buf(trans_id, compose, len);
504}
505
506static mgcp_trans_id_t mgcpgw_client_next_trans_id(struct mgcpgw_client *mgcp)
507{
508 /* avoid zero trans_id to distinguish from unset trans_id */
509 if (!mgcp->next_trans_id)
510 mgcp->next_trans_id ++;
511 return mgcp->next_trans_id ++;
512}
513
514struct msgb *mgcp_msg_crcx(struct mgcpgw_client *mgcp,
515 uint16_t rtp_endpoint, unsigned int call_id,
516 enum mgcp_connection_mode mode)
517{
518 mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp);
519 return mgcp_msg_from_str(trans_id,
520 "CRCX %u %x@mgw MGCP 1.0\r\n"
521 "C: %x\r\n"
522 "L: p:20, a:AMR, nt:IN\r\n"
523 "M: %s\r\n"
524 ,
525 trans_id,
526 rtp_endpoint,
527 call_id,
528 mgcp_cmode_name(mode));
529}
530
531struct msgb *mgcp_msg_mdcx(struct mgcpgw_client *mgcp,
532 uint16_t rtp_endpoint, const char *rtp_conn_addr,
533 uint16_t rtp_port, enum mgcp_connection_mode mode)
534
535{
536 mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp);
537 return mgcp_msg_from_str(trans_id,
538 "MDCX %u %x@mgw MGCP 1.0\r\n"
539 "M: %s\r\n"
540 "\r\n"
541 "c=IN IP4 %s\r\n"
542 "m=audio %u RTP/AVP 255\r\n"
543 ,
544 trans_id,
545 rtp_endpoint,
546 mgcp_cmode_name(mode),
547 rtp_conn_addr,
548 rtp_port);
549}