blob: 924173dd2a2db61bf3e7fa6f840616c34f8a2e51 [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
Harald Welteda7ab742009-12-19 22:23:05 +010094/* decode an rtp frame and create a new buffer with payload */
95static int rtp_decode(struct msgb *msg, u_int32_t callref, struct msgb **data)
96{
97 struct msgb *new_msg;
98 struct gsm_data_frame *frame;
99 struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
100 struct rtp_x_hdr *rtpxh;
101 u_int8_t *payload;
102 int payload_len;
103 int msg_type;
104 int x_len;
105
106 if (msg->len < 12) {
107 DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n",
108 msg->len);
109 return -EINVAL;
110 }
111 if (rtph->version != RTP_VERSION) {
112 DEBUGPC(DMUX, "received RTP version %d not supported.\n",
113 rtph->version);
114 return -EINVAL;
115 }
116 payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
117 payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
118 if (payload_len < 0) {
119 DEBUGPC(DMUX, "received RTP frame too short (len = %d, "
120 "csrc count = %d)\n", msg->len, rtph->csrc_count);
121 return -EINVAL;
122 }
123 if (rtph->extension) {
124 if (payload_len < sizeof(struct rtp_x_hdr)) {
125 DEBUGPC(DMUX, "received RTP frame too short for "
126 "extension header\n");
127 return -EINVAL;
128 }
129 rtpxh = (struct rtp_x_hdr *)payload;
130 x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
131 payload += x_len;
132 payload_len -= x_len;
133 if (payload_len < 0) {
134 DEBUGPC(DMUX, "received RTP frame too short, "
135 "extension header exceeds frame length\n");
136 return -EINVAL;
137 }
138 }
139 if (rtph->padding) {
140 if (payload_len < 0) {
141 DEBUGPC(DMUX, "received RTP frame too short for "
142 "padding length\n");
143 return -EINVAL;
144 }
145 payload_len -= payload[payload_len - 1];
146 if (payload_len < 0) {
147 DEBUGPC(DMUX, "received RTP frame with padding "
148 "greater than payload\n");
149 return -EINVAL;
150 }
151 }
152
153 switch (rtph->payload_type) {
154 case RTP_PT_GSM_FULL:
155 msg_type = GSM_TCHF_FRAME;
156 if (payload_len != 33) {
157 DEBUGPC(DMUX, "received RTP full rate frame with "
158 "payload length != 32 (len = %d)\n",
159 payload_len);
160 return -EINVAL;
161 }
162 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100163 case RTP_PT_GSM_EFR:
164 msg_type = GSM_TCHF_FRAME_EFR;
165 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100166 default:
167 DEBUGPC(DMUX, "received RTP frame with unknown payload "
168 "type %d\n", rtph->payload_type);
169 return -EINVAL;
170 }
171
172 new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len,
173 "GSM-DATA");
174 if (!new_msg)
175 return -ENOMEM;
176 frame = (struct gsm_data_frame *)(new_msg->data);
177 frame->msg_type = msg_type;
178 frame->callref = callref;
179 memcpy(frame->data, payload, payload_len);
180 msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len);
181
182 *data = new_msg;
183 return 0;
184}
185
Harald Welte392736d2009-12-20 13:16:14 +0100186/* "to - from" */
187static void tv_difference(struct timeval *diff, const struct timeval *from,
188 const struct timeval *__to)
189{
190 struct timeval _to = *__to, *to = &_to;
191
192 if (to->tv_usec < from->tv_usec) {
193 to->tv_sec -= 1;
194 to->tv_usec += 1000000;
195 }
196
197 diff->tv_usec = to->tv_usec - from->tv_usec;
198 diff->tv_sec = to->tv_sec - from->tv_sec;
199}
200
Harald Welteda7ab742009-12-19 22:23:05 +0100201/* encode and send a rtp frame */
202int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
203{
204 struct rtp_sub_socket *rss = &rs->rtp;
205 struct msgb *msg;
206 struct rtp_hdr *rtph;
207 int payload_type;
208 int payload_len;
209 int duration; /* in samples */
210
211 if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
212 /* initialize sequences */
213 rs->tx_action = RTP_SEND_DOWNSTREAM;
214 rs->transmit.ssrc = rand();
215 rs->transmit.sequence = random();
216 rs->transmit.timestamp = random();
217 }
218
219 switch (frame->msg_type) {
220 case GSM_TCHF_FRAME:
221 payload_type = RTP_PT_GSM_FULL;
222 payload_len = 33;
223 duration = 160;
224 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100225 case GSM_TCHF_FRAME_EFR:
226 payload_type = RTP_PT_GSM_EFR;
227 payload_len = 31;
228 duration = 160;
229 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100230 default:
231 DEBUGPC(DMUX, "unsupported message type %d\n",
232 frame->msg_type);
233 return -EINVAL;
234 }
235
Harald Welte392736d2009-12-20 13:16:14 +0100236 {
237 struct timeval tv, tv_diff;
238 long int usec_diff, frame_diff;
239
240 gettimeofday(&tv, NULL);
241 tv_difference(&tv_diff, &rs->transmit.last_tv, &tv);
242 rs->transmit.last_tv = tv;
243
244 usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
245 frame_diff = (usec_diff / 20000);
246
247 if (abs(frame_diff) > 1) {
248 long int frame_diff_excess = frame_diff - 1;
249
Harald Welte (local)9fcf6d72009-12-27 17:01:40 +0100250 LOGP(DMUX, LOGL_NOTICE,
251 "Correcting frame difference of %ld frames\n", frame_diff_excess);
Harald Welte392736d2009-12-20 13:16:14 +0100252 rs->transmit.sequence += frame_diff_excess;
253 rs->transmit.timestamp += frame_diff_excess * duration;
254 }
255 }
256
Harald Welteda7ab742009-12-19 22:23:05 +0100257 msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
258 if (!msg)
259 return -ENOMEM;
260 rtph = (struct rtp_hdr *)msg->data;
261 rtph->version = RTP_VERSION;
262 rtph->padding = 0;
263 rtph->extension = 0;
264 rtph->csrc_count = 0;
265 rtph->marker = 0;
266 rtph->payload_type = payload_type;
267 rtph->sequence = htons(rs->transmit.sequence++);
268 rtph->timestamp = htonl(rs->transmit.timestamp);
269 rs->transmit.timestamp += duration;
270 rtph->ssrc = htonl(rs->transmit.ssrc);
271 memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len);
272 msgb_put(msg, sizeof(struct rtp_hdr) + payload_len);
273 msgb_enqueue(&rss->tx_queue, msg);
274 rss->bfd.when |= BSC_FD_WRITE;
275
276 return 0;
277}
278
Holger Hans Peter Freyther89acf062009-07-29 06:46:26 +0200279/* iterate over all chunks in one RTCP message, look for CNAME IEs and
Harald Welte805f6442009-07-28 18:25:29 +0200280 * replace all of those with 'new_cname' */
281static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
282 u_int16_t *rtcp_len, const char *new_cname)
283{
284 u_int8_t *rtcp_end;
285 u_int8_t *cur = (u_int8_t *) rh;
286 u_int8_t tag, len = 0;
287
288 rtcp_end = cur + *rtcp_len;
289 /* move cur to end of RTP header */
Harald Welte198f3f52009-07-29 10:46:41 +0200290 cur += sizeof(*rh);
Harald Welte805f6442009-07-28 18:25:29 +0200291
292 /* iterate over Chunks */
293 while (cur+4 < rtcp_end) {
294 /* skip four bytes SSRC/CSRC */
295 cur += 4;
296
297 /* iterate over IE's inside the chunk */
298 while (cur+1 < rtcp_end) {
299 tag = *cur++;
300 if (tag == 0) {
301 /* end of chunk, skip additional zero */
302 while (*cur++ == 0) { }
303 break;
304 }
305 len = *cur++;
306
307 if (tag == RTCP_IE_CNAME) {
308 /* we've found the CNAME, lets mangle it */
309 if (len < strlen(new_cname)) {
310 /* we need to make more space */
311 int increase = strlen(new_cname) - len;
312
313 msgb_push(msg, increase);
314 memmove(cur+len+increase, cur+len,
315 rtcp_end - (cur+len));
316 /* FIXME: we have to respect RTCP
317 * padding/alignment rules! */
318 len += increase;
319 *(cur-1) += increase;
320 rtcp_end += increase;
321 *rtcp_len += increase;
322 }
323 /* copy new CNAME into message */
324 memcpy(cur, new_cname, strlen(new_cname));
325 /* FIXME: zero the padding in case new CNAME
326 * is smaller than old one !!! */
327 }
328 cur += len;
329 }
330 }
331
332 return 0;
333}
334
335static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
336{
337 struct rtp_sub_socket *rss = &rs->rtcp;
338 struct rtcp_hdr *rtph;
339 u_int16_t old_len;
340 int rc;
341
342 if (!mangle_rtcp_cname)
343 return 0;
344
Harald Welteda7ab742009-12-19 22:23:05 +0100345 printf("RTCP\n");
Harald Welte805f6442009-07-28 18:25:29 +0200346 /* iterate over list of RTCP messages */
347 rtph = (struct rtcp_hdr *)msg->data;
Harald Welteda7ab742009-12-19 22:23:05 +0100348 while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
Harald Welte805f6442009-07-28 18:25:29 +0200349 old_len = (ntohs(rtph->length) + 1) * 4;
Harald Welteda7ab742009-12-19 22:23:05 +0100350 if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
351 DEBUGPC(DMUX, "received RTCP packet too short for "
352 "length element\n");
353 return -EINVAL;
354 }
Harald Welte805f6442009-07-28 18:25:29 +0200355 if (rtph->type == RTCP_TYPE_SDES) {
356 char new_cname[255];
357 strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
358 sizeof(new_cname));
359 new_cname[sizeof(new_cname)-1] = '\0';
360 rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
361 new_cname);
362 if (rc < 0)
363 return rc;
364 }
365 rtph = (void *)rtph + old_len;
366 }
367
368 return 0;
369}
370
Harald Welteead7a7b2009-07-28 00:01:58 +0200371/* read from incoming RTP/RTCP socket */
372static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
373{
374 int rc;
375 struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
Harald Welteda7ab742009-12-19 22:23:05 +0100376 struct msgb *new_msg;
Harald Welteead7a7b2009-07-28 00:01:58 +0200377 struct rtp_sub_socket *other_rss;
378
379 if (!msg)
380 return -ENOMEM;
381
382 rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
383 if (rc <= 0) {
384 rss->bfd.when &= ~BSC_FD_READ;
385 return rc;
386 }
387
388 msgb_put(msg, rc);
389
390 switch (rs->rx_action) {
391 case RTP_PROXY:
Harald Welte805f6442009-07-28 18:25:29 +0200392 if (!rs->proxy.other_sock) {
393 rc = -EIO;
394 goto out_free;
395 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200396 if (rss->bfd.priv_nr == RTP_PRIV_RTP)
397 other_rss = &rs->proxy.other_sock->rtp;
Harald Welte805f6442009-07-28 18:25:29 +0200398 else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200399 other_rss = &rs->proxy.other_sock->rtcp;
Harald Welte805f6442009-07-28 18:25:29 +0200400 /* modify RTCP SDES CNAME */
401 rc = rtcp_mangle(msg, rs);
402 if (rc < 0)
403 goto out_free;
404 } else {
405 rc = -EINVAL;
406 goto out_free;
Harald Welteead7a7b2009-07-28 00:01:58 +0200407 }
408 msgb_enqueue(&other_rss->tx_queue, msg);
409 other_rss->bfd.when |= BSC_FD_WRITE;
410 break;
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200411
412 case RTP_RECV_UPSTREAM:
Harald Welteda7ab742009-12-19 22:23:05 +0100413 if (!rs->receive.callref || !rs->receive.net) {
414 rc = -EIO;
415 goto out_free;
416 }
417 if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
418 if (!mangle_rtcp_cname) {
419 msgb_free(msg);
420 break;
421 }
422 /* modify RTCP SDES CNAME */
423 rc = rtcp_mangle(msg, rs);
424 if (rc < 0)
425 goto out_free;
426 msgb_enqueue(&rss->tx_queue, msg);
427 rss->bfd.when |= BSC_FD_WRITE;
428 break;
429 }
430 if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
431 rc = -EINVAL;
432 goto out_free;
433 }
434 rc = rtp_decode(msg, rs->receive.callref, &new_msg);
435 if (rc < 0)
436 goto out_free;
437 msgb_free(msg);
438 msgb_enqueue(&rs->receive.net->upqueue, new_msg);
439 break;
440
441 case RTP_NONE: /* if socket exists, but disabled by app */
442 msgb_free(msg);
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200443 break;
Harald Welteead7a7b2009-07-28 00:01:58 +0200444 }
445
Harald Welteda7ab742009-12-19 22:23:05 +0100446 return 0;
Harald Welte805f6442009-07-28 18:25:29 +0200447
448out_free:
449 msgb_free(msg);
450 return rc;
Harald Welteead7a7b2009-07-28 00:01:58 +0200451}
452
453/* write from tx_queue to RTP/RTCP socket */
454static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
455{
456 struct msgb *msg;
457 int written;
458
459 msg = msgb_dequeue(&rss->tx_queue);
460 if (!msg) {
461 rss->bfd.when &= ~BSC_FD_WRITE;
462 return 0;
463 }
464
465 written = write(rss->bfd.fd, msg->data, msg->len);
466 if (written < msg->len) {
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100467 LOGP(DMIB, LOGL_ERROR, "short write");
Harald Welteead7a7b2009-07-28 00:01:58 +0200468 msgb_free(msg);
469 return -EIO;
470 }
471
472 msgb_free(msg);
473
474 return 0;
475}
476
477
478/* callback for the select.c:bfd_* layer */
479static int rtp_bfd_cb(struct bsc_fd *bfd, unsigned int flags)
480{
481 struct rtp_socket *rs = bfd->data;
482 struct rtp_sub_socket *rss;
483
484 switch (bfd->priv_nr) {
485 case RTP_PRIV_RTP:
486 rss = &rs->rtp;
487 break;
488 case RTP_PRIV_RTCP:
489 rss = &rs->rtcp;
490 break;
491 default:
492 return -EINVAL;
493 }
494
495 if (flags & BSC_FD_READ)
496 rtp_socket_read(rs, rss);
497
498 if (flags & BSC_FD_WRITE)
499 rtp_socket_write(rs, rss);
500
501 return 0;
502}
503
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200504static void init_rss(struct rtp_sub_socket *rss,
Harald Welteead7a7b2009-07-28 00:01:58 +0200505 struct rtp_socket *rs, int fd, int priv_nr)
506{
507 /* initialize bfd */
508 rss->bfd.fd = fd;
509 rss->bfd.data = rs;
510 rss->bfd.priv_nr = priv_nr;
511 rss->bfd.cb = rtp_bfd_cb;
512}
513
514struct rtp_socket *rtp_socket_create(void)
515{
516 int rc;
517 struct rtp_socket *rs;
518
Harald Welte805f6442009-07-28 18:25:29 +0200519 DEBUGP(DMUX, "rtp_socket_create(): ");
520
Harald Welteead7a7b2009-07-28 00:01:58 +0200521 rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
522 if (!rs)
523 return NULL;
524
525 INIT_LLIST_HEAD(&rs->rtp.tx_queue);
526 INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
527
528 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
529 if (rc < 0)
530 goto out_free;
531
532 init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
533 rc = bsc_register_fd(&rs->rtp.bfd);
534 if (rc < 0)
535 goto out_rtp_socket;
536
537 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
538 if (rc < 0)
539 goto out_rtp_bfd;
540
541 init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
542 rc = bsc_register_fd(&rs->rtcp.bfd);
543 if (rc < 0)
544 goto out_rtcp_socket;
545
Harald Welte805f6442009-07-28 18:25:29 +0200546 DEBUGPC(DMUX, "success\n");
547
548 rc = rtp_socket_bind(rs, INADDR_ANY);
549 if (rc < 0)
550 goto out_rtcp_bfd;
551
Harald Welteead7a7b2009-07-28 00:01:58 +0200552 return rs;
553
Harald Welte805f6442009-07-28 18:25:29 +0200554out_rtcp_bfd:
555 bsc_unregister_fd(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200556out_rtcp_socket:
557 close(rs->rtcp.bfd.fd);
558out_rtp_bfd:
559 bsc_unregister_fd(&rs->rtp.bfd);
560out_rtp_socket:
561 close(rs->rtp.bfd.fd);
562out_free:
563 talloc_free(rs);
Harald Welte805f6442009-07-28 18:25:29 +0200564 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200565 return NULL;
566}
567
568static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, u_int32_t ip,
569 u_int16_t port)
570{
Harald Welte805f6442009-07-28 18:25:29 +0200571 int rc;
572 socklen_t alen = sizeof(rss->sin_local);
573
Harald Welteead7a7b2009-07-28 00:01:58 +0200574 rss->sin_local.sin_family = AF_INET;
575 rss->sin_local.sin_addr.s_addr = htonl(ip);
576 rss->sin_local.sin_port = htons(port);
577 rss->bfd.when |= BSC_FD_READ;
578
Harald Welte805f6442009-07-28 18:25:29 +0200579 rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
580 sizeof(rss->sin_local));
581 if (rc < 0)
582 return rc;
583
584 /* retrieve the address we actually bound to, in case we
585 * passed INADDR_ANY as IP address */
586 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
587 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200588}
589
590#define RTP_PORT_BASE 30000
Harald Welte805f6442009-07-28 18:25:29 +0200591static unsigned int next_udp_port = RTP_PORT_BASE;
Harald Welteead7a7b2009-07-28 00:01:58 +0200592
593/* bind a RTP socket to a local address */
594int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip)
595{
Harald Welte805f6442009-07-28 18:25:29 +0200596 int rc = -EIO;
597 struct in_addr ia;
598
599 ia.s_addr = htonl(ip);
600 DEBUGP(DMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
601 inet_ntoa(ia));
Harald Welteead7a7b2009-07-28 00:01:58 +0200602
603 /* try to bind to a consecutive pair of ports */
Harald Welte805f6442009-07-28 18:25:29 +0200604 for (next_udp_port = next_udp_port % 0xffff;
605 next_udp_port < 0xffff; next_udp_port += 2) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200606 rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
607 if (rc != 0)
608 continue;
609
610 rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
611 if (rc == 0)
612 break;
613 }
Harald Welte805f6442009-07-28 18:25:29 +0200614 if (rc < 0) {
615 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200616 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200617 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200618
Harald Welte805f6442009-07-28 18:25:29 +0200619 ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
620 DEBUGPC(DMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
621 inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
Harald Welteead7a7b2009-07-28 00:01:58 +0200622 return ntohs(rs->rtp.sin_local.sin_port);
623}
624
625static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
626 u_int32_t ip, u_int16_t port)
627{
Harald Welte805f6442009-07-28 18:25:29 +0200628 int rc;
629 socklen_t alen = sizeof(rss->sin_local);
630
Harald Welteead7a7b2009-07-28 00:01:58 +0200631 rss->sin_remote.sin_family = AF_INET;
632 rss->sin_remote.sin_addr.s_addr = htonl(ip);
633 rss->sin_remote.sin_port = htons(port);
634
Harald Welte805f6442009-07-28 18:25:29 +0200635 rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
636 sizeof(rss->sin_remote));
637 if (rc < 0)
638 return rc;
639
640 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
641 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200642}
643
644/* 'connect' a RTP socket to a remote peer */
645int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port)
646{
647 int rc;
Harald Welte805f6442009-07-28 18:25:29 +0200648 struct in_addr ia;
649
650 ia.s_addr = htonl(ip);
651 DEBUGP(DMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
652 rs, inet_ntoa(ia), port);
Harald Welteead7a7b2009-07-28 00:01:58 +0200653
654 rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
655 if (rc < 0)
656 return rc;
657
658 return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
659}
660
661/* bind two RTP/RTCP sockets together */
662int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
663{
Harald Welte805f6442009-07-28 18:25:29 +0200664 DEBUGP(DMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
665 this, other);
666
Harald Welteead7a7b2009-07-28 00:01:58 +0200667 this->rx_action = RTP_PROXY;
668 this->proxy.other_sock = other;
669
670 other->rx_action = RTP_PROXY;
671 other->proxy.other_sock = this;
672
673 return 0;
674}
675
Harald Welteda7ab742009-12-19 22:23:05 +0100676/* bind RTP/RTCP socket to application */
Harald Welte9fb1f102009-12-20 17:07:23 +0100677int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
678 u_int32_t callref)
Harald Welteda7ab742009-12-19 22:23:05 +0100679{
Harald Welte9fb1f102009-12-20 17:07:23 +0100680 DEBUGP(DMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100681 this, callref);
682
683 if (callref) {
684 this->rx_action = RTP_RECV_UPSTREAM;
685 this->receive.net = net;
686 this->receive.callref = callref;
687 } else
688 this->rx_action = RTP_NONE;
689
690 return 0;
691}
692
Harald Welteead7a7b2009-07-28 00:01:58 +0200693static void free_tx_queue(struct rtp_sub_socket *rss)
694{
695 struct msgb *msg;
696
697 while ((msg = msgb_dequeue(&rss->tx_queue)))
698 msgb_free(msg);
699}
700
701int rtp_socket_free(struct rtp_socket *rs)
702{
Harald Welte805f6442009-07-28 18:25:29 +0200703 DEBUGP(DMUX, "rtp_socket_free(rs=%p)\n", rs);
Harald Welteead7a7b2009-07-28 00:01:58 +0200704
705 /* make sure we don't leave references dangling to us */
706 if (rs->rx_action == RTP_PROXY &&
707 rs->proxy.other_sock)
708 rs->proxy.other_sock->proxy.other_sock = NULL;
709
710 bsc_unregister_fd(&rs->rtp.bfd);
711 close(rs->rtp.bfd.fd);
712 free_tx_queue(&rs->rtp);
713
714 bsc_unregister_fd(&rs->rtcp.bfd);
715 close(rs->rtcp.bfd.fd);
716 free_tx_queue(&rs->rtcp);
717
718 talloc_free(rs);
719
720 return 0;
721}