blob: 86d562418d434ca07768c3451deac934de1d2f1d [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>
37
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020038/* attempt to determine byte order */
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020039#include <sys/param.h>
40#include <limits.h>
41
42#ifndef __BYTE_ORDER
43#error "__BYTE_ORDER should be defined by someone"
44#endif
45
Harald Welteead7a7b2009-07-28 00:01:58 +020046static LLIST_HEAD(rtp_sockets);
47
Harald Welte805f6442009-07-28 18:25:29 +020048/* should we mangle the CNAME inside SDES of RTCP packets? We disable
49 * this by default, as it seems to be not needed */
50static int mangle_rtcp_cname = 0;
51
Harald Welteead7a7b2009-07-28 00:01:58 +020052enum rtp_bfd_priv {
53 RTP_PRIV_NONE,
54 RTP_PRIV_RTP,
55 RTP_PRIV_RTCP
56};
57
58#define RTP_ALLOC_SIZE 1500
59
Harald Welte805f6442009-07-28 18:25:29 +020060/* according to RFC 1889 */
61struct rtcp_hdr {
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020062 uint8_t byte0;
63 uint8_t type;
64 uint16_t length;
Harald Welte805f6442009-07-28 18:25:29 +020065} __attribute__((packed));
66
67#define RTCP_TYPE_SDES 202
68
69#define RTCP_IE_CNAME 1
70
Harald Welteda7ab742009-12-19 22:23:05 +010071/* according to RFC 3550 */
72struct rtp_hdr {
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010073#if __BYTE_ORDER == __LITTLE_ENDIAN
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020074 uint8_t csrc_count:4,
Harald Welteda7ab742009-12-19 22:23:05 +010075 extension:1,
76 padding:1,
77 version:2;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020078 uint8_t payload_type:7,
Harald Welteda7ab742009-12-19 22:23:05 +010079 marker:1;
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010080#elif __BYTE_ORDER == __BIG_ENDIAN
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020081 uint8_t version:2,
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010082 padding:1,
83 extension:1,
84 csrc_count:4;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020085 uint8_t marker:1,
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010086 payload_type:7;
87#endif
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020088 uint16_t sequence;
89 uint32_t timestamp;
90 uint32_t ssrc;
Harald Welteda7ab742009-12-19 22:23:05 +010091} __attribute__((packed));
92
93struct rtp_x_hdr {
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020094 uint16_t by_profile;
95 uint16_t length;
Harald Welteda7ab742009-12-19 22:23:05 +010096} __attribute__((packed));
97
98#define RTP_VERSION 2
99
Harald Welteda7ab742009-12-19 22:23:05 +0100100/* decode an rtp frame and create a new buffer with payload */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200101static int rtp_decode(struct msgb *msg, uint32_t callref, struct msgb **data)
Harald Welteda7ab742009-12-19 22:23:05 +0100102{
103 struct msgb *new_msg;
104 struct gsm_data_frame *frame;
105 struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
106 struct rtp_x_hdr *rtpxh;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200107 uint8_t *payload;
Harald Welteda7ab742009-12-19 22:23:05 +0100108 int payload_len;
109 int msg_type;
110 int x_len;
111
112 if (msg->len < 12) {
113 DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n",
114 msg->len);
115 return -EINVAL;
116 }
117 if (rtph->version != RTP_VERSION) {
118 DEBUGPC(DMUX, "received RTP version %d not supported.\n",
119 rtph->version);
120 return -EINVAL;
121 }
122 payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
123 payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
124 if (payload_len < 0) {
125 DEBUGPC(DMUX, "received RTP frame too short (len = %d, "
126 "csrc count = %d)\n", msg->len, rtph->csrc_count);
127 return -EINVAL;
128 }
129 if (rtph->extension) {
130 if (payload_len < sizeof(struct rtp_x_hdr)) {
131 DEBUGPC(DMUX, "received RTP frame too short for "
132 "extension header\n");
133 return -EINVAL;
134 }
135 rtpxh = (struct rtp_x_hdr *)payload;
136 x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
137 payload += x_len;
138 payload_len -= x_len;
139 if (payload_len < 0) {
140 DEBUGPC(DMUX, "received RTP frame too short, "
141 "extension header exceeds frame length\n");
142 return -EINVAL;
143 }
144 }
145 if (rtph->padding) {
146 if (payload_len < 0) {
147 DEBUGPC(DMUX, "received RTP frame too short for "
148 "padding length\n");
149 return -EINVAL;
150 }
151 payload_len -= payload[payload_len - 1];
152 if (payload_len < 0) {
153 DEBUGPC(DMUX, "received RTP frame with padding "
154 "greater than payload\n");
155 return -EINVAL;
156 }
157 }
158
159 switch (rtph->payload_type) {
160 case RTP_PT_GSM_FULL:
161 msg_type = GSM_TCHF_FRAME;
162 if (payload_len != 33) {
163 DEBUGPC(DMUX, "received RTP full rate frame with "
164 "payload length != 32 (len = %d)\n",
165 payload_len);
166 return -EINVAL;
167 }
168 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100169 case RTP_PT_GSM_EFR:
170 msg_type = GSM_TCHF_FRAME_EFR;
171 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100172 default:
173 DEBUGPC(DMUX, "received RTP frame with unknown payload "
174 "type %d\n", rtph->payload_type);
175 return -EINVAL;
176 }
177
178 new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len,
179 "GSM-DATA");
180 if (!new_msg)
181 return -ENOMEM;
182 frame = (struct gsm_data_frame *)(new_msg->data);
183 frame->msg_type = msg_type;
184 frame->callref = callref;
185 memcpy(frame->data, payload, payload_len);
186 msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len);
187
188 *data = new_msg;
189 return 0;
190}
191
Harald Welte392736d2009-12-20 13:16:14 +0100192/* "to - from" */
193static void tv_difference(struct timeval *diff, const struct timeval *from,
194 const struct timeval *__to)
195{
196 struct timeval _to = *__to, *to = &_to;
197
198 if (to->tv_usec < from->tv_usec) {
199 to->tv_sec -= 1;
200 to->tv_usec += 1000000;
201 }
202
203 diff->tv_usec = to->tv_usec - from->tv_usec;
204 diff->tv_sec = to->tv_sec - from->tv_sec;
205}
206
Harald Welteda7ab742009-12-19 22:23:05 +0100207/* encode and send a rtp frame */
208int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
209{
210 struct rtp_sub_socket *rss = &rs->rtp;
211 struct msgb *msg;
212 struct rtp_hdr *rtph;
213 int payload_type;
214 int payload_len;
215 int duration; /* in samples */
216
217 if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
218 /* initialize sequences */
219 rs->tx_action = RTP_SEND_DOWNSTREAM;
220 rs->transmit.ssrc = rand();
221 rs->transmit.sequence = random();
222 rs->transmit.timestamp = random();
223 }
224
225 switch (frame->msg_type) {
226 case GSM_TCHF_FRAME:
227 payload_type = RTP_PT_GSM_FULL;
228 payload_len = 33;
229 duration = 160;
230 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100231 case GSM_TCHF_FRAME_EFR:
232 payload_type = RTP_PT_GSM_EFR;
233 payload_len = 31;
234 duration = 160;
235 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100236 default:
237 DEBUGPC(DMUX, "unsupported message type %d\n",
238 frame->msg_type);
239 return -EINVAL;
240 }
241
Harald Welte392736d2009-12-20 13:16:14 +0100242 {
243 struct timeval tv, tv_diff;
244 long int usec_diff, frame_diff;
245
246 gettimeofday(&tv, NULL);
247 tv_difference(&tv_diff, &rs->transmit.last_tv, &tv);
248 rs->transmit.last_tv = tv;
249
250 usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
251 frame_diff = (usec_diff / 20000);
252
253 if (abs(frame_diff) > 1) {
254 long int frame_diff_excess = frame_diff - 1;
255
Harald Welte (local)9fcf6d72009-12-27 17:01:40 +0100256 LOGP(DMUX, LOGL_NOTICE,
257 "Correcting frame difference of %ld frames\n", frame_diff_excess);
Harald Welte392736d2009-12-20 13:16:14 +0100258 rs->transmit.sequence += frame_diff_excess;
259 rs->transmit.timestamp += frame_diff_excess * duration;
260 }
261 }
262
Harald Welteda7ab742009-12-19 22:23:05 +0100263 msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
264 if (!msg)
265 return -ENOMEM;
266 rtph = (struct rtp_hdr *)msg->data;
267 rtph->version = RTP_VERSION;
268 rtph->padding = 0;
269 rtph->extension = 0;
270 rtph->csrc_count = 0;
271 rtph->marker = 0;
272 rtph->payload_type = payload_type;
273 rtph->sequence = htons(rs->transmit.sequence++);
274 rtph->timestamp = htonl(rs->transmit.timestamp);
275 rs->transmit.timestamp += duration;
276 rtph->ssrc = htonl(rs->transmit.ssrc);
277 memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len);
278 msgb_put(msg, sizeof(struct rtp_hdr) + payload_len);
279 msgb_enqueue(&rss->tx_queue, msg);
280 rss->bfd.when |= BSC_FD_WRITE;
281
282 return 0;
283}
284
Holger Hans Peter Freyther89acf062009-07-29 06:46:26 +0200285/* iterate over all chunks in one RTCP message, look for CNAME IEs and
Harald Welte805f6442009-07-28 18:25:29 +0200286 * replace all of those with 'new_cname' */
287static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200288 uint16_t *rtcp_len, const char *new_cname)
Harald Welte805f6442009-07-28 18:25:29 +0200289{
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200290 uint8_t *rtcp_end;
291 uint8_t *cur = (uint8_t *) rh;
292 uint8_t tag, len = 0;
Harald Welte805f6442009-07-28 18:25:29 +0200293
294 rtcp_end = cur + *rtcp_len;
295 /* move cur to end of RTP header */
Harald Welte198f3f52009-07-29 10:46:41 +0200296 cur += sizeof(*rh);
Harald Welte805f6442009-07-28 18:25:29 +0200297
298 /* iterate over Chunks */
299 while (cur+4 < rtcp_end) {
300 /* skip four bytes SSRC/CSRC */
301 cur += 4;
302
303 /* iterate over IE's inside the chunk */
304 while (cur+1 < rtcp_end) {
305 tag = *cur++;
306 if (tag == 0) {
307 /* end of chunk, skip additional zero */
308 while (*cur++ == 0) { }
309 break;
310 }
311 len = *cur++;
312
313 if (tag == RTCP_IE_CNAME) {
314 /* we've found the CNAME, lets mangle it */
315 if (len < strlen(new_cname)) {
316 /* we need to make more space */
317 int increase = strlen(new_cname) - len;
318
319 msgb_push(msg, increase);
320 memmove(cur+len+increase, cur+len,
321 rtcp_end - (cur+len));
322 /* FIXME: we have to respect RTCP
323 * padding/alignment rules! */
324 len += increase;
325 *(cur-1) += increase;
326 rtcp_end += increase;
327 *rtcp_len += increase;
328 }
329 /* copy new CNAME into message */
330 memcpy(cur, new_cname, strlen(new_cname));
331 /* FIXME: zero the padding in case new CNAME
332 * is smaller than old one !!! */
333 }
334 cur += len;
335 }
336 }
337
338 return 0;
339}
340
341static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
342{
343 struct rtp_sub_socket *rss = &rs->rtcp;
344 struct rtcp_hdr *rtph;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200345 uint16_t old_len;
Harald Welte805f6442009-07-28 18:25:29 +0200346 int rc;
347
348 if (!mangle_rtcp_cname)
349 return 0;
350
Harald Welteda7ab742009-12-19 22:23:05 +0100351 printf("RTCP\n");
Harald Welte805f6442009-07-28 18:25:29 +0200352 /* iterate over list of RTCP messages */
353 rtph = (struct rtcp_hdr *)msg->data;
Harald Welteda7ab742009-12-19 22:23:05 +0100354 while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
Harald Welte805f6442009-07-28 18:25:29 +0200355 old_len = (ntohs(rtph->length) + 1) * 4;
Harald Welteda7ab742009-12-19 22:23:05 +0100356 if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
357 DEBUGPC(DMUX, "received RTCP packet too short for "
358 "length element\n");
359 return -EINVAL;
360 }
Harald Welte805f6442009-07-28 18:25:29 +0200361 if (rtph->type == RTCP_TYPE_SDES) {
362 char new_cname[255];
363 strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
364 sizeof(new_cname));
365 new_cname[sizeof(new_cname)-1] = '\0';
366 rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
367 new_cname);
368 if (rc < 0)
369 return rc;
370 }
371 rtph = (void *)rtph + old_len;
372 }
373
374 return 0;
375}
376
Harald Welteead7a7b2009-07-28 00:01:58 +0200377/* read from incoming RTP/RTCP socket */
378static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
379{
380 int rc;
381 struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
Harald Welteda7ab742009-12-19 22:23:05 +0100382 struct msgb *new_msg;
Harald Welteead7a7b2009-07-28 00:01:58 +0200383 struct rtp_sub_socket *other_rss;
384
385 if (!msg)
386 return -ENOMEM;
387
388 rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
389 if (rc <= 0) {
390 rss->bfd.when &= ~BSC_FD_READ;
391 return rc;
392 }
393
394 msgb_put(msg, rc);
395
396 switch (rs->rx_action) {
397 case RTP_PROXY:
Harald Welte805f6442009-07-28 18:25:29 +0200398 if (!rs->proxy.other_sock) {
399 rc = -EIO;
400 goto out_free;
401 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200402 if (rss->bfd.priv_nr == RTP_PRIV_RTP)
403 other_rss = &rs->proxy.other_sock->rtp;
Harald Welte805f6442009-07-28 18:25:29 +0200404 else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200405 other_rss = &rs->proxy.other_sock->rtcp;
Harald Welte805f6442009-07-28 18:25:29 +0200406 /* modify RTCP SDES CNAME */
407 rc = rtcp_mangle(msg, rs);
408 if (rc < 0)
409 goto out_free;
410 } else {
411 rc = -EINVAL;
412 goto out_free;
Harald Welteead7a7b2009-07-28 00:01:58 +0200413 }
414 msgb_enqueue(&other_rss->tx_queue, msg);
415 other_rss->bfd.when |= BSC_FD_WRITE;
416 break;
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200417
418 case RTP_RECV_UPSTREAM:
Harald Welteda7ab742009-12-19 22:23:05 +0100419 if (!rs->receive.callref || !rs->receive.net) {
420 rc = -EIO;
421 goto out_free;
422 }
423 if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
424 if (!mangle_rtcp_cname) {
425 msgb_free(msg);
426 break;
427 }
428 /* modify RTCP SDES CNAME */
429 rc = rtcp_mangle(msg, rs);
430 if (rc < 0)
431 goto out_free;
432 msgb_enqueue(&rss->tx_queue, msg);
433 rss->bfd.when |= BSC_FD_WRITE;
434 break;
435 }
436 if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
437 rc = -EINVAL;
438 goto out_free;
439 }
440 rc = rtp_decode(msg, rs->receive.callref, &new_msg);
441 if (rc < 0)
442 goto out_free;
443 msgb_free(msg);
Harald Welte31c00f72011-03-03 23:29:05 +0100444 trau_tx_to_mncc(rs->receive.net, new_msg);
Harald Welteda7ab742009-12-19 22:23:05 +0100445 break;
446
447 case RTP_NONE: /* if socket exists, but disabled by app */
448 msgb_free(msg);
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200449 break;
Harald Welteead7a7b2009-07-28 00:01:58 +0200450 }
451
Harald Welteda7ab742009-12-19 22:23:05 +0100452 return 0;
Harald Welte805f6442009-07-28 18:25:29 +0200453
454out_free:
455 msgb_free(msg);
456 return rc;
Harald Welteead7a7b2009-07-28 00:01:58 +0200457}
458
459/* write from tx_queue to RTP/RTCP socket */
460static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
461{
462 struct msgb *msg;
463 int written;
464
465 msg = msgb_dequeue(&rss->tx_queue);
466 if (!msg) {
467 rss->bfd.when &= ~BSC_FD_WRITE;
468 return 0;
469 }
470
471 written = write(rss->bfd.fd, msg->data, msg->len);
472 if (written < msg->len) {
Harald Welteb1d4c8e2009-12-17 23:10:46 +0100473 LOGP(DMIB, LOGL_ERROR, "short write");
Harald Welteead7a7b2009-07-28 00:01:58 +0200474 msgb_free(msg);
475 return -EIO;
476 }
477
478 msgb_free(msg);
479
480 return 0;
481}
482
483
484/* callback for the select.c:bfd_* layer */
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200485static int rtp_bfd_cb(struct osmo_fd *bfd, unsigned int flags)
Harald Welteead7a7b2009-07-28 00:01:58 +0200486{
487 struct rtp_socket *rs = bfd->data;
488 struct rtp_sub_socket *rss;
489
490 switch (bfd->priv_nr) {
491 case RTP_PRIV_RTP:
492 rss = &rs->rtp;
493 break;
494 case RTP_PRIV_RTCP:
495 rss = &rs->rtcp;
496 break;
497 default:
498 return -EINVAL;
499 }
500
501 if (flags & BSC_FD_READ)
502 rtp_socket_read(rs, rss);
503
504 if (flags & BSC_FD_WRITE)
505 rtp_socket_write(rs, rss);
506
507 return 0;
508}
509
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200510static void init_rss(struct rtp_sub_socket *rss,
Harald Welteead7a7b2009-07-28 00:01:58 +0200511 struct rtp_socket *rs, int fd, int priv_nr)
512{
513 /* initialize bfd */
514 rss->bfd.fd = fd;
515 rss->bfd.data = rs;
516 rss->bfd.priv_nr = priv_nr;
517 rss->bfd.cb = rtp_bfd_cb;
518}
519
520struct rtp_socket *rtp_socket_create(void)
521{
522 int rc;
523 struct rtp_socket *rs;
524
Harald Welte805f6442009-07-28 18:25:29 +0200525 DEBUGP(DMUX, "rtp_socket_create(): ");
526
Harald Welteead7a7b2009-07-28 00:01:58 +0200527 rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
528 if (!rs)
529 return NULL;
530
531 INIT_LLIST_HEAD(&rs->rtp.tx_queue);
532 INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
533
534 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
535 if (rc < 0)
536 goto out_free;
537
538 init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200539 rc = osmo_fd_register(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200540 if (rc < 0)
541 goto out_rtp_socket;
542
543 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
544 if (rc < 0)
545 goto out_rtp_bfd;
546
547 init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200548 rc = osmo_fd_register(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200549 if (rc < 0)
550 goto out_rtcp_socket;
551
Harald Welte805f6442009-07-28 18:25:29 +0200552 DEBUGPC(DMUX, "success\n");
553
554 rc = rtp_socket_bind(rs, INADDR_ANY);
555 if (rc < 0)
556 goto out_rtcp_bfd;
557
Harald Welteead7a7b2009-07-28 00:01:58 +0200558 return rs;
559
Harald Welte805f6442009-07-28 18:25:29 +0200560out_rtcp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200561 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200562out_rtcp_socket:
563 close(rs->rtcp.bfd.fd);
564out_rtp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200565 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200566out_rtp_socket:
567 close(rs->rtp.bfd.fd);
568out_free:
569 talloc_free(rs);
Harald Welte805f6442009-07-28 18:25:29 +0200570 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200571 return NULL;
572}
573
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200574static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, uint32_t ip,
575 uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200576{
Harald Welte805f6442009-07-28 18:25:29 +0200577 int rc;
578 socklen_t alen = sizeof(rss->sin_local);
579
Harald Welteead7a7b2009-07-28 00:01:58 +0200580 rss->sin_local.sin_family = AF_INET;
581 rss->sin_local.sin_addr.s_addr = htonl(ip);
582 rss->sin_local.sin_port = htons(port);
583 rss->bfd.when |= BSC_FD_READ;
584
Harald Welte805f6442009-07-28 18:25:29 +0200585 rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
586 sizeof(rss->sin_local));
587 if (rc < 0)
588 return rc;
589
590 /* retrieve the address we actually bound to, in case we
591 * passed INADDR_ANY as IP address */
592 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
593 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200594}
595
596#define RTP_PORT_BASE 30000
Harald Welte805f6442009-07-28 18:25:29 +0200597static unsigned int next_udp_port = RTP_PORT_BASE;
Harald Welteead7a7b2009-07-28 00:01:58 +0200598
599/* bind a RTP socket to a local address */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200600int rtp_socket_bind(struct rtp_socket *rs, uint32_t ip)
Harald Welteead7a7b2009-07-28 00:01:58 +0200601{
Harald Welte805f6442009-07-28 18:25:29 +0200602 int rc = -EIO;
603 struct in_addr ia;
604
605 ia.s_addr = htonl(ip);
606 DEBUGP(DMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
607 inet_ntoa(ia));
Harald Welteead7a7b2009-07-28 00:01:58 +0200608
609 /* try to bind to a consecutive pair of ports */
Harald Welte805f6442009-07-28 18:25:29 +0200610 for (next_udp_port = next_udp_port % 0xffff;
611 next_udp_port < 0xffff; next_udp_port += 2) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200612 rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
613 if (rc != 0)
614 continue;
615
616 rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
617 if (rc == 0)
618 break;
619 }
Harald Welte805f6442009-07-28 18:25:29 +0200620 if (rc < 0) {
621 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200622 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200623 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200624
Harald Welte805f6442009-07-28 18:25:29 +0200625 ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
626 DEBUGPC(DMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
627 inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
Harald Welteead7a7b2009-07-28 00:01:58 +0200628 return ntohs(rs->rtp.sin_local.sin_port);
629}
630
631static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200632 uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200633{
Harald Welte805f6442009-07-28 18:25:29 +0200634 int rc;
635 socklen_t alen = sizeof(rss->sin_local);
636
Harald Welteead7a7b2009-07-28 00:01:58 +0200637 rss->sin_remote.sin_family = AF_INET;
638 rss->sin_remote.sin_addr.s_addr = htonl(ip);
639 rss->sin_remote.sin_port = htons(port);
640
Harald Welte805f6442009-07-28 18:25:29 +0200641 rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
642 sizeof(rss->sin_remote));
643 if (rc < 0)
644 return rc;
645
646 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
647 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200648}
649
650/* 'connect' a RTP socket to a remote peer */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200651int rtp_socket_connect(struct rtp_socket *rs, uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200652{
653 int rc;
Harald Welte805f6442009-07-28 18:25:29 +0200654 struct in_addr ia;
655
656 ia.s_addr = htonl(ip);
657 DEBUGP(DMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
658 rs, inet_ntoa(ia), port);
Harald Welteead7a7b2009-07-28 00:01:58 +0200659
660 rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
661 if (rc < 0)
662 return rc;
663
664 return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
665}
666
667/* bind two RTP/RTCP sockets together */
668int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
669{
Harald Welte805f6442009-07-28 18:25:29 +0200670 DEBUGP(DMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
671 this, other);
672
Harald Welteead7a7b2009-07-28 00:01:58 +0200673 this->rx_action = RTP_PROXY;
674 this->proxy.other_sock = other;
675
676 other->rx_action = RTP_PROXY;
677 other->proxy.other_sock = this;
678
679 return 0;
680}
681
Harald Welteda7ab742009-12-19 22:23:05 +0100682/* bind RTP/RTCP socket to application */
Harald Welte9fb1f102009-12-20 17:07:23 +0100683int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200684 uint32_t callref)
Harald Welteda7ab742009-12-19 22:23:05 +0100685{
Harald Welte9fb1f102009-12-20 17:07:23 +0100686 DEBUGP(DMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100687 this, callref);
688
689 if (callref) {
690 this->rx_action = RTP_RECV_UPSTREAM;
691 this->receive.net = net;
692 this->receive.callref = callref;
693 } else
694 this->rx_action = RTP_NONE;
695
696 return 0;
697}
698
Harald Welteead7a7b2009-07-28 00:01:58 +0200699static void free_tx_queue(struct rtp_sub_socket *rss)
700{
701 struct msgb *msg;
702
703 while ((msg = msgb_dequeue(&rss->tx_queue)))
704 msgb_free(msg);
705}
706
707int rtp_socket_free(struct rtp_socket *rs)
708{
Harald Welte805f6442009-07-28 18:25:29 +0200709 DEBUGP(DMUX, "rtp_socket_free(rs=%p)\n", rs);
Harald Welteead7a7b2009-07-28 00:01:58 +0200710
711 /* make sure we don't leave references dangling to us */
712 if (rs->rx_action == RTP_PROXY &&
713 rs->proxy.other_sock)
714 rs->proxy.other_sock->proxy.other_sock = NULL;
715
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200716 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200717 close(rs->rtp.bfd.fd);
718 free_tx_queue(&rs->rtp);
719
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200720 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200721 close(rs->rtcp.bfd.fd);
722 free_tx_queue(&rs->rtcp);
723
724 talloc_free(rs);
725
726 return 0;
727}