blob: 1cd37be9d3183711600089499f33624195d98d17 [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>
26
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +020027#include <osmocom/mgcp_client/mgcp_client.h>
28#include <osmocom/mgcp_client/mgcp_client_internal.h>
Neels Hofmeyre9920f22017-07-10 15:07:22 +020029
30#include <netinet/in.h>
31#include <arpa/inet.h>
32
33#include <errno.h>
34#include <unistd.h>
35#include <string.h>
36
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +020037void mgcp_client_conf_init(struct mgcp_client_conf *conf)
Neels Hofmeyre9920f22017-07-10 15:07:22 +020038{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +020039 /* NULL and -1 default to MGCP_CLIENT_*_DEFAULT values */
40 *conf = (struct mgcp_client_conf){
Neels Hofmeyre9920f22017-07-10 15:07:22 +020041 .local_addr = NULL,
42 .local_port = -1,
43 .remote_addr = NULL,
44 .remote_port = -1,
45 .first_endpoint = 0,
46 .last_endpoint = 0,
47 .bts_base = 0,
48 };
49}
50
51/* Test if a given endpoint id is currently in use */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +020052static bool endpoint_in_use(uint16_t id, struct mgcp_client *client)
Neels Hofmeyre9920f22017-07-10 15:07:22 +020053{
54 struct mgcp_inuse_endpoint *endpoint;
55 llist_for_each_entry(endpoint, &client->inuse_endpoints, entry) {
56 if (endpoint->id == id)
57 return true;
58 }
59
60 return false;
61}
62
63/* Find and seize an unsused endpoint id */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +020064int mgcp_client_next_endpoint(struct mgcp_client *client)
Neels Hofmeyre9920f22017-07-10 15:07:22 +020065{
66 int i;
67 uint16_t first_endpoint = client->actual.first_endpoint;
68 uint16_t last_endpoint = client->actual.last_endpoint;
69 struct mgcp_inuse_endpoint *endpoint;
70
71 /* Use the maximum permitted range if the VTY
72 * configuration does not specify a range */
73 if (client->actual.last_endpoint == 0) {
74 first_endpoint = 1;
75 last_endpoint = 65534;
76 }
77
78 /* Test the permitted endpoint range for an endpoint
79 * number that is not in use. When a suitable endpoint
80 * number can be found, seize it by adding it to the
81 * inuse list. */
82 for (i=first_endpoint;i<last_endpoint;i++)
83 {
84 if (endpoint_in_use(i,client) == false) {
85 endpoint = talloc_zero(client, struct mgcp_inuse_endpoint);
86 endpoint->id = i;
87 llist_add_tail(&endpoint->entry, &client->inuse_endpoints);
88 return endpoint->id;
89 }
90 }
91
92 /* All endpoints are busy! */
93 return -EINVAL;
94}
95
96/* Release a seized endpoint id to make it available again for other calls */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +020097void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client)
Neels Hofmeyre9920f22017-07-10 15:07:22 +020098{
99 struct mgcp_inuse_endpoint *endpoint;
100 struct mgcp_inuse_endpoint *endpoint_tmp;
101 llist_for_each_entry_safe(endpoint, endpoint_tmp, &client->inuse_endpoints, entry) {
102 if (endpoint->id == id) {
103 llist_del(&endpoint->entry);
104 talloc_free(endpoint);
105 }
106 }
107}
108
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200109static void mgcp_client_handle_response(struct mgcp_client *mgcp,
110 struct mgcp_response_pending *pending,
111 struct mgcp_response *response)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200112{
113 if (!pending) {
114 LOGP(DLMGCP, LOGL_ERROR,
115 "Cannot handle NULL response\n");
116 return;
117 }
118 if (pending->response_cb)
119 pending->response_cb(response, pending->priv);
120 else
121 LOGP(DLMGCP, LOGL_INFO, "MGCP response ignored (NULL cb)\n");
122 talloc_free(pending);
123}
124
125static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg)
126{
127 int comment_pos;
128 char *end;
129
130 if (mgcp_msg_terminate_nul(msg))
131 goto response_parse_failure;
132
133 r->body = (char *)msg->data;
134
135 if (sscanf(r->body, "%3d %u %n",
136 &r->head.response_code, &r->head.trans_id,
137 &comment_pos) != 2)
138 goto response_parse_failure;
139
140 r->head.comment = r->body + comment_pos;
141 end = strchr(r->head.comment, '\r');
142 if (!end)
143 goto response_parse_failure;
144 /* Mark the end of the comment */
145 *end = '\0';
146 r->body = end + 1;
147 if (r->body[0] == '\n')
148 r->body ++;
149 return 0;
150
151response_parse_failure:
152 LOGP(DLMGCP, LOGL_ERROR,
153 "Failed to parse MGCP response header\n");
154 return -EINVAL;
155}
156
157/* TODO undup against mgcp_protocol.c:mgcp_check_param() */
158static bool mgcp_line_is_valid(const char *line)
159{
160 const size_t line_len = strlen(line);
161 if (line[0] == '\0')
162 return true;
163
164 if (line_len < 2
165 || line[1] != '=') {
166 LOGP(DLMGCP, LOGL_ERROR,
167 "Wrong MGCP option format: '%s'\n",
168 line);
169 return false;
170 }
171
172 return true;
173}
174
175/* Parse a line like "m=audio 16002 RTP/AVP 98" */
176static int mgcp_parse_audio(struct mgcp_response *r, const char *line)
177{
178 if (sscanf(line, "m=audio %hu",
179 &r->audio_port) != 1)
180 goto response_parse_failure;
181
182 return 0;
183
184response_parse_failure:
185 LOGP(DLMGCP, LOGL_ERROR,
186 "Failed to parse MGCP response header\n");
187 return -EINVAL;
188}
189
190int mgcp_response_parse_params(struct mgcp_response *r)
191{
192 char *line;
193 int rc;
194 OSMO_ASSERT(r->body);
195 char *data = strstr(r->body, "\n\n");
196
197 if (!data) {
198 LOGP(DLMGCP, LOGL_ERROR,
199 "MGCP response: cannot find start of parameters\n");
200 return -EINVAL;
201 }
202
203 /* Advance to after the \n\n, replace the second \n with \0. That's
204 * where the parameters start. */
205 data ++;
206 *data = '\0';
207 data ++;
208
Neels Hofmeyrd95ab1e2017-09-22 00:52:54 +0200209 for_each_non_empty_line(line, data) {
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200210 if (!mgcp_line_is_valid(line))
211 return -EINVAL;
212
213 switch (line[0]) {
214 case 'm':
215 rc = mgcp_parse_audio(r, line);
216 if (rc)
217 return rc;
218 break;
219 default:
220 /* skip unhandled parameters */
221 break;
222 }
223 }
224 return 0;
225}
226
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200227static struct mgcp_response_pending *mgcp_client_response_pending_get(
228 struct mgcp_client *mgcp,
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200229 struct mgcp_response *r)
230{
231 struct mgcp_response_pending *pending;
232 if (!r)
233 return NULL;
234 llist_for_each_entry(pending, &mgcp->responses_pending, entry) {
235 if (pending->trans_id == r->head.trans_id) {
236 llist_del(&pending->entry);
237 return pending;
238 }
239 }
240 return NULL;
241}
242
243/* Feed an MGCP message into the receive processing.
244 * Parse the head and call any callback registered for the transaction id found
245 * in the MGCP message. This is normally called directly from the internal
246 * mgcp_do_read that reads from the socket connected to the MGCP gateway. This
247 * function is published mainly to be able to feed data from the test suite.
248 */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200249int mgcp_client_rx(struct mgcp_client *mgcp, struct msgb *msg)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200250{
251 struct mgcp_response r = { 0 };
252 struct mgcp_response_pending *pending;
253 int rc;
254
255 rc = mgcp_response_parse_head(&r, msg);
256 if (rc) {
257 LOGP(DLMGCP, LOGL_ERROR, "Cannot parse MGCP response\n");
258 return -1;
259 }
260
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200261 pending = mgcp_client_response_pending_get(mgcp, &r);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200262 if (!pending) {
263 LOGP(DLMGCP, LOGL_ERROR,
264 "Cannot find matching MGCP transaction for trans_id %d\n",
265 r.head.trans_id);
266 return -1;
267 }
268
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200269 mgcp_client_handle_response(mgcp, pending, &r);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200270 return 0;
271}
272
273static int mgcp_do_read(struct osmo_fd *fd)
274{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200275 struct mgcp_client *mgcp = fd->data;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200276 struct msgb *msg;
277 int ret;
278
279 msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw");
280 if (!msg) {
281 LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n");
282 return -1;
283 }
284
285 ret = read(fd->fd, msg->data, 4096 - 128);
286 if (ret <= 0) {
287 LOGP(DLMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno));
288 msgb_free(msg);
289 return -1;
290 } else if (ret > 4096 - 128) {
291 LOGP(DLMGCP, LOGL_ERROR, "Too much data: %d\n", ret);
292 msgb_free(msg);
293 return -1;
294 }
295
296 msg->l2h = msgb_put(msg, ret);
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200297 ret = mgcp_client_rx(mgcp, msg);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200298 talloc_free(msg);
299 return ret;
300}
301
302static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
303{
304 int ret;
305 static char strbuf[4096];
306 unsigned int l = msg->len < sizeof(strbuf) ? msg->len : sizeof(strbuf);
307 unsigned int i;
308
309 strncpy(strbuf, (const char*)msg->data, l);
310 for (i = 0; i < sizeof(strbuf); i++) {
311 if (strbuf[i] == '\n' || strbuf[i] == '\r') {
312 strbuf[i] = '\0';
313 break;
314 }
315 }
316 DEBUGP(DLMGCP, "Tx MGCP msg to MGCP GW: '%s'\n", strbuf);
317
318 LOGP(DLMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len);
319
320 ret = write(fd->fd, msg->data, msg->len);
321 if (ret != msg->len)
322 LOGP(DLMGCP, LOGL_ERROR, "Failed to forward message to MGCP"
323 " GW: %s\n", strerror(errno));
324
325 return ret;
326}
327
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200328struct mgcp_client *mgcp_client_init(void *ctx,
329 struct mgcp_client_conf *conf)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200330{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200331 struct mgcp_client *mgcp;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200332
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200333 mgcp = talloc_zero(ctx, struct mgcp_client);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200334
335 INIT_LLIST_HEAD(&mgcp->responses_pending);
336 INIT_LLIST_HEAD(&mgcp->inuse_endpoints);
337
338 mgcp->next_trans_id = 1;
339
340 mgcp->actual.local_addr = conf->local_addr ? conf->local_addr :
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200341 MGCP_CLIENT_LOCAL_ADDR_DEFAULT;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200342 mgcp->actual.local_port = conf->local_port >= 0 ? (uint16_t)conf->local_port :
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200343 MGCP_CLIENT_LOCAL_PORT_DEFAULT;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200344
345 mgcp->actual.remote_addr = conf->remote_addr ? conf->remote_addr :
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200346 MGCP_CLIENT_REMOTE_ADDR_DEFAULT;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200347 mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port :
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200348 MGCP_CLIENT_REMOTE_PORT_DEFAULT;
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200349
350 mgcp->actual.first_endpoint = conf->first_endpoint > 0 ? (uint16_t)conf->first_endpoint : 0;
351 mgcp->actual.last_endpoint = conf->last_endpoint > 0 ? (uint16_t)conf->last_endpoint : 0;
352 mgcp->actual.bts_base = conf->bts_base > 0 ? (uint16_t)conf->bts_base : 4000;
353
354 return mgcp;
355}
356
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200357int mgcp_client_connect(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200358{
359 int on;
360 struct sockaddr_in addr;
361 struct osmo_wqueue *wq;
362 int rc;
363
364 if (!mgcp) {
365 LOGP(DLMGCP, LOGL_FATAL, "MGCPGW client not initialized properly\n");
366 return -EINVAL;
367 }
368
369 wq = &mgcp->wq;
370
371 wq->bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
372 if (wq->bfd.fd < 0) {
373 LOGP(DLMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno);
374 return -errno;
375 }
376
377 on = 1;
378 if (setsockopt(wq->bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
379 LOGP(DLMGCP, LOGL_FATAL,
380 "Failed to initialize socket for MGCP GW: %s\n",
381 strerror(errno));
382 rc = -errno;
383 goto error_close_fd;
384 }
385
386 /* bind socket */
387 memset(&addr, 0, sizeof(addr));
388 addr.sin_family = AF_INET;
389 inet_aton(mgcp->actual.local_addr, &addr.sin_addr);
390 addr.sin_port = htons(mgcp->actual.local_port);
391 if (bind(wq->bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
392 LOGP(DLMGCP, LOGL_FATAL,
393 "Failed to bind for MGCP GW to %s %u\n",
394 mgcp->actual.local_addr, mgcp->actual.local_port);
395 rc = -errno;
396 goto error_close_fd;
397 }
398
399 /* connect to the remote */
400 inet_aton(mgcp->actual.remote_addr, &addr.sin_addr);
401 addr.sin_port = htons(mgcp->actual.remote_port);
402 if (connect(wq->bfd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
403 LOGP(DLMGCP, LOGL_FATAL,
404 "Failed to connect to MGCP GW at %s %u: %s\n",
405 mgcp->actual.remote_addr, mgcp->actual.remote_port,
406 strerror(errno));
407 rc = -errno;
408 goto error_close_fd;
409 }
410
411 mgcp->remote_addr = htonl(addr.sin_addr.s_addr);
412
413 osmo_wqueue_init(wq, 10);
414 wq->bfd.when = BSC_FD_READ;
415 wq->bfd.data = mgcp;
416 wq->read_cb = mgcp_do_read;
417 wq->write_cb = mgcp_do_write;
418
419 if (osmo_fd_register(&wq->bfd) != 0) {
420 LOGP(DLMGCP, LOGL_FATAL, "Failed to register BFD\n");
421 rc = -EIO;
422 goto error_close_fd;
423 }
424 LOGP(DLMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n",
425 mgcp->actual.local_addr, mgcp->actual.local_port,
426 mgcp->actual.remote_addr, mgcp->actual.remote_port);
427
428 return 0;
429error_close_fd:
430 close(wq->bfd.fd);
431 wq->bfd.fd = -1;
432 return rc;
433}
434
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200435const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200436{
437 return mgcp->actual.remote_addr;
438}
439
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200440uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200441{
442 return mgcp->actual.remote_port;
443}
444
445/* Return the MGCP GW binary IPv4 address in network byte order. */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200446uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200447{
448 return mgcp->remote_addr;
449}
450
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200451struct mgcp_response_pending * mgcp_client_pending_add(
452 struct mgcp_client *mgcp,
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200453 mgcp_trans_id_t trans_id,
454 mgcp_response_cb_t response_cb,
455 void *priv)
456{
457 struct mgcp_response_pending *pending;
458
459 pending = talloc_zero(mgcp, struct mgcp_response_pending);
460 pending->trans_id = trans_id;
461 pending->response_cb = response_cb;
462 pending->priv = priv;
463 llist_add_tail(&pending->entry, &mgcp->responses_pending);
464
465 return pending;
466}
467
468/* Send the MGCP message in msg to the MGCP GW and handle a response with
469 * response_cb. NOTE: the response_cb still needs to call
470 * mgcp_response_parse_params(response) to get the parsed parameters -- to
471 * potentially save some CPU cycles, only the head line has been parsed when
472 * the response_cb is invoked. */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200473int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
474 mgcp_response_cb_t response_cb, void *priv)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200475{
476 struct mgcp_response_pending *pending;
477 mgcp_trans_id_t trans_id;
478 int rc;
479
480 trans_id = msg->cb[MSGB_CB_MGCP_TRANS_ID];
481 if (!trans_id) {
482 LOGP(DLMGCP, LOGL_ERROR,
483 "Unset transaction id in mgcp send request\n");
484 talloc_free(msg);
485 return -EINVAL;
486 }
487
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200488 pending = mgcp_client_pending_add(mgcp, trans_id, response_cb, priv);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200489
490 if (msgb_l2len(msg) > 4096) {
491 LOGP(DLMGCP, LOGL_ERROR,
492 "Cannot send, MGCP message too large: %u\n",
493 msgb_l2len(msg));
494 msgb_free(msg);
495 rc = -EINVAL;
496 goto mgcp_tx_error;
497 }
498
499 rc = osmo_wqueue_enqueue(&mgcp->wq, msg);
500 if (rc) {
501 LOGP(DLMGCP, LOGL_FATAL, "Could not queue message to MGCP GW\n");
502 msgb_free(msg);
503 goto mgcp_tx_error;
504 } else
505 LOGP(DLMGCP, LOGL_INFO, "Queued %u bytes for MGCP GW\n",
506 msgb_l2len(msg));
507 return 0;
508
509mgcp_tx_error:
510 /* Pass NULL to response cb to indicate an error */
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200511 mgcp_client_handle_response(mgcp, pending, NULL);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200512 return -1;
513}
514
515static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id,
516 const char *buf, int len)
517{
518 struct msgb *msg;
519
520 if (len > (4096 - 128)) {
521 LOGP(DLMGCP, LOGL_ERROR, "Cannot send to MGCP GW:"
522 " message too large: %d\n", len);
523 return NULL;
524 }
525
526 msg = msgb_alloc_headroom(4096, 128, "MGCP tx");
527 OSMO_ASSERT(msg);
528
529 char *dst = (char*)msgb_put(msg, len);
530 memcpy(dst, buf, len);
531 msg->l2h = msg->data;
532 msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id;
533
534 return msg;
535}
536
537static struct msgb *mgcp_msg_from_str(mgcp_trans_id_t trans_id,
538 const char *fmt, ...)
539{
540 static char compose[4096 - 128];
541 va_list ap;
542 int len;
543 OSMO_ASSERT(fmt);
544
545 va_start(ap, fmt);
546 len = vsnprintf(compose, sizeof(compose), fmt, ap);
547 va_end(ap);
548 if (len >= sizeof(compose)) {
549 LOGP(DLMGCP, LOGL_ERROR,
550 "Message too large: trans_id=%u len=%d\n",
551 trans_id, len);
552 return NULL;
553 }
554 if (len < 1) {
555 LOGP(DLMGCP, LOGL_ERROR,
556 "Failed to compose message: trans_id=%u len=%d\n",
557 trans_id, len);
558 return NULL;
559 }
560 return mgcp_msg_from_buf(trans_id, compose, len);
561}
562
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200563static mgcp_trans_id_t mgcp_client_next_trans_id(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200564{
565 /* avoid zero trans_id to distinguish from unset trans_id */
566 if (!mgcp->next_trans_id)
567 mgcp->next_trans_id ++;
568 return mgcp->next_trans_id ++;
569}
570
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200571struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200572 uint16_t rtp_endpoint, unsigned int call_id,
573 enum mgcp_connection_mode mode)
574{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200575 mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200576 return mgcp_msg_from_str(trans_id,
577 "CRCX %u %x@mgw MGCP 1.0\r\n"
578 "C: %x\r\n"
579 "L: p:20, a:AMR, nt:IN\r\n"
580 "M: %s\r\n"
581 ,
582 trans_id,
583 rtp_endpoint,
584 call_id,
Neels Hofmeyrd95ab1e2017-09-22 00:52:54 +0200585 mgcp_client_cmode_name(mode));
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200586}
587
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200588struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200589 uint16_t rtp_endpoint, const char *rtp_conn_addr,
590 uint16_t rtp_port, enum mgcp_connection_mode mode)
591
592{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200593 mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200594 return mgcp_msg_from_str(trans_id,
595 "MDCX %u %x@mgw MGCP 1.0\r\n"
596 "M: %s\r\n"
597 "\r\n"
598 "c=IN IP4 %s\r\n"
599 "m=audio %u RTP/AVP 255\r\n"
600 ,
601 trans_id,
602 rtp_endpoint,
Neels Hofmeyrd95ab1e2017-09-22 00:52:54 +0200603 mgcp_client_cmode_name(mode),
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200604 rtp_conn_addr,
605 rtp_port);
606}
607
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200608struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint,
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200609 unsigned int call_id)
610{
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200611 mgcp_trans_id_t trans_id = mgcp_client_next_trans_id(mgcp);
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200612 return mgcp_msg_from_str(trans_id,
613 "DLCX %u %x@mgw MGCP 1.0\r\n"
614 "C: %x\r\n", trans_id, rtp_endpoint, call_id);
615}
616
Neels Hofmeyr3a8e7232017-09-04 01:02:56 +0200617struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp)
Neels Hofmeyre9920f22017-07-10 15:07:22 +0200618{
619 return &mgcp->actual;
620}
Neels Hofmeyrd95ab1e2017-09-22 00:52:54 +0200621
622const struct value_string mgcp_client_connection_mode_strs[] = {
623 { MGCP_CONN_NONE, "none" },
624 { MGCP_CONN_RECV_SEND, "sendrecv" },
625 { MGCP_CONN_SEND_ONLY, "sendonly" },
626 { MGCP_CONN_RECV_ONLY, "recvonly" },
627 { MGCP_CONN_LOOPBACK, "loopback" },
628 { 0, NULL }
629};