blob: 9b1f4d91bb1637ccd426f9e9174dfb388503155b [file] [log] [blame]
Harald Welteead7a7b2009-07-28 00:01:58 +02001/* RTP proxy handling for ip.access nanoBTS */
2
3/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
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 General Public License as published by
8 * the Free Software Foundation; either version 2 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 General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 */
21
22#include <errno.h>
23#include <unistd.h>
24#include <sys/socket.h>
25#include <netinet/in.h>
Harald Welte805f6442009-07-28 18:25:29 +020026#include <arpa/inet.h>
Harald Welteda7ab742009-12-19 22:23:05 +010027#include <sys/time.h> /* gettimeofday() */
28#include <unistd.h> /* get..() */
29#include <time.h> /* clock() */
30#include <sys/utsname.h> /* uname() */
Harald Welteead7a7b2009-07-28 00:01:58 +020031
Harald Weltedfe6c7d2010-02-20 16:24:02 +010032#include <osmocore/talloc.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020033#include <openbsc/gsm_data.h>
Harald Weltedfe6c7d2010-02-20 16:24:02 +010034#include <osmocore/msgb.h>
35#include <osmocore/select.h>
Harald Welte805f6442009-07-28 18:25:29 +020036#include <openbsc/debug.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020037#include <openbsc/rtp_proxy.h>
38
39static LLIST_HEAD(rtp_sockets);
40
Harald Welte805f6442009-07-28 18:25:29 +020041/* should we mangle the CNAME inside SDES of RTCP packets? We disable
42 * this by default, as it seems to be not needed */
43static int mangle_rtcp_cname = 0;
44
Harald Welteead7a7b2009-07-28 00:01:58 +020045enum rtp_bfd_priv {
46 RTP_PRIV_NONE,
47 RTP_PRIV_RTP,
48 RTP_PRIV_RTCP
49};
50
51#define RTP_ALLOC_SIZE 1500
52
Harald Welte805f6442009-07-28 18:25:29 +020053/* according to RFC 1889 */
54struct rtcp_hdr {
55 u_int8_t byte0;
56 u_int8_t type;
57 u_int16_t length;
58} __attribute__((packed));
59
60#define RTCP_TYPE_SDES 202
61
62#define RTCP_IE_CNAME 1
63
Harald Welteda7ab742009-12-19 22:23:05 +010064/* according to RFC 3550 */
65struct rtp_hdr {
66 u_int8_t csrc_count:4,
67 extension:1,
68 padding:1,
69 version:2;
70 u_int8_t payload_type:7,
71 marker:1;
72 u_int16_t sequence;
73 u_int32_t timestamp;
74 u_int32_t ssrc;
75} __attribute__((packed));
76
77struct rtp_x_hdr {
78 u_int16_t by_profile;
79 u_int16_t length;
80} __attribute__((packed));
81
82#define RTP_VERSION 2
83
84#define RTP_PT_GSM_FULL 3
Harald Welteaca8f152009-12-19 23:06:41 +010085#define RTP_PT_GSM_EFR 97
Harald Welteda7ab742009-12-19 22:23:05 +010086
87/* decode an rtp frame and create a new buffer with payload */
88static int rtp_decode(struct msgb *msg, u_int32_t callref, struct msgb **data)
89{
90 struct msgb *new_msg;
91 struct gsm_data_frame *frame;
92 struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
93 struct rtp_x_hdr *rtpxh;
94 u_int8_t *payload;
95 int payload_len;
96 int msg_type;
97 int x_len;
98
99 if (msg->len < 12) {
100 DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n",
101 msg->len);
102 return -EINVAL;
103 }
104 if (rtph->version != RTP_VERSION) {
105 DEBUGPC(DMUX, "received RTP version %d not supported.\n",
106 rtph->version);
107 return -EINVAL;
108 }
109 payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
110 payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
111 if (payload_len < 0) {
112 DEBUGPC(DMUX, "received RTP frame too short (len = %d, "
113 "csrc count = %d)\n", msg->len, rtph->csrc_count);
114 return -EINVAL;
115 }
116 if (rtph->extension) {
117 if (payload_len < sizeof(struct rtp_x_hdr)) {
118 DEBUGPC(DMUX, "received RTP frame too short for "
119 "extension header\n");
120 return -EINVAL;
121 }
122 rtpxh = (struct rtp_x_hdr *)payload;
123 x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
124 payload += x_len;
125 payload_len -= x_len;
126 if (payload_len < 0) {
127 DEBUGPC(DMUX, "received RTP frame too short, "
128 "extension header exceeds frame length\n");
129 return -EINVAL;
130 }
131 }
132 if (rtph->padding) {
133 if (payload_len < 0) {
134 DEBUGPC(DMUX, "received RTP frame too short for "
135 "padding length\n");
136 return -EINVAL;
137 }
138 payload_len -= payload[payload_len - 1];
139 if (payload_len < 0) {
140 DEBUGPC(DMUX, "received RTP frame with padding "
141 "greater than payload\n");
142 return -EINVAL;
143 }
144 }
145
146 switch (rtph->payload_type) {
147 case RTP_PT_GSM_FULL:
148 msg_type = GSM_TCHF_FRAME;
149 if (payload_len != 33) {
150 DEBUGPC(DMUX, "received RTP full rate frame with "
151 "payload length != 32 (len = %d)\n",
152 payload_len);
153 return -EINVAL;
154 }
155 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100156 case RTP_PT_GSM_EFR:
157 msg_type = GSM_TCHF_FRAME_EFR;
158 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100159 default:
160 DEBUGPC(DMUX, "received RTP frame with unknown payload "
161 "type %d\n", rtph->payload_type);
162 return -EINVAL;
163 }
164
165 new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len,
166 "GSM-DATA");
167 if (!new_msg)
168 return -ENOMEM;
169 frame = (struct gsm_data_frame *)(new_msg->data);
170 frame->msg_type = msg_type;
171 frame->callref = callref;
172 memcpy(frame->data, payload, payload_len);
173 msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len);
174
175 *data = new_msg;
176 return 0;
177}
178
Harald Welte392736d2009-12-20 13:16:14 +0100179/* "to - from" */
180static void tv_difference(struct timeval *diff, const struct timeval *from,
181 const struct timeval *__to)
182{
183 struct timeval _to = *__to, *to = &_to;
184
185 if (to->tv_usec < from->tv_usec) {
186 to->tv_sec -= 1;
187 to->tv_usec += 1000000;
188 }
189
190 diff->tv_usec = to->tv_usec - from->tv_usec;
191 diff->tv_sec = to->tv_sec - from->tv_sec;
192}
193
Harald Welteda7ab742009-12-19 22:23:05 +0100194/* encode and send a rtp frame */
195int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
196{
197 struct rtp_sub_socket *rss = &rs->rtp;
198 struct msgb *msg;
199 struct rtp_hdr *rtph;
200 int payload_type;
201 int payload_len;
202 int duration; /* in samples */
203
204 if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
205 /* initialize sequences */
206 rs->tx_action = RTP_SEND_DOWNSTREAM;
207 rs->transmit.ssrc = rand();
208 rs->transmit.sequence = random();
209 rs->transmit.timestamp = random();
210 }
211
212 switch (frame->msg_type) {
213 case GSM_TCHF_FRAME:
214 payload_type = RTP_PT_GSM_FULL;
215 payload_len = 33;
216 duration = 160;
217 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100218 case GSM_TCHF_FRAME_EFR:
219 payload_type = RTP_PT_GSM_EFR;
220 payload_len = 31;
221 duration = 160;
222 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100223 default:
224 DEBUGPC(DMUX, "unsupported message type %d\n",
225 frame->msg_type);
226 return -EINVAL;
227 }
228
Harald Welte392736d2009-12-20 13:16:14 +0100229 {
230 struct timeval tv, tv_diff;
231 long int usec_diff, frame_diff;
232
233 gettimeofday(&tv, NULL);
234 tv_difference(&tv_diff, &rs->transmit.last_tv, &tv);
235 rs->transmit.last_tv = tv;
236
237 usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
238 frame_diff = (usec_diff / 20000);
239
240 if (abs(frame_diff) > 1) {
241 long int frame_diff_excess = frame_diff - 1;
242
Harald Welte (local)9fcf6d72009-12-27 17:01:40 +0100243 LOGP(DMUX, LOGL_NOTICE,
244 "Correcting frame difference of %ld frames\n", frame_diff_excess);
Harald Welte392736d2009-12-20 13:16:14 +0100245 rs->transmit.sequence += frame_diff_excess;
246 rs->transmit.timestamp += frame_diff_excess * duration;
247 }
248 }
249
Harald Welteda7ab742009-12-19 22:23:05 +0100250 msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
251 if (!msg)
252 return -ENOMEM;
253 rtph = (struct rtp_hdr *)msg->data;
254 rtph->version = RTP_VERSION;
255 rtph->padding = 0;
256 rtph->extension = 0;
257 rtph->csrc_count = 0;
258 rtph->marker = 0;
259 rtph->payload_type = payload_type;
260 rtph->sequence = htons(rs->transmit.sequence++);
261 rtph->timestamp = htonl(rs->transmit.timestamp);
262 rs->transmit.timestamp += duration;
263 rtph->ssrc = htonl(rs->transmit.ssrc);
264 memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len);
265 msgb_put(msg, sizeof(struct rtp_hdr) + payload_len);
266 msgb_enqueue(&rss->tx_queue, msg);
267 rss->bfd.when |= BSC_FD_WRITE;
268
269 return 0;
270}
271
Holger Hans Peter Freyther89acf062009-07-29 06:46:26 +0200272/* iterate over all chunks in one RTCP message, look for CNAME IEs and
Harald Welte805f6442009-07-28 18:25:29 +0200273 * replace all of those with 'new_cname' */
274static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
275 u_int16_t *rtcp_len, const char *new_cname)
276{
277 u_int8_t *rtcp_end;
278 u_int8_t *cur = (u_int8_t *) rh;
279 u_int8_t tag, len = 0;
280
281 rtcp_end = cur + *rtcp_len;
282 /* move cur to end of RTP header */
Harald Welte198f3f52009-07-29 10:46:41 +0200283 cur += sizeof(*rh);
Harald Welte805f6442009-07-28 18:25:29 +0200284
285 /* iterate over Chunks */
286 while (cur+4 < rtcp_end) {
287 /* skip four bytes SSRC/CSRC */
288 cur += 4;
289
290 /* iterate over IE's inside the chunk */
291 while (cur+1 < rtcp_end) {
292 tag = *cur++;
293 if (tag == 0) {
294 /* end of chunk, skip additional zero */
295 while (*cur++ == 0) { }
296 break;
297 }
298 len = *cur++;
299
300 if (tag == RTCP_IE_CNAME) {
301 /* we've found the CNAME, lets mangle it */
302 if (len < strlen(new_cname)) {
303 /* we need to make more space */
304 int increase = strlen(new_cname) - len;
305
306 msgb_push(msg, increase);
307 memmove(cur+len+increase, cur+len,
308 rtcp_end - (cur+len));
309 /* FIXME: we have to respect RTCP
310 * padding/alignment rules! */
311 len += increase;
312 *(cur-1) += increase;
313 rtcp_end += increase;
314 *rtcp_len += increase;
315 }
316 /* copy new CNAME into message */
317 memcpy(cur, new_cname, strlen(new_cname));
318 /* FIXME: zero the padding in case new CNAME
319 * is smaller than old one !!! */
320 }
321 cur += len;
322 }
323 }
324
325 return 0;
326}
327
328static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
329{
330 struct rtp_sub_socket *rss = &rs->rtcp;
331 struct rtcp_hdr *rtph;
332 u_int16_t old_len;
333 int rc;
334
335 if (!mangle_rtcp_cname)
336 return 0;
337
Harald Welteda7ab742009-12-19 22:23:05 +0100338 printf("RTCP\n");
Harald Welte805f6442009-07-28 18:25:29 +0200339 /* iterate over list of RTCP messages */
340 rtph = (struct rtcp_hdr *)msg->data;
Harald Welteda7ab742009-12-19 22:23:05 +0100341 while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
Harald Welte805f6442009-07-28 18:25:29 +0200342 old_len = (ntohs(rtph->length) + 1) * 4;
Harald Welteda7ab742009-12-19 22:23:05 +0100343 if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
344 DEBUGPC(DMUX, "received RTCP packet too short for "
345 "length element\n");
346 return -EINVAL;
347 }
Harald Welte805f6442009-07-28 18:25:29 +0200348 if (rtph->type == RTCP_TYPE_SDES) {
349 char new_cname[255];
350 strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
351 sizeof(new_cname));
352 new_cname[sizeof(new_cname)-1] = '\0';
353 rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
354 new_cname);
355 if (rc < 0)
356 return rc;
357 }
358 rtph = (void *)rtph + old_len;
359 }
360
361 return 0;
362}
363
Harald Welteead7a7b2009-07-28 00:01:58 +0200364/* read from incoming RTP/RTCP socket */
365static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
366{
367 int rc;
368 struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
Harald Welteda7ab742009-12-19 22:23:05 +0100369 struct msgb *new_msg;
Harald Welteead7a7b2009-07-28 00:01:58 +0200370 struct rtp_sub_socket *other_rss;
371
372 if (!msg)
373 return -ENOMEM;
374
375 rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
376 if (rc <= 0) {
377 rss->bfd.when &= ~BSC_FD_READ;
378 return rc;
379 }
380
381 msgb_put(msg, rc);
382
383 switch (rs->rx_action) {
384 case RTP_PROXY:
Harald Welte805f6442009-07-28 18:25:29 +0200385 if (!rs->proxy.other_sock) {
386 rc = -EIO;
387 goto out_free;
388 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200389 if (rss->bfd.priv_nr == RTP_PRIV_RTP)
390 other_rss = &rs->proxy.other_sock->rtp;
Harald Welte805f6442009-07-28 18:25:29 +0200391 else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200392 other_rss = &rs->proxy.other_sock->rtcp;
Harald Welte805f6442009-07-28 18:25:29 +0200393 /* modify RTCP SDES CNAME */
394 rc = rtcp_mangle(msg, rs);
395 if (rc < 0)
396 goto out_free;
397 } else {
398 rc = -EINVAL;
399 goto out_free;
Harald Welteead7a7b2009-07-28 00:01:58 +0200400 }
401 msgb_enqueue(&other_rss->tx_queue, msg);
402 other_rss->bfd.when |= BSC_FD_WRITE;
403 break;
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200404
405 case RTP_RECV_UPSTREAM:
Harald Welteda7ab742009-12-19 22:23:05 +0100406 if (!rs->receive.callref || !rs->receive.net) {
407 rc = -EIO;
408 goto out_free;
409 }
410 if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
411 if (!mangle_rtcp_cname) {
412 msgb_free(msg);
413 break;
414 }
415 /* modify RTCP SDES CNAME */
416 rc = rtcp_mangle(msg, rs);
417 if (rc < 0)
418 goto out_free;
419 msgb_enqueue(&rss->tx_queue, msg);
420 rss->bfd.when |= BSC_FD_WRITE;
421 break;
422 }
423 if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
424 rc = -EINVAL;
425 goto out_free;
426 }
427 rc = rtp_decode(msg, rs->receive.callref, &new_msg);
428 if (rc < 0)
429 goto out_free;
430 msgb_free(msg);
431 msgb_enqueue(&rs->receive.net->upqueue, new_msg);
432 break;
433
434 case RTP_NONE: /* if socket exists, but disabled by app */
435 msgb_free(msg);
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200436 break;
Harald Welteead7a7b2009-07-28 00:01:58 +0200437 }
438
Harald Welteda7ab742009-12-19 22:23:05 +0100439 return 0;
Harald Welte805f6442009-07-28 18:25:29 +0200440
441out_free:
442 msgb_free(msg);
443 return rc;
Harald Welteead7a7b2009-07-28 00:01:58 +0200444}
445
446/* write from tx_queue to RTP/RTCP socket */
447static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
448{
449 struct msgb *msg;
450 int written;
451
452 msg = msgb_dequeue(&rss->tx_queue);
453 if (!msg) {
454 rss->bfd.when &= ~BSC_FD_WRITE;
455 return 0;
456 }
457
458 written = write(rss->bfd.fd, msg->data, msg->len);
459 if (written < msg->len) {
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100460 LOGP(DMIB, LOGL_ERROR, "short write");
Harald Welteead7a7b2009-07-28 00:01:58 +0200461 msgb_free(msg);
462 return -EIO;
463 }
464
465 msgb_free(msg);
466
467 return 0;
468}
469
470
471/* callback for the select.c:bfd_* layer */
472static int rtp_bfd_cb(struct bsc_fd *bfd, unsigned int flags)
473{
474 struct rtp_socket *rs = bfd->data;
475 struct rtp_sub_socket *rss;
476
477 switch (bfd->priv_nr) {
478 case RTP_PRIV_RTP:
479 rss = &rs->rtp;
480 break;
481 case RTP_PRIV_RTCP:
482 rss = &rs->rtcp;
483 break;
484 default:
485 return -EINVAL;
486 }
487
488 if (flags & BSC_FD_READ)
489 rtp_socket_read(rs, rss);
490
491 if (flags & BSC_FD_WRITE)
492 rtp_socket_write(rs, rss);
493
494 return 0;
495}
496
497static void init_rss(struct rtp_sub_socket *rss,
498 struct rtp_socket *rs, int fd, int priv_nr)
499{
500 /* initialize bfd */
501 rss->bfd.fd = fd;
502 rss->bfd.data = rs;
503 rss->bfd.priv_nr = priv_nr;
504 rss->bfd.cb = rtp_bfd_cb;
505}
506
507struct rtp_socket *rtp_socket_create(void)
508{
509 int rc;
510 struct rtp_socket *rs;
511
Harald Welte805f6442009-07-28 18:25:29 +0200512 DEBUGP(DMUX, "rtp_socket_create(): ");
513
Harald Welteead7a7b2009-07-28 00:01:58 +0200514 rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
515 if (!rs)
516 return NULL;
517
518 INIT_LLIST_HEAD(&rs->rtp.tx_queue);
519 INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
520
521 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
522 if (rc < 0)
523 goto out_free;
524
525 init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
526 rc = bsc_register_fd(&rs->rtp.bfd);
527 if (rc < 0)
528 goto out_rtp_socket;
529
530 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
531 if (rc < 0)
532 goto out_rtp_bfd;
533
534 init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
535 rc = bsc_register_fd(&rs->rtcp.bfd);
536 if (rc < 0)
537 goto out_rtcp_socket;
538
Harald Welte805f6442009-07-28 18:25:29 +0200539 DEBUGPC(DMUX, "success\n");
540
541 rc = rtp_socket_bind(rs, INADDR_ANY);
542 if (rc < 0)
543 goto out_rtcp_bfd;
544
Harald Welteead7a7b2009-07-28 00:01:58 +0200545 return rs;
546
Harald Welte805f6442009-07-28 18:25:29 +0200547out_rtcp_bfd:
548 bsc_unregister_fd(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200549out_rtcp_socket:
550 close(rs->rtcp.bfd.fd);
551out_rtp_bfd:
552 bsc_unregister_fd(&rs->rtp.bfd);
553out_rtp_socket:
554 close(rs->rtp.bfd.fd);
555out_free:
556 talloc_free(rs);
Harald Welte805f6442009-07-28 18:25:29 +0200557 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200558 return NULL;
559}
560
561static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, u_int32_t ip,
562 u_int16_t port)
563{
Harald Welte805f6442009-07-28 18:25:29 +0200564 int rc;
565 socklen_t alen = sizeof(rss->sin_local);
566
Harald Welteead7a7b2009-07-28 00:01:58 +0200567 rss->sin_local.sin_family = AF_INET;
568 rss->sin_local.sin_addr.s_addr = htonl(ip);
569 rss->sin_local.sin_port = htons(port);
570 rss->bfd.when |= BSC_FD_READ;
571
Harald Welte805f6442009-07-28 18:25:29 +0200572 rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
573 sizeof(rss->sin_local));
574 if (rc < 0)
575 return rc;
576
577 /* retrieve the address we actually bound to, in case we
578 * passed INADDR_ANY as IP address */
579 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
580 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200581}
582
583#define RTP_PORT_BASE 30000
Harald Welte805f6442009-07-28 18:25:29 +0200584static unsigned int next_udp_port = RTP_PORT_BASE;
Harald Welteead7a7b2009-07-28 00:01:58 +0200585
586/* bind a RTP socket to a local address */
587int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip)
588{
Harald Welte805f6442009-07-28 18:25:29 +0200589 int rc = -EIO;
590 struct in_addr ia;
591
592 ia.s_addr = htonl(ip);
593 DEBUGP(DMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
594 inet_ntoa(ia));
Harald Welteead7a7b2009-07-28 00:01:58 +0200595
596 /* try to bind to a consecutive pair of ports */
Harald Welte805f6442009-07-28 18:25:29 +0200597 for (next_udp_port = next_udp_port % 0xffff;
598 next_udp_port < 0xffff; next_udp_port += 2) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200599 rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
600 if (rc != 0)
601 continue;
602
603 rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
604 if (rc == 0)
605 break;
606 }
Harald Welte805f6442009-07-28 18:25:29 +0200607 if (rc < 0) {
608 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200609 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200610 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200611
Harald Welte805f6442009-07-28 18:25:29 +0200612 ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
613 DEBUGPC(DMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
614 inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
Harald Welteead7a7b2009-07-28 00:01:58 +0200615 return ntohs(rs->rtp.sin_local.sin_port);
616}
617
618static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
619 u_int32_t ip, u_int16_t port)
620{
Harald Welte805f6442009-07-28 18:25:29 +0200621 int rc;
622 socklen_t alen = sizeof(rss->sin_local);
623
Harald Welteead7a7b2009-07-28 00:01:58 +0200624 rss->sin_remote.sin_family = AF_INET;
625 rss->sin_remote.sin_addr.s_addr = htonl(ip);
626 rss->sin_remote.sin_port = htons(port);
627
Harald Welte805f6442009-07-28 18:25:29 +0200628 rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
629 sizeof(rss->sin_remote));
630 if (rc < 0)
631 return rc;
632
633 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
634 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200635}
636
637/* 'connect' a RTP socket to a remote peer */
638int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port)
639{
640 int rc;
Harald Welte805f6442009-07-28 18:25:29 +0200641 struct in_addr ia;
642
643 ia.s_addr = htonl(ip);
644 DEBUGP(DMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
645 rs, inet_ntoa(ia), port);
Harald Welteead7a7b2009-07-28 00:01:58 +0200646
647 rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
648 if (rc < 0)
649 return rc;
650
651 return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
652}
653
654/* bind two RTP/RTCP sockets together */
655int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
656{
Harald Welte805f6442009-07-28 18:25:29 +0200657 DEBUGP(DMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
658 this, other);
659
Harald Welteead7a7b2009-07-28 00:01:58 +0200660 this->rx_action = RTP_PROXY;
661 this->proxy.other_sock = other;
662
663 other->rx_action = RTP_PROXY;
664 other->proxy.other_sock = this;
665
666 return 0;
667}
668
Harald Welteda7ab742009-12-19 22:23:05 +0100669/* bind RTP/RTCP socket to application */
Harald Welte9fb1f102009-12-20 17:07:23 +0100670int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
671 u_int32_t callref)
Harald Welteda7ab742009-12-19 22:23:05 +0100672{
Harald Welte9fb1f102009-12-20 17:07:23 +0100673 DEBUGP(DMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100674 this, callref);
675
676 if (callref) {
677 this->rx_action = RTP_RECV_UPSTREAM;
678 this->receive.net = net;
679 this->receive.callref = callref;
680 } else
681 this->rx_action = RTP_NONE;
682
683 return 0;
684}
685
Harald Welteead7a7b2009-07-28 00:01:58 +0200686static void free_tx_queue(struct rtp_sub_socket *rss)
687{
688 struct msgb *msg;
689
690 while ((msg = msgb_dequeue(&rss->tx_queue)))
691 msgb_free(msg);
692}
693
694int rtp_socket_free(struct rtp_socket *rs)
695{
Harald Welte805f6442009-07-28 18:25:29 +0200696 DEBUGP(DMUX, "rtp_socket_free(rs=%p)\n", rs);
Harald Welteead7a7b2009-07-28 00:01:58 +0200697
698 /* make sure we don't leave references dangling to us */
699 if (rs->rx_action == RTP_PROXY &&
700 rs->proxy.other_sock)
701 rs->proxy.other_sock->proxy.other_sock = NULL;
702
703 bsc_unregister_fd(&rs->rtp.bfd);
704 close(rs->rtp.bfd.fd);
705 free_tx_queue(&rs->rtp);
706
707 bsc_unregister_fd(&rs->rtcp.bfd);
708 close(rs->rtcp.bfd.fd);
709 free_tx_queue(&rs->rtcp);
710
711 talloc_free(rs);
712
713 return 0;
714}