blob: 9f2e2fd765cd94fb3635c33c1078750bb771c21a [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
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010022#include <endian.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020023#include <errno.h>
24#include <unistd.h>
25#include <sys/socket.h>
26#include <netinet/in.h>
Harald Welte805f6442009-07-28 18:25:29 +020027#include <arpa/inet.h>
Harald Welteda7ab742009-12-19 22:23:05 +010028#include <sys/time.h> /* gettimeofday() */
29#include <unistd.h> /* get..() */
30#include <time.h> /* clock() */
31#include <sys/utsname.h> /* uname() */
Harald Welteead7a7b2009-07-28 00:01:58 +020032
Harald Weltedfe6c7d2010-02-20 16:24:02 +010033#include <osmocore/talloc.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020034#include <openbsc/gsm_data.h>
Harald Weltedfe6c7d2010-02-20 16:24:02 +010035#include <osmocore/msgb.h>
36#include <osmocore/select.h>
Harald Welte805f6442009-07-28 18:25:29 +020037#include <openbsc/debug.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020038#include <openbsc/rtp_proxy.h>
39
40static LLIST_HEAD(rtp_sockets);
41
Harald Welte805f6442009-07-28 18:25:29 +020042/* should we mangle the CNAME inside SDES of RTCP packets? We disable
43 * this by default, as it seems to be not needed */
44static int mangle_rtcp_cname = 0;
45
Harald Welteead7a7b2009-07-28 00:01:58 +020046enum rtp_bfd_priv {
47 RTP_PRIV_NONE,
48 RTP_PRIV_RTP,
49 RTP_PRIV_RTCP
50};
51
52#define RTP_ALLOC_SIZE 1500
53
Harald Welte805f6442009-07-28 18:25:29 +020054/* according to RFC 1889 */
55struct rtcp_hdr {
56 u_int8_t byte0;
57 u_int8_t type;
58 u_int16_t length;
59} __attribute__((packed));
60
61#define RTCP_TYPE_SDES 202
62
63#define RTCP_IE_CNAME 1
64
Harald Welteda7ab742009-12-19 22:23:05 +010065/* according to RFC 3550 */
66struct rtp_hdr {
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010067#if __BYTE_ORDER == __LITTLE_ENDIAN
Harald Welteda7ab742009-12-19 22:23:05 +010068 u_int8_t csrc_count:4,
69 extension:1,
70 padding:1,
71 version:2;
72 u_int8_t payload_type:7,
73 marker:1;
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010074#elif __BYTE_ORDER == __BIG_ENDIAN
75 u_int8_t version:2,
76 padding:1,
77 extension:1,
78 csrc_count:4;
79 u_int8_t marker:1,
80 payload_type:7;
81#endif
Harald Welteda7ab742009-12-19 22:23:05 +010082 u_int16_t sequence;
83 u_int32_t timestamp;
84 u_int32_t ssrc;
85} __attribute__((packed));
86
87struct rtp_x_hdr {
88 u_int16_t by_profile;
89 u_int16_t length;
90} __attribute__((packed));
91
92#define RTP_VERSION 2
93
94#define RTP_PT_GSM_FULL 3
Harald Welteaca8f152009-12-19 23:06:41 +010095#define RTP_PT_GSM_EFR 97
Harald Welteda7ab742009-12-19 22:23:05 +010096
97/* decode an rtp frame and create a new buffer with payload */
98static int rtp_decode(struct msgb *msg, u_int32_t callref, struct msgb **data)
99{
100 struct msgb *new_msg;
101 struct gsm_data_frame *frame;
102 struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
103 struct rtp_x_hdr *rtpxh;
104 u_int8_t *payload;
105 int payload_len;
106 int msg_type;
107 int x_len;
108
109 if (msg->len < 12) {
110 DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n",
111 msg->len);
112 return -EINVAL;
113 }
114 if (rtph->version != RTP_VERSION) {
115 DEBUGPC(DMUX, "received RTP version %d not supported.\n",
116 rtph->version);
117 return -EINVAL;
118 }
119 payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
120 payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
121 if (payload_len < 0) {
122 DEBUGPC(DMUX, "received RTP frame too short (len = %d, "
123 "csrc count = %d)\n", msg->len, rtph->csrc_count);
124 return -EINVAL;
125 }
126 if (rtph->extension) {
127 if (payload_len < sizeof(struct rtp_x_hdr)) {
128 DEBUGPC(DMUX, "received RTP frame too short for "
129 "extension header\n");
130 return -EINVAL;
131 }
132 rtpxh = (struct rtp_x_hdr *)payload;
133 x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
134 payload += x_len;
135 payload_len -= x_len;
136 if (payload_len < 0) {
137 DEBUGPC(DMUX, "received RTP frame too short, "
138 "extension header exceeds frame length\n");
139 return -EINVAL;
140 }
141 }
142 if (rtph->padding) {
143 if (payload_len < 0) {
144 DEBUGPC(DMUX, "received RTP frame too short for "
145 "padding length\n");
146 return -EINVAL;
147 }
148 payload_len -= payload[payload_len - 1];
149 if (payload_len < 0) {
150 DEBUGPC(DMUX, "received RTP frame with padding "
151 "greater than payload\n");
152 return -EINVAL;
153 }
154 }
155
156 switch (rtph->payload_type) {
157 case RTP_PT_GSM_FULL:
158 msg_type = GSM_TCHF_FRAME;
159 if (payload_len != 33) {
160 DEBUGPC(DMUX, "received RTP full rate frame with "
161 "payload length != 32 (len = %d)\n",
162 payload_len);
163 return -EINVAL;
164 }
165 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100166 case RTP_PT_GSM_EFR:
167 msg_type = GSM_TCHF_FRAME_EFR;
168 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100169 default:
170 DEBUGPC(DMUX, "received RTP frame with unknown payload "
171 "type %d\n", rtph->payload_type);
172 return -EINVAL;
173 }
174
175 new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len,
176 "GSM-DATA");
177 if (!new_msg)
178 return -ENOMEM;
179 frame = (struct gsm_data_frame *)(new_msg->data);
180 frame->msg_type = msg_type;
181 frame->callref = callref;
182 memcpy(frame->data, payload, payload_len);
183 msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len);
184
185 *data = new_msg;
186 return 0;
187}
188
Harald Welte392736d2009-12-20 13:16:14 +0100189/* "to - from" */
190static void tv_difference(struct timeval *diff, const struct timeval *from,
191 const struct timeval *__to)
192{
193 struct timeval _to = *__to, *to = &_to;
194
195 if (to->tv_usec < from->tv_usec) {
196 to->tv_sec -= 1;
197 to->tv_usec += 1000000;
198 }
199
200 diff->tv_usec = to->tv_usec - from->tv_usec;
201 diff->tv_sec = to->tv_sec - from->tv_sec;
202}
203
Harald Welteda7ab742009-12-19 22:23:05 +0100204/* encode and send a rtp frame */
205int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
206{
207 struct rtp_sub_socket *rss = &rs->rtp;
208 struct msgb *msg;
209 struct rtp_hdr *rtph;
210 int payload_type;
211 int payload_len;
212 int duration; /* in samples */
213
214 if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
215 /* initialize sequences */
216 rs->tx_action = RTP_SEND_DOWNSTREAM;
217 rs->transmit.ssrc = rand();
218 rs->transmit.sequence = random();
219 rs->transmit.timestamp = random();
220 }
221
222 switch (frame->msg_type) {
223 case GSM_TCHF_FRAME:
224 payload_type = RTP_PT_GSM_FULL;
225 payload_len = 33;
226 duration = 160;
227 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100228 case GSM_TCHF_FRAME_EFR:
229 payload_type = RTP_PT_GSM_EFR;
230 payload_len = 31;
231 duration = 160;
232 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100233 default:
234 DEBUGPC(DMUX, "unsupported message type %d\n",
235 frame->msg_type);
236 return -EINVAL;
237 }
238
Harald Welte392736d2009-12-20 13:16:14 +0100239 {
240 struct timeval tv, tv_diff;
241 long int usec_diff, frame_diff;
242
243 gettimeofday(&tv, NULL);
244 tv_difference(&tv_diff, &rs->transmit.last_tv, &tv);
245 rs->transmit.last_tv = tv;
246
247 usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
248 frame_diff = (usec_diff / 20000);
249
250 if (abs(frame_diff) > 1) {
251 long int frame_diff_excess = frame_diff - 1;
252
Harald Welte (local)9fcf6d72009-12-27 17:01:40 +0100253 LOGP(DMUX, LOGL_NOTICE,
254 "Correcting frame difference of %ld frames\n", frame_diff_excess);
Harald Welte392736d2009-12-20 13:16:14 +0100255 rs->transmit.sequence += frame_diff_excess;
256 rs->transmit.timestamp += frame_diff_excess * duration;
257 }
258 }
259
Harald Welteda7ab742009-12-19 22:23:05 +0100260 msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
261 if (!msg)
262 return -ENOMEM;
263 rtph = (struct rtp_hdr *)msg->data;
264 rtph->version = RTP_VERSION;
265 rtph->padding = 0;
266 rtph->extension = 0;
267 rtph->csrc_count = 0;
268 rtph->marker = 0;
269 rtph->payload_type = payload_type;
270 rtph->sequence = htons(rs->transmit.sequence++);
271 rtph->timestamp = htonl(rs->transmit.timestamp);
272 rs->transmit.timestamp += duration;
273 rtph->ssrc = htonl(rs->transmit.ssrc);
274 memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len);
275 msgb_put(msg, sizeof(struct rtp_hdr) + payload_len);
276 msgb_enqueue(&rss->tx_queue, msg);
277 rss->bfd.when |= BSC_FD_WRITE;
278
279 return 0;
280}
281
Holger Hans Peter Freyther89acf062009-07-29 06:46:26 +0200282/* iterate over all chunks in one RTCP message, look for CNAME IEs and
Harald Welte805f6442009-07-28 18:25:29 +0200283 * replace all of those with 'new_cname' */
284static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
285 u_int16_t *rtcp_len, const char *new_cname)
286{
287 u_int8_t *rtcp_end;
288 u_int8_t *cur = (u_int8_t *) rh;
289 u_int8_t tag, len = 0;
290
291 rtcp_end = cur + *rtcp_len;
292 /* move cur to end of RTP header */
Harald Welte198f3f52009-07-29 10:46:41 +0200293 cur += sizeof(*rh);
Harald Welte805f6442009-07-28 18:25:29 +0200294
295 /* iterate over Chunks */
296 while (cur+4 < rtcp_end) {
297 /* skip four bytes SSRC/CSRC */
298 cur += 4;
299
300 /* iterate over IE's inside the chunk */
301 while (cur+1 < rtcp_end) {
302 tag = *cur++;
303 if (tag == 0) {
304 /* end of chunk, skip additional zero */
305 while (*cur++ == 0) { }
306 break;
307 }
308 len = *cur++;
309
310 if (tag == RTCP_IE_CNAME) {
311 /* we've found the CNAME, lets mangle it */
312 if (len < strlen(new_cname)) {
313 /* we need to make more space */
314 int increase = strlen(new_cname) - len;
315
316 msgb_push(msg, increase);
317 memmove(cur+len+increase, cur+len,
318 rtcp_end - (cur+len));
319 /* FIXME: we have to respect RTCP
320 * padding/alignment rules! */
321 len += increase;
322 *(cur-1) += increase;
323 rtcp_end += increase;
324 *rtcp_len += increase;
325 }
326 /* copy new CNAME into message */
327 memcpy(cur, new_cname, strlen(new_cname));
328 /* FIXME: zero the padding in case new CNAME
329 * is smaller than old one !!! */
330 }
331 cur += len;
332 }
333 }
334
335 return 0;
336}
337
338static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
339{
340 struct rtp_sub_socket *rss = &rs->rtcp;
341 struct rtcp_hdr *rtph;
342 u_int16_t old_len;
343 int rc;
344
345 if (!mangle_rtcp_cname)
346 return 0;
347
Harald Welteda7ab742009-12-19 22:23:05 +0100348 printf("RTCP\n");
Harald Welte805f6442009-07-28 18:25:29 +0200349 /* iterate over list of RTCP messages */
350 rtph = (struct rtcp_hdr *)msg->data;
Harald Welteda7ab742009-12-19 22:23:05 +0100351 while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
Harald Welte805f6442009-07-28 18:25:29 +0200352 old_len = (ntohs(rtph->length) + 1) * 4;
Harald Welteda7ab742009-12-19 22:23:05 +0100353 if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
354 DEBUGPC(DMUX, "received RTCP packet too short for "
355 "length element\n");
356 return -EINVAL;
357 }
Harald Welte805f6442009-07-28 18:25:29 +0200358 if (rtph->type == RTCP_TYPE_SDES) {
359 char new_cname[255];
360 strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
361 sizeof(new_cname));
362 new_cname[sizeof(new_cname)-1] = '\0';
363 rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
364 new_cname);
365 if (rc < 0)
366 return rc;
367 }
368 rtph = (void *)rtph + old_len;
369 }
370
371 return 0;
372}
373
Harald Welteead7a7b2009-07-28 00:01:58 +0200374/* read from incoming RTP/RTCP socket */
375static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
376{
377 int rc;
378 struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
Harald Welteda7ab742009-12-19 22:23:05 +0100379 struct msgb *new_msg;
Harald Welteead7a7b2009-07-28 00:01:58 +0200380 struct rtp_sub_socket *other_rss;
381
382 if (!msg)
383 return -ENOMEM;
384
385 rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
386 if (rc <= 0) {
387 rss->bfd.when &= ~BSC_FD_READ;
388 return rc;
389 }
390
391 msgb_put(msg, rc);
392
393 switch (rs->rx_action) {
394 case RTP_PROXY:
Harald Welte805f6442009-07-28 18:25:29 +0200395 if (!rs->proxy.other_sock) {
396 rc = -EIO;
397 goto out_free;
398 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200399 if (rss->bfd.priv_nr == RTP_PRIV_RTP)
400 other_rss = &rs->proxy.other_sock->rtp;
Harald Welte805f6442009-07-28 18:25:29 +0200401 else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200402 other_rss = &rs->proxy.other_sock->rtcp;
Harald Welte805f6442009-07-28 18:25:29 +0200403 /* modify RTCP SDES CNAME */
404 rc = rtcp_mangle(msg, rs);
405 if (rc < 0)
406 goto out_free;
407 } else {
408 rc = -EINVAL;
409 goto out_free;
Harald Welteead7a7b2009-07-28 00:01:58 +0200410 }
411 msgb_enqueue(&other_rss->tx_queue, msg);
412 other_rss->bfd.when |= BSC_FD_WRITE;
413 break;
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200414
415 case RTP_RECV_UPSTREAM:
Harald Welteda7ab742009-12-19 22:23:05 +0100416 if (!rs->receive.callref || !rs->receive.net) {
417 rc = -EIO;
418 goto out_free;
419 }
420 if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
421 if (!mangle_rtcp_cname) {
422 msgb_free(msg);
423 break;
424 }
425 /* modify RTCP SDES CNAME */
426 rc = rtcp_mangle(msg, rs);
427 if (rc < 0)
428 goto out_free;
429 msgb_enqueue(&rss->tx_queue, msg);
430 rss->bfd.when |= BSC_FD_WRITE;
431 break;
432 }
433 if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
434 rc = -EINVAL;
435 goto out_free;
436 }
437 rc = rtp_decode(msg, rs->receive.callref, &new_msg);
438 if (rc < 0)
439 goto out_free;
440 msgb_free(msg);
441 msgb_enqueue(&rs->receive.net->upqueue, new_msg);
442 break;
443
444 case RTP_NONE: /* if socket exists, but disabled by app */
445 msgb_free(msg);
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200446 break;
Harald Welteead7a7b2009-07-28 00:01:58 +0200447 }
448
Harald Welteda7ab742009-12-19 22:23:05 +0100449 return 0;
Harald Welte805f6442009-07-28 18:25:29 +0200450
451out_free:
452 msgb_free(msg);
453 return rc;
Harald Welteead7a7b2009-07-28 00:01:58 +0200454}
455
456/* write from tx_queue to RTP/RTCP socket */
457static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
458{
459 struct msgb *msg;
460 int written;
461
462 msg = msgb_dequeue(&rss->tx_queue);
463 if (!msg) {
464 rss->bfd.when &= ~BSC_FD_WRITE;
465 return 0;
466 }
467
468 written = write(rss->bfd.fd, msg->data, msg->len);
469 if (written < msg->len) {
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100470 LOGP(DMIB, LOGL_ERROR, "short write");
Harald Welteead7a7b2009-07-28 00:01:58 +0200471 msgb_free(msg);
472 return -EIO;
473 }
474
475 msgb_free(msg);
476
477 return 0;
478}
479
480
481/* callback for the select.c:bfd_* layer */
482static int rtp_bfd_cb(struct bsc_fd *bfd, unsigned int flags)
483{
484 struct rtp_socket *rs = bfd->data;
485 struct rtp_sub_socket *rss;
486
487 switch (bfd->priv_nr) {
488 case RTP_PRIV_RTP:
489 rss = &rs->rtp;
490 break;
491 case RTP_PRIV_RTCP:
492 rss = &rs->rtcp;
493 break;
494 default:
495 return -EINVAL;
496 }
497
498 if (flags & BSC_FD_READ)
499 rtp_socket_read(rs, rss);
500
501 if (flags & BSC_FD_WRITE)
502 rtp_socket_write(rs, rss);
503
504 return 0;
505}
506
507static void init_rss(struct rtp_sub_socket *rss,
508 struct rtp_socket *rs, int fd, int priv_nr)
509{
510 /* initialize bfd */
511 rss->bfd.fd = fd;
512 rss->bfd.data = rs;
513 rss->bfd.priv_nr = priv_nr;
514 rss->bfd.cb = rtp_bfd_cb;
515}
516
517struct rtp_socket *rtp_socket_create(void)
518{
519 int rc;
520 struct rtp_socket *rs;
521
Harald Welte805f6442009-07-28 18:25:29 +0200522 DEBUGP(DMUX, "rtp_socket_create(): ");
523
Harald Welteead7a7b2009-07-28 00:01:58 +0200524 rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
525 if (!rs)
526 return NULL;
527
528 INIT_LLIST_HEAD(&rs->rtp.tx_queue);
529 INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
530
531 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
532 if (rc < 0)
533 goto out_free;
534
535 init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
536 rc = bsc_register_fd(&rs->rtp.bfd);
537 if (rc < 0)
538 goto out_rtp_socket;
539
540 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
541 if (rc < 0)
542 goto out_rtp_bfd;
543
544 init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
545 rc = bsc_register_fd(&rs->rtcp.bfd);
546 if (rc < 0)
547 goto out_rtcp_socket;
548
Harald Welte805f6442009-07-28 18:25:29 +0200549 DEBUGPC(DMUX, "success\n");
550
551 rc = rtp_socket_bind(rs, INADDR_ANY);
552 if (rc < 0)
553 goto out_rtcp_bfd;
554
Harald Welteead7a7b2009-07-28 00:01:58 +0200555 return rs;
556
Harald Welte805f6442009-07-28 18:25:29 +0200557out_rtcp_bfd:
558 bsc_unregister_fd(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200559out_rtcp_socket:
560 close(rs->rtcp.bfd.fd);
561out_rtp_bfd:
562 bsc_unregister_fd(&rs->rtp.bfd);
563out_rtp_socket:
564 close(rs->rtp.bfd.fd);
565out_free:
566 talloc_free(rs);
Harald Welte805f6442009-07-28 18:25:29 +0200567 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200568 return NULL;
569}
570
571static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, u_int32_t ip,
572 u_int16_t port)
573{
Harald Welte805f6442009-07-28 18:25:29 +0200574 int rc;
575 socklen_t alen = sizeof(rss->sin_local);
576
Harald Welteead7a7b2009-07-28 00:01:58 +0200577 rss->sin_local.sin_family = AF_INET;
578 rss->sin_local.sin_addr.s_addr = htonl(ip);
579 rss->sin_local.sin_port = htons(port);
580 rss->bfd.when |= BSC_FD_READ;
581
Harald Welte805f6442009-07-28 18:25:29 +0200582 rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
583 sizeof(rss->sin_local));
584 if (rc < 0)
585 return rc;
586
587 /* retrieve the address we actually bound to, in case we
588 * passed INADDR_ANY as IP address */
589 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
590 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200591}
592
593#define RTP_PORT_BASE 30000
Harald Welte805f6442009-07-28 18:25:29 +0200594static unsigned int next_udp_port = RTP_PORT_BASE;
Harald Welteead7a7b2009-07-28 00:01:58 +0200595
596/* bind a RTP socket to a local address */
597int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip)
598{
Harald Welte805f6442009-07-28 18:25:29 +0200599 int rc = -EIO;
600 struct in_addr ia;
601
602 ia.s_addr = htonl(ip);
603 DEBUGP(DMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
604 inet_ntoa(ia));
Harald Welteead7a7b2009-07-28 00:01:58 +0200605
606 /* try to bind to a consecutive pair of ports */
Harald Welte805f6442009-07-28 18:25:29 +0200607 for (next_udp_port = next_udp_port % 0xffff;
608 next_udp_port < 0xffff; next_udp_port += 2) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200609 rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
610 if (rc != 0)
611 continue;
612
613 rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
614 if (rc == 0)
615 break;
616 }
Harald Welte805f6442009-07-28 18:25:29 +0200617 if (rc < 0) {
618 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200619 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200620 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200621
Harald Welte805f6442009-07-28 18:25:29 +0200622 ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
623 DEBUGPC(DMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
624 inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
Harald Welteead7a7b2009-07-28 00:01:58 +0200625 return ntohs(rs->rtp.sin_local.sin_port);
626}
627
628static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
629 u_int32_t ip, u_int16_t port)
630{
Harald Welte805f6442009-07-28 18:25:29 +0200631 int rc;
632 socklen_t alen = sizeof(rss->sin_local);
633
Harald Welteead7a7b2009-07-28 00:01:58 +0200634 rss->sin_remote.sin_family = AF_INET;
635 rss->sin_remote.sin_addr.s_addr = htonl(ip);
636 rss->sin_remote.sin_port = htons(port);
637
Harald Welte805f6442009-07-28 18:25:29 +0200638 rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
639 sizeof(rss->sin_remote));
640 if (rc < 0)
641 return rc;
642
643 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
644 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200645}
646
647/* 'connect' a RTP socket to a remote peer */
648int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port)
649{
650 int rc;
Harald Welte805f6442009-07-28 18:25:29 +0200651 struct in_addr ia;
652
653 ia.s_addr = htonl(ip);
654 DEBUGP(DMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
655 rs, inet_ntoa(ia), port);
Harald Welteead7a7b2009-07-28 00:01:58 +0200656
657 rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
658 if (rc < 0)
659 return rc;
660
661 return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
662}
663
664/* bind two RTP/RTCP sockets together */
665int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
666{
Harald Welte805f6442009-07-28 18:25:29 +0200667 DEBUGP(DMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
668 this, other);
669
Harald Welteead7a7b2009-07-28 00:01:58 +0200670 this->rx_action = RTP_PROXY;
671 this->proxy.other_sock = other;
672
673 other->rx_action = RTP_PROXY;
674 other->proxy.other_sock = this;
675
676 return 0;
677}
678
Harald Welteda7ab742009-12-19 22:23:05 +0100679/* bind RTP/RTCP socket to application */
Harald Welte9fb1f102009-12-20 17:07:23 +0100680int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
681 u_int32_t callref)
Harald Welteda7ab742009-12-19 22:23:05 +0100682{
Harald Welte9fb1f102009-12-20 17:07:23 +0100683 DEBUGP(DMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100684 this, callref);
685
686 if (callref) {
687 this->rx_action = RTP_RECV_UPSTREAM;
688 this->receive.net = net;
689 this->receive.callref = callref;
690 } else
691 this->rx_action = RTP_NONE;
692
693 return 0;
694}
695
Harald Welteead7a7b2009-07-28 00:01:58 +0200696static void free_tx_queue(struct rtp_sub_socket *rss)
697{
698 struct msgb *msg;
699
700 while ((msg = msgb_dequeue(&rss->tx_queue)))
701 msgb_free(msg);
702}
703
704int rtp_socket_free(struct rtp_socket *rs)
705{
Harald Welte805f6442009-07-28 18:25:29 +0200706 DEBUGP(DMUX, "rtp_socket_free(rs=%p)\n", rs);
Harald Welteead7a7b2009-07-28 00:01:58 +0200707
708 /* make sure we don't leave references dangling to us */
709 if (rs->rx_action == RTP_PROXY &&
710 rs->proxy.other_sock)
711 rs->proxy.other_sock->proxy.other_sock = NULL;
712
713 bsc_unregister_fd(&rs->rtp.bfd);
714 close(rs->rtp.bfd.fd);
715 free_tx_queue(&rs->rtp);
716
717 bsc_unregister_fd(&rs->rtcp.bfd);
718 close(rs->rtcp.bfd.fd);
719 free_tx_queue(&rs->rtcp);
720
721 talloc_free(rs);
722
723 return 0;
724}