blob: 4c5f12eb529bf386e8dd2e2f003591b4fa2e0b68 [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
Harald Welte9af6ddf2011-01-01 15:25:50 +01007 * 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
Harald Welteead7a7b2009-07-28 00:01:58 +02009 * (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
Harald Welte9af6ddf2011-01-01 15:25:50 +010014 * GNU Affero General Public License for more details.
Harald Welteead7a7b2009-07-28 00:01:58 +020015 *
Harald Welte9af6ddf2011-01-01 15:25:50 +010016 * 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/>.
Harald Welteead7a7b2009-07-28 00:01:58 +020018 *
19 */
20
21#include <errno.h>
22#include <unistd.h>
23#include <sys/socket.h>
24#include <netinet/in.h>
Harald Welte805f6442009-07-28 18:25:29 +020025#include <arpa/inet.h>
Harald Welteda7ab742009-12-19 22:23:05 +010026#include <sys/time.h> /* gettimeofday() */
27#include <unistd.h> /* get..() */
28#include <time.h> /* clock() */
29#include <sys/utsname.h> /* uname() */
Harald Welteead7a7b2009-07-28 00:01:58 +020030
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010031#include <osmocom/core/talloc.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020032#include <openbsc/gsm_data.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010033#include <osmocom/core/msgb.h>
34#include <osmocom/core/select.h>
Harald Welte805f6442009-07-28 18:25:29 +020035#include <openbsc/debug.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020036#include <openbsc/rtp_proxy.h>
Harald Weltef142c972011-05-24 13:25:38 +020037#include <openbsc/mncc.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020038
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020039/* attempt to determine byte order */
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020040#include <sys/param.h>
41#include <limits.h>
42
43#ifndef __BYTE_ORDER
44#error "__BYTE_ORDER should be defined by someone"
45#endif
46
Harald Welteead7a7b2009-07-28 00:01:58 +020047static LLIST_HEAD(rtp_sockets);
48
Harald Welte805f6442009-07-28 18:25:29 +020049/* should we mangle the CNAME inside SDES of RTCP packets? We disable
50 * this by default, as it seems to be not needed */
51static int mangle_rtcp_cname = 0;
52
Harald Welteead7a7b2009-07-28 00:01:58 +020053enum rtp_bfd_priv {
54 RTP_PRIV_NONE,
55 RTP_PRIV_RTP,
56 RTP_PRIV_RTCP
57};
58
59#define RTP_ALLOC_SIZE 1500
60
Harald Welte805f6442009-07-28 18:25:29 +020061/* according to RFC 1889 */
62struct rtcp_hdr {
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020063 uint8_t byte0;
64 uint8_t type;
65 uint16_t length;
Harald Welte805f6442009-07-28 18:25:29 +020066} __attribute__((packed));
67
68#define RTCP_TYPE_SDES 202
69
70#define RTCP_IE_CNAME 1
71
Harald Welteda7ab742009-12-19 22:23:05 +010072/* according to RFC 3550 */
73struct rtp_hdr {
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010074#if __BYTE_ORDER == __LITTLE_ENDIAN
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020075 uint8_t csrc_count:4,
Harald Welteda7ab742009-12-19 22:23:05 +010076 extension:1,
77 padding:1,
78 version:2;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020079 uint8_t payload_type:7,
Harald Welteda7ab742009-12-19 22:23:05 +010080 marker:1;
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010081#elif __BYTE_ORDER == __BIG_ENDIAN
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020082 uint8_t version:2,
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010083 padding:1,
84 extension:1,
85 csrc_count:4;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020086 uint8_t marker:1,
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010087 payload_type:7;
88#endif
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020089 uint16_t sequence;
90 uint32_t timestamp;
91 uint32_t ssrc;
Harald Welteda7ab742009-12-19 22:23:05 +010092} __attribute__((packed));
93
94struct rtp_x_hdr {
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020095 uint16_t by_profile;
96 uint16_t length;
Harald Welteda7ab742009-12-19 22:23:05 +010097} __attribute__((packed));
98
99#define RTP_VERSION 2
100
Harald Welteda7ab742009-12-19 22:23:05 +0100101/* decode an rtp frame and create a new buffer with payload */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200102static int rtp_decode(struct msgb *msg, uint32_t callref, struct msgb **data)
Harald Welteda7ab742009-12-19 22:23:05 +0100103{
104 struct msgb *new_msg;
105 struct gsm_data_frame *frame;
106 struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
107 struct rtp_x_hdr *rtpxh;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200108 uint8_t *payload;
Harald Welteda7ab742009-12-19 22:23:05 +0100109 int payload_len;
110 int msg_type;
111 int x_len;
112
113 if (msg->len < 12) {
114 DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n",
115 msg->len);
116 return -EINVAL;
117 }
118 if (rtph->version != RTP_VERSION) {
119 DEBUGPC(DMUX, "received RTP version %d not supported.\n",
120 rtph->version);
121 return -EINVAL;
122 }
123 payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
124 payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
125 if (payload_len < 0) {
126 DEBUGPC(DMUX, "received RTP frame too short (len = %d, "
127 "csrc count = %d)\n", msg->len, rtph->csrc_count);
128 return -EINVAL;
129 }
130 if (rtph->extension) {
131 if (payload_len < sizeof(struct rtp_x_hdr)) {
132 DEBUGPC(DMUX, "received RTP frame too short for "
133 "extension header\n");
134 return -EINVAL;
135 }
136 rtpxh = (struct rtp_x_hdr *)payload;
137 x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
138 payload += x_len;
139 payload_len -= x_len;
140 if (payload_len < 0) {
141 DEBUGPC(DMUX, "received RTP frame too short, "
142 "extension header exceeds frame length\n");
143 return -EINVAL;
144 }
145 }
146 if (rtph->padding) {
147 if (payload_len < 0) {
148 DEBUGPC(DMUX, "received RTP frame too short for "
149 "padding length\n");
150 return -EINVAL;
151 }
152 payload_len -= payload[payload_len - 1];
153 if (payload_len < 0) {
154 DEBUGPC(DMUX, "received RTP frame with padding "
155 "greater than payload\n");
156 return -EINVAL;
157 }
158 }
159
160 switch (rtph->payload_type) {
161 case RTP_PT_GSM_FULL:
162 msg_type = GSM_TCHF_FRAME;
163 if (payload_len != 33) {
164 DEBUGPC(DMUX, "received RTP full rate frame with "
165 "payload length != 32 (len = %d)\n",
166 payload_len);
167 return -EINVAL;
168 }
169 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100170 case RTP_PT_GSM_EFR:
171 msg_type = GSM_TCHF_FRAME_EFR;
172 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100173 default:
174 DEBUGPC(DMUX, "received RTP frame with unknown payload "
175 "type %d\n", rtph->payload_type);
176 return -EINVAL;
177 }
178
179 new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len,
180 "GSM-DATA");
181 if (!new_msg)
182 return -ENOMEM;
183 frame = (struct gsm_data_frame *)(new_msg->data);
184 frame->msg_type = msg_type;
185 frame->callref = callref;
186 memcpy(frame->data, payload, payload_len);
187 msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len);
188
189 *data = new_msg;
190 return 0;
191}
192
Harald Welte392736d2009-12-20 13:16:14 +0100193/* "to - from" */
194static void tv_difference(struct timeval *diff, const struct timeval *from,
195 const struct timeval *__to)
196{
197 struct timeval _to = *__to, *to = &_to;
198
199 if (to->tv_usec < from->tv_usec) {
200 to->tv_sec -= 1;
201 to->tv_usec += 1000000;
202 }
203
204 diff->tv_usec = to->tv_usec - from->tv_usec;
205 diff->tv_sec = to->tv_sec - from->tv_sec;
206}
207
Harald Welteda7ab742009-12-19 22:23:05 +0100208/* encode and send a rtp frame */
209int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
210{
211 struct rtp_sub_socket *rss = &rs->rtp;
212 struct msgb *msg;
213 struct rtp_hdr *rtph;
214 int payload_type;
215 int payload_len;
216 int duration; /* in samples */
217
218 if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
219 /* initialize sequences */
220 rs->tx_action = RTP_SEND_DOWNSTREAM;
221 rs->transmit.ssrc = rand();
222 rs->transmit.sequence = random();
223 rs->transmit.timestamp = random();
224 }
225
226 switch (frame->msg_type) {
227 case GSM_TCHF_FRAME:
228 payload_type = RTP_PT_GSM_FULL;
229 payload_len = 33;
230 duration = 160;
231 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100232 case GSM_TCHF_FRAME_EFR:
233 payload_type = RTP_PT_GSM_EFR;
234 payload_len = 31;
235 duration = 160;
236 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100237 default:
238 DEBUGPC(DMUX, "unsupported message type %d\n",
239 frame->msg_type);
240 return -EINVAL;
241 }
242
Harald Welte392736d2009-12-20 13:16:14 +0100243 {
244 struct timeval tv, tv_diff;
245 long int usec_diff, frame_diff;
246
247 gettimeofday(&tv, NULL);
248 tv_difference(&tv_diff, &rs->transmit.last_tv, &tv);
249 rs->transmit.last_tv = tv;
250
251 usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
252 frame_diff = (usec_diff / 20000);
253
254 if (abs(frame_diff) > 1) {
255 long int frame_diff_excess = frame_diff - 1;
256
Harald Welte (local)9fcf6d72009-12-27 17:01:40 +0100257 LOGP(DMUX, LOGL_NOTICE,
258 "Correcting frame difference of %ld frames\n", frame_diff_excess);
Harald Welte392736d2009-12-20 13:16:14 +0100259 rs->transmit.sequence += frame_diff_excess;
260 rs->transmit.timestamp += frame_diff_excess * duration;
261 }
262 }
263
Harald Welteda7ab742009-12-19 22:23:05 +0100264 msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
265 if (!msg)
266 return -ENOMEM;
267 rtph = (struct rtp_hdr *)msg->data;
268 rtph->version = RTP_VERSION;
269 rtph->padding = 0;
270 rtph->extension = 0;
271 rtph->csrc_count = 0;
272 rtph->marker = 0;
273 rtph->payload_type = payload_type;
274 rtph->sequence = htons(rs->transmit.sequence++);
275 rtph->timestamp = htonl(rs->transmit.timestamp);
276 rs->transmit.timestamp += duration;
277 rtph->ssrc = htonl(rs->transmit.ssrc);
278 memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len);
279 msgb_put(msg, sizeof(struct rtp_hdr) + payload_len);
280 msgb_enqueue(&rss->tx_queue, msg);
281 rss->bfd.when |= BSC_FD_WRITE;
282
283 return 0;
284}
285
Holger Hans Peter Freyther89acf062009-07-29 06:46:26 +0200286/* iterate over all chunks in one RTCP message, look for CNAME IEs and
Harald Welte805f6442009-07-28 18:25:29 +0200287 * replace all of those with 'new_cname' */
288static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200289 uint16_t *rtcp_len, const char *new_cname)
Harald Welte805f6442009-07-28 18:25:29 +0200290{
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200291 uint8_t *rtcp_end;
292 uint8_t *cur = (uint8_t *) rh;
293 uint8_t tag, len = 0;
Harald Welte805f6442009-07-28 18:25:29 +0200294
295 rtcp_end = cur + *rtcp_len;
296 /* move cur to end of RTP header */
Harald Welte198f3f52009-07-29 10:46:41 +0200297 cur += sizeof(*rh);
Harald Welte805f6442009-07-28 18:25:29 +0200298
299 /* iterate over Chunks */
300 while (cur+4 < rtcp_end) {
301 /* skip four bytes SSRC/CSRC */
302 cur += 4;
303
304 /* iterate over IE's inside the chunk */
305 while (cur+1 < rtcp_end) {
306 tag = *cur++;
307 if (tag == 0) {
308 /* end of chunk, skip additional zero */
309 while (*cur++ == 0) { }
310 break;
311 }
312 len = *cur++;
313
314 if (tag == RTCP_IE_CNAME) {
315 /* we've found the CNAME, lets mangle it */
316 if (len < strlen(new_cname)) {
317 /* we need to make more space */
318 int increase = strlen(new_cname) - len;
319
320 msgb_push(msg, increase);
321 memmove(cur+len+increase, cur+len,
322 rtcp_end - (cur+len));
323 /* FIXME: we have to respect RTCP
324 * padding/alignment rules! */
325 len += increase;
326 *(cur-1) += increase;
327 rtcp_end += increase;
328 *rtcp_len += increase;
329 }
330 /* copy new CNAME into message */
331 memcpy(cur, new_cname, strlen(new_cname));
332 /* FIXME: zero the padding in case new CNAME
333 * is smaller than old one !!! */
334 }
335 cur += len;
336 }
337 }
338
339 return 0;
340}
341
342static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
343{
344 struct rtp_sub_socket *rss = &rs->rtcp;
345 struct rtcp_hdr *rtph;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200346 uint16_t old_len;
Harald Welte805f6442009-07-28 18:25:29 +0200347 int rc;
348
349 if (!mangle_rtcp_cname)
350 return 0;
351
Harald Welteda7ab742009-12-19 22:23:05 +0100352 printf("RTCP\n");
Harald Welte805f6442009-07-28 18:25:29 +0200353 /* iterate over list of RTCP messages */
354 rtph = (struct rtcp_hdr *)msg->data;
Harald Welteda7ab742009-12-19 22:23:05 +0100355 while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
Harald Welte805f6442009-07-28 18:25:29 +0200356 old_len = (ntohs(rtph->length) + 1) * 4;
Harald Welteda7ab742009-12-19 22:23:05 +0100357 if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
358 DEBUGPC(DMUX, "received RTCP packet too short for "
359 "length element\n");
360 return -EINVAL;
361 }
Harald Welte805f6442009-07-28 18:25:29 +0200362 if (rtph->type == RTCP_TYPE_SDES) {
363 char new_cname[255];
364 strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
365 sizeof(new_cname));
366 new_cname[sizeof(new_cname)-1] = '\0';
367 rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
368 new_cname);
369 if (rc < 0)
370 return rc;
371 }
372 rtph = (void *)rtph + old_len;
373 }
374
375 return 0;
376}
377
Harald Welteead7a7b2009-07-28 00:01:58 +0200378/* read from incoming RTP/RTCP socket */
379static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
380{
381 int rc;
382 struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
Harald Welteda7ab742009-12-19 22:23:05 +0100383 struct msgb *new_msg;
Harald Welteead7a7b2009-07-28 00:01:58 +0200384 struct rtp_sub_socket *other_rss;
385
386 if (!msg)
387 return -ENOMEM;
388
389 rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
390 if (rc <= 0) {
391 rss->bfd.when &= ~BSC_FD_READ;
392 return rc;
393 }
394
395 msgb_put(msg, rc);
396
397 switch (rs->rx_action) {
398 case RTP_PROXY:
Harald Welte805f6442009-07-28 18:25:29 +0200399 if (!rs->proxy.other_sock) {
400 rc = -EIO;
401 goto out_free;
402 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200403 if (rss->bfd.priv_nr == RTP_PRIV_RTP)
404 other_rss = &rs->proxy.other_sock->rtp;
Harald Welte805f6442009-07-28 18:25:29 +0200405 else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200406 other_rss = &rs->proxy.other_sock->rtcp;
Harald Welte805f6442009-07-28 18:25:29 +0200407 /* modify RTCP SDES CNAME */
408 rc = rtcp_mangle(msg, rs);
409 if (rc < 0)
410 goto out_free;
411 } else {
412 rc = -EINVAL;
413 goto out_free;
Harald Welteead7a7b2009-07-28 00:01:58 +0200414 }
415 msgb_enqueue(&other_rss->tx_queue, msg);
416 other_rss->bfd.when |= BSC_FD_WRITE;
417 break;
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200418
419 case RTP_RECV_UPSTREAM:
Harald Welteda7ab742009-12-19 22:23:05 +0100420 if (!rs->receive.callref || !rs->receive.net) {
421 rc = -EIO;
422 goto out_free;
423 }
424 if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
425 if (!mangle_rtcp_cname) {
426 msgb_free(msg);
427 break;
428 }
429 /* modify RTCP SDES CNAME */
430 rc = rtcp_mangle(msg, rs);
431 if (rc < 0)
432 goto out_free;
433 msgb_enqueue(&rss->tx_queue, msg);
434 rss->bfd.when |= BSC_FD_WRITE;
435 break;
436 }
437 if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
438 rc = -EINVAL;
439 goto out_free;
440 }
441 rc = rtp_decode(msg, rs->receive.callref, &new_msg);
442 if (rc < 0)
443 goto out_free;
444 msgb_free(msg);
Harald Welte31c00f72011-03-03 23:29:05 +0100445 trau_tx_to_mncc(rs->receive.net, new_msg);
Harald Welteda7ab742009-12-19 22:23:05 +0100446 break;
447
448 case RTP_NONE: /* if socket exists, but disabled by app */
449 msgb_free(msg);
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200450 break;
Harald Welteead7a7b2009-07-28 00:01:58 +0200451 }
452
Harald Welteda7ab742009-12-19 22:23:05 +0100453 return 0;
Harald Welte805f6442009-07-28 18:25:29 +0200454
455out_free:
456 msgb_free(msg);
457 return rc;
Harald Welteead7a7b2009-07-28 00:01:58 +0200458}
459
460/* write from tx_queue to RTP/RTCP socket */
461static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
462{
463 struct msgb *msg;
464 int written;
465
466 msg = msgb_dequeue(&rss->tx_queue);
467 if (!msg) {
468 rss->bfd.when &= ~BSC_FD_WRITE;
469 return 0;
470 }
471
472 written = write(rss->bfd.fd, msg->data, msg->len);
473 if (written < msg->len) {
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100474 LOGP(DMIB, LOGL_ERROR, "short write");
Harald Welteead7a7b2009-07-28 00:01:58 +0200475 msgb_free(msg);
476 return -EIO;
477 }
478
479 msgb_free(msg);
480
481 return 0;
482}
483
484
485/* callback for the select.c:bfd_* layer */
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200486static int rtp_bfd_cb(struct osmo_fd *bfd, unsigned int flags)
Harald Welteead7a7b2009-07-28 00:01:58 +0200487{
488 struct rtp_socket *rs = bfd->data;
489 struct rtp_sub_socket *rss;
490
491 switch (bfd->priv_nr) {
492 case RTP_PRIV_RTP:
493 rss = &rs->rtp;
494 break;
495 case RTP_PRIV_RTCP:
496 rss = &rs->rtcp;
497 break;
498 default:
499 return -EINVAL;
500 }
501
502 if (flags & BSC_FD_READ)
503 rtp_socket_read(rs, rss);
504
505 if (flags & BSC_FD_WRITE)
506 rtp_socket_write(rs, rss);
507
508 return 0;
509}
510
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200511static void init_rss(struct rtp_sub_socket *rss,
Harald Welteead7a7b2009-07-28 00:01:58 +0200512 struct rtp_socket *rs, int fd, int priv_nr)
513{
514 /* initialize bfd */
515 rss->bfd.fd = fd;
516 rss->bfd.data = rs;
517 rss->bfd.priv_nr = priv_nr;
518 rss->bfd.cb = rtp_bfd_cb;
519}
520
521struct rtp_socket *rtp_socket_create(void)
522{
523 int rc;
524 struct rtp_socket *rs;
525
Harald Welte805f6442009-07-28 18:25:29 +0200526 DEBUGP(DMUX, "rtp_socket_create(): ");
527
Harald Welteead7a7b2009-07-28 00:01:58 +0200528 rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
529 if (!rs)
530 return NULL;
531
532 INIT_LLIST_HEAD(&rs->rtp.tx_queue);
533 INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
534
535 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
536 if (rc < 0)
537 goto out_free;
538
539 init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200540 rc = osmo_fd_register(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200541 if (rc < 0)
542 goto out_rtp_socket;
543
544 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
545 if (rc < 0)
546 goto out_rtp_bfd;
547
548 init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200549 rc = osmo_fd_register(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200550 if (rc < 0)
551 goto out_rtcp_socket;
552
Harald Welte805f6442009-07-28 18:25:29 +0200553 DEBUGPC(DMUX, "success\n");
554
555 rc = rtp_socket_bind(rs, INADDR_ANY);
556 if (rc < 0)
557 goto out_rtcp_bfd;
558
Harald Welteead7a7b2009-07-28 00:01:58 +0200559 return rs;
560
Harald Welte805f6442009-07-28 18:25:29 +0200561out_rtcp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200562 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200563out_rtcp_socket:
564 close(rs->rtcp.bfd.fd);
565out_rtp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200566 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200567out_rtp_socket:
568 close(rs->rtp.bfd.fd);
569out_free:
570 talloc_free(rs);
Harald Welte805f6442009-07-28 18:25:29 +0200571 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200572 return NULL;
573}
574
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200575static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, uint32_t ip,
576 uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200577{
Harald Welte805f6442009-07-28 18:25:29 +0200578 int rc;
579 socklen_t alen = sizeof(rss->sin_local);
580
Harald Welteead7a7b2009-07-28 00:01:58 +0200581 rss->sin_local.sin_family = AF_INET;
582 rss->sin_local.sin_addr.s_addr = htonl(ip);
583 rss->sin_local.sin_port = htons(port);
584 rss->bfd.when |= BSC_FD_READ;
585
Harald Welte805f6442009-07-28 18:25:29 +0200586 rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
587 sizeof(rss->sin_local));
588 if (rc < 0)
589 return rc;
590
591 /* retrieve the address we actually bound to, in case we
592 * passed INADDR_ANY as IP address */
593 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
594 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200595}
596
597#define RTP_PORT_BASE 30000
Harald Welte805f6442009-07-28 18:25:29 +0200598static unsigned int next_udp_port = RTP_PORT_BASE;
Harald Welteead7a7b2009-07-28 00:01:58 +0200599
600/* bind a RTP socket to a local address */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200601int rtp_socket_bind(struct rtp_socket *rs, uint32_t ip)
Harald Welteead7a7b2009-07-28 00:01:58 +0200602{
Harald Welte805f6442009-07-28 18:25:29 +0200603 int rc = -EIO;
604 struct in_addr ia;
605
606 ia.s_addr = htonl(ip);
607 DEBUGP(DMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
608 inet_ntoa(ia));
Harald Welteead7a7b2009-07-28 00:01:58 +0200609
610 /* try to bind to a consecutive pair of ports */
Harald Welte805f6442009-07-28 18:25:29 +0200611 for (next_udp_port = next_udp_port % 0xffff;
612 next_udp_port < 0xffff; next_udp_port += 2) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200613 rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
614 if (rc != 0)
615 continue;
616
617 rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
618 if (rc == 0)
619 break;
620 }
Harald Welte805f6442009-07-28 18:25:29 +0200621 if (rc < 0) {
622 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200623 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200624 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200625
Harald Welte805f6442009-07-28 18:25:29 +0200626 ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
627 DEBUGPC(DMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
628 inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
Harald Welteead7a7b2009-07-28 00:01:58 +0200629 return ntohs(rs->rtp.sin_local.sin_port);
630}
631
632static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200633 uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200634{
Harald Welte805f6442009-07-28 18:25:29 +0200635 int rc;
636 socklen_t alen = sizeof(rss->sin_local);
637
Harald Welteead7a7b2009-07-28 00:01:58 +0200638 rss->sin_remote.sin_family = AF_INET;
639 rss->sin_remote.sin_addr.s_addr = htonl(ip);
640 rss->sin_remote.sin_port = htons(port);
641
Harald Welte805f6442009-07-28 18:25:29 +0200642 rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
643 sizeof(rss->sin_remote));
644 if (rc < 0)
645 return rc;
646
647 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
648 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200649}
650
651/* 'connect' a RTP socket to a remote peer */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200652int rtp_socket_connect(struct rtp_socket *rs, uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200653{
654 int rc;
Harald Welte805f6442009-07-28 18:25:29 +0200655 struct in_addr ia;
656
657 ia.s_addr = htonl(ip);
658 DEBUGP(DMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
659 rs, inet_ntoa(ia), port);
Harald Welteead7a7b2009-07-28 00:01:58 +0200660
661 rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
662 if (rc < 0)
663 return rc;
664
665 return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
666}
667
668/* bind two RTP/RTCP sockets together */
669int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
670{
Harald Welte805f6442009-07-28 18:25:29 +0200671 DEBUGP(DMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
672 this, other);
673
Harald Welteead7a7b2009-07-28 00:01:58 +0200674 this->rx_action = RTP_PROXY;
675 this->proxy.other_sock = other;
676
677 other->rx_action = RTP_PROXY;
678 other->proxy.other_sock = this;
679
680 return 0;
681}
682
Harald Welteda7ab742009-12-19 22:23:05 +0100683/* bind RTP/RTCP socket to application */
Harald Welte9fb1f102009-12-20 17:07:23 +0100684int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200685 uint32_t callref)
Harald Welteda7ab742009-12-19 22:23:05 +0100686{
Harald Welte9fb1f102009-12-20 17:07:23 +0100687 DEBUGP(DMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100688 this, callref);
689
690 if (callref) {
691 this->rx_action = RTP_RECV_UPSTREAM;
692 this->receive.net = net;
693 this->receive.callref = callref;
694 } else
695 this->rx_action = RTP_NONE;
696
697 return 0;
698}
699
Harald Welteead7a7b2009-07-28 00:01:58 +0200700static void free_tx_queue(struct rtp_sub_socket *rss)
701{
702 struct msgb *msg;
703
704 while ((msg = msgb_dequeue(&rss->tx_queue)))
705 msgb_free(msg);
706}
707
708int rtp_socket_free(struct rtp_socket *rs)
709{
Harald Welte805f6442009-07-28 18:25:29 +0200710 DEBUGP(DMUX, "rtp_socket_free(rs=%p)\n", rs);
Harald Welteead7a7b2009-07-28 00:01:58 +0200711
712 /* make sure we don't leave references dangling to us */
713 if (rs->rx_action == RTP_PROXY &&
714 rs->proxy.other_sock)
715 rs->proxy.other_sock->proxy.other_sock = NULL;
716
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200717 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200718 close(rs->rtp.bfd.fd);
719 free_tx_queue(&rs->rtp);
720
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200721 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200722 close(rs->rtcp.bfd.fd);
723 free_tx_queue(&rs->rtcp);
724
725 talloc_free(rs);
726
727 return 0;
728}