blob: 3d34ac6f6c625499d2c0f93b6daebfe6aee9f2ba [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>
Alexander Huemer0a654612011-09-06 00:09:49 +020038#include <openbsc/trau_upqueue.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020039
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020040/* attempt to determine byte order */
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020041#include <sys/param.h>
42#include <limits.h>
43
44#ifndef __BYTE_ORDER
45#error "__BYTE_ORDER should be defined by someone"
46#endif
47
Harald Welteead7a7b2009-07-28 00:01:58 +020048static LLIST_HEAD(rtp_sockets);
49
Harald Welte805f6442009-07-28 18:25:29 +020050/* should we mangle the CNAME inside SDES of RTCP packets? We disable
51 * this by default, as it seems to be not needed */
52static int mangle_rtcp_cname = 0;
53
Harald Welteead7a7b2009-07-28 00:01:58 +020054enum rtp_bfd_priv {
55 RTP_PRIV_NONE,
56 RTP_PRIV_RTP,
57 RTP_PRIV_RTCP
58};
59
60#define RTP_ALLOC_SIZE 1500
61
Harald Welte805f6442009-07-28 18:25:29 +020062/* according to RFC 1889 */
63struct rtcp_hdr {
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020064 uint8_t byte0;
65 uint8_t type;
66 uint16_t length;
Harald Welte805f6442009-07-28 18:25:29 +020067} __attribute__((packed));
68
69#define RTCP_TYPE_SDES 202
70
71#define RTCP_IE_CNAME 1
72
Harald Welteda7ab742009-12-19 22:23:05 +010073/* according to RFC 3550 */
74struct rtp_hdr {
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010075#if __BYTE_ORDER == __LITTLE_ENDIAN
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020076 uint8_t csrc_count:4,
Harald Welteda7ab742009-12-19 22:23:05 +010077 extension:1,
78 padding:1,
79 version:2;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020080 uint8_t payload_type:7,
Harald Welteda7ab742009-12-19 22:23:05 +010081 marker:1;
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010082#elif __BYTE_ORDER == __BIG_ENDIAN
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020083 uint8_t version:2,
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010084 padding:1,
85 extension:1,
86 csrc_count:4;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020087 uint8_t marker:1,
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010088 payload_type:7;
89#endif
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020090 uint16_t sequence;
91 uint32_t timestamp;
92 uint32_t ssrc;
Harald Welteda7ab742009-12-19 22:23:05 +010093} __attribute__((packed));
94
95struct rtp_x_hdr {
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020096 uint16_t by_profile;
97 uint16_t length;
Harald Welteda7ab742009-12-19 22:23:05 +010098} __attribute__((packed));
99
100#define RTP_VERSION 2
101
Harald Welteda7ab742009-12-19 22:23:05 +0100102/* decode an rtp frame and create a new buffer with payload */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200103static int rtp_decode(struct msgb *msg, uint32_t callref, struct msgb **data)
Harald Welteda7ab742009-12-19 22:23:05 +0100104{
105 struct msgb *new_msg;
106 struct gsm_data_frame *frame;
107 struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
108 struct rtp_x_hdr *rtpxh;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200109 uint8_t *payload;
Harald Welteda7ab742009-12-19 22:23:05 +0100110 int payload_len;
111 int msg_type;
112 int x_len;
113
114 if (msg->len < 12) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200115 DEBUGPC(DLMUX, "received RTP frame too short (len = %d)\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100116 msg->len);
117 return -EINVAL;
118 }
119 if (rtph->version != RTP_VERSION) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200120 DEBUGPC(DLMUX, "received RTP version %d not supported.\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100121 rtph->version);
122 return -EINVAL;
123 }
124 payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
125 payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
126 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200127 DEBUGPC(DLMUX, "received RTP frame too short (len = %d, "
Harald Welteda7ab742009-12-19 22:23:05 +0100128 "csrc count = %d)\n", msg->len, rtph->csrc_count);
129 return -EINVAL;
130 }
131 if (rtph->extension) {
132 if (payload_len < sizeof(struct rtp_x_hdr)) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200133 DEBUGPC(DLMUX, "received RTP frame too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100134 "extension header\n");
135 return -EINVAL;
136 }
137 rtpxh = (struct rtp_x_hdr *)payload;
138 x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
139 payload += x_len;
140 payload_len -= x_len;
141 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200142 DEBUGPC(DLMUX, "received RTP frame too short, "
Harald Welteda7ab742009-12-19 22:23:05 +0100143 "extension header exceeds frame length\n");
144 return -EINVAL;
145 }
146 }
147 if (rtph->padding) {
148 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200149 DEBUGPC(DLMUX, "received RTP frame too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100150 "padding length\n");
151 return -EINVAL;
152 }
153 payload_len -= payload[payload_len - 1];
154 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200155 DEBUGPC(DLMUX, "received RTP frame with padding "
Harald Welteda7ab742009-12-19 22:23:05 +0100156 "greater than payload\n");
157 return -EINVAL;
158 }
159 }
160
161 switch (rtph->payload_type) {
162 case RTP_PT_GSM_FULL:
163 msg_type = GSM_TCHF_FRAME;
164 if (payload_len != 33) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200165 DEBUGPC(DLMUX, "received RTP full rate frame with "
Harald Welteda7ab742009-12-19 22:23:05 +0100166 "payload length != 32 (len = %d)\n",
167 payload_len);
168 return -EINVAL;
169 }
170 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100171 case RTP_PT_GSM_EFR:
172 msg_type = GSM_TCHF_FRAME_EFR;
173 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100174 default:
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200175 DEBUGPC(DLMUX, "received RTP frame with unknown payload "
Harald Welteda7ab742009-12-19 22:23:05 +0100176 "type %d\n", rtph->payload_type);
177 return -EINVAL;
178 }
179
180 new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len,
181 "GSM-DATA");
182 if (!new_msg)
183 return -ENOMEM;
184 frame = (struct gsm_data_frame *)(new_msg->data);
185 frame->msg_type = msg_type;
186 frame->callref = callref;
187 memcpy(frame->data, payload, payload_len);
188 msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len);
189
190 *data = new_msg;
191 return 0;
192}
193
Harald Welte392736d2009-12-20 13:16:14 +0100194/* "to - from" */
195static void tv_difference(struct timeval *diff, const struct timeval *from,
196 const struct timeval *__to)
197{
198 struct timeval _to = *__to, *to = &_to;
199
200 if (to->tv_usec < from->tv_usec) {
201 to->tv_sec -= 1;
202 to->tv_usec += 1000000;
203 }
204
205 diff->tv_usec = to->tv_usec - from->tv_usec;
206 diff->tv_sec = to->tv_sec - from->tv_sec;
207}
208
Harald Welteda7ab742009-12-19 22:23:05 +0100209/* encode and send a rtp frame */
210int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
211{
212 struct rtp_sub_socket *rss = &rs->rtp;
213 struct msgb *msg;
214 struct rtp_hdr *rtph;
215 int payload_type;
216 int payload_len;
217 int duration; /* in samples */
218
219 if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
220 /* initialize sequences */
221 rs->tx_action = RTP_SEND_DOWNSTREAM;
222 rs->transmit.ssrc = rand();
223 rs->transmit.sequence = random();
224 rs->transmit.timestamp = random();
225 }
226
227 switch (frame->msg_type) {
228 case GSM_TCHF_FRAME:
229 payload_type = RTP_PT_GSM_FULL;
230 payload_len = 33;
231 duration = 160;
232 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100233 case GSM_TCHF_FRAME_EFR:
234 payload_type = RTP_PT_GSM_EFR;
235 payload_len = 31;
236 duration = 160;
237 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100238 default:
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200239 DEBUGPC(DLMUX, "unsupported message type %d\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100240 frame->msg_type);
241 return -EINVAL;
242 }
243
Harald Welte392736d2009-12-20 13:16:14 +0100244 {
245 struct timeval tv, tv_diff;
246 long int usec_diff, frame_diff;
247
248 gettimeofday(&tv, NULL);
249 tv_difference(&tv_diff, &rs->transmit.last_tv, &tv);
250 rs->transmit.last_tv = tv;
251
252 usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
253 frame_diff = (usec_diff / 20000);
254
255 if (abs(frame_diff) > 1) {
256 long int frame_diff_excess = frame_diff - 1;
257
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200258 LOGP(DLMUX, LOGL_NOTICE,
Harald Welte (local)9fcf6d72009-12-27 17:01:40 +0100259 "Correcting frame difference of %ld frames\n", frame_diff_excess);
Harald Welte392736d2009-12-20 13:16:14 +0100260 rs->transmit.sequence += frame_diff_excess;
261 rs->transmit.timestamp += frame_diff_excess * duration;
262 }
263 }
264
Harald Welteda7ab742009-12-19 22:23:05 +0100265 msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
266 if (!msg)
267 return -ENOMEM;
268 rtph = (struct rtp_hdr *)msg->data;
269 rtph->version = RTP_VERSION;
270 rtph->padding = 0;
271 rtph->extension = 0;
272 rtph->csrc_count = 0;
273 rtph->marker = 0;
274 rtph->payload_type = payload_type;
275 rtph->sequence = htons(rs->transmit.sequence++);
276 rtph->timestamp = htonl(rs->transmit.timestamp);
277 rs->transmit.timestamp += duration;
278 rtph->ssrc = htonl(rs->transmit.ssrc);
279 memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len);
280 msgb_put(msg, sizeof(struct rtp_hdr) + payload_len);
281 msgb_enqueue(&rss->tx_queue, msg);
282 rss->bfd.when |= BSC_FD_WRITE;
283
284 return 0;
285}
286
Holger Hans Peter Freyther89acf062009-07-29 06:46:26 +0200287/* iterate over all chunks in one RTCP message, look for CNAME IEs and
Harald Welte805f6442009-07-28 18:25:29 +0200288 * replace all of those with 'new_cname' */
289static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200290 uint16_t *rtcp_len, const char *new_cname)
Harald Welte805f6442009-07-28 18:25:29 +0200291{
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200292 uint8_t *rtcp_end;
293 uint8_t *cur = (uint8_t *) rh;
294 uint8_t tag, len = 0;
Harald Welte805f6442009-07-28 18:25:29 +0200295
296 rtcp_end = cur + *rtcp_len;
297 /* move cur to end of RTP header */
Harald Welte198f3f52009-07-29 10:46:41 +0200298 cur += sizeof(*rh);
Harald Welte805f6442009-07-28 18:25:29 +0200299
300 /* iterate over Chunks */
301 while (cur+4 < rtcp_end) {
302 /* skip four bytes SSRC/CSRC */
303 cur += 4;
304
305 /* iterate over IE's inside the chunk */
306 while (cur+1 < rtcp_end) {
307 tag = *cur++;
308 if (tag == 0) {
309 /* end of chunk, skip additional zero */
310 while (*cur++ == 0) { }
311 break;
312 }
313 len = *cur++;
314
315 if (tag == RTCP_IE_CNAME) {
316 /* we've found the CNAME, lets mangle it */
317 if (len < strlen(new_cname)) {
318 /* we need to make more space */
319 int increase = strlen(new_cname) - len;
320
321 msgb_push(msg, increase);
322 memmove(cur+len+increase, cur+len,
323 rtcp_end - (cur+len));
324 /* FIXME: we have to respect RTCP
325 * padding/alignment rules! */
326 len += increase;
327 *(cur-1) += increase;
328 rtcp_end += increase;
329 *rtcp_len += increase;
330 }
331 /* copy new CNAME into message */
332 memcpy(cur, new_cname, strlen(new_cname));
333 /* FIXME: zero the padding in case new CNAME
334 * is smaller than old one !!! */
335 }
336 cur += len;
337 }
338 }
339
340 return 0;
341}
342
343static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
344{
345 struct rtp_sub_socket *rss = &rs->rtcp;
346 struct rtcp_hdr *rtph;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200347 uint16_t old_len;
Harald Welte805f6442009-07-28 18:25:29 +0200348 int rc;
349
350 if (!mangle_rtcp_cname)
351 return 0;
352
Harald Welteda7ab742009-12-19 22:23:05 +0100353 printf("RTCP\n");
Harald Welte805f6442009-07-28 18:25:29 +0200354 /* iterate over list of RTCP messages */
355 rtph = (struct rtcp_hdr *)msg->data;
Harald Welteda7ab742009-12-19 22:23:05 +0100356 while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
Harald Welte805f6442009-07-28 18:25:29 +0200357 old_len = (ntohs(rtph->length) + 1) * 4;
Harald Welteda7ab742009-12-19 22:23:05 +0100358 if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200359 DEBUGPC(DLMUX, "received RTCP packet too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100360 "length element\n");
361 return -EINVAL;
362 }
Harald Welte805f6442009-07-28 18:25:29 +0200363 if (rtph->type == RTCP_TYPE_SDES) {
364 char new_cname[255];
365 strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
366 sizeof(new_cname));
367 new_cname[sizeof(new_cname)-1] = '\0';
368 rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
369 new_cname);
370 if (rc < 0)
371 return rc;
372 }
373 rtph = (void *)rtph + old_len;
374 }
375
376 return 0;
377}
378
Harald Welteead7a7b2009-07-28 00:01:58 +0200379/* read from incoming RTP/RTCP socket */
380static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
381{
382 int rc;
383 struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
Harald Welteda7ab742009-12-19 22:23:05 +0100384 struct msgb *new_msg;
Harald Welteead7a7b2009-07-28 00:01:58 +0200385 struct rtp_sub_socket *other_rss;
386
387 if (!msg)
388 return -ENOMEM;
389
390 rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
391 if (rc <= 0) {
392 rss->bfd.when &= ~BSC_FD_READ;
393 return rc;
394 }
395
396 msgb_put(msg, rc);
397
398 switch (rs->rx_action) {
399 case RTP_PROXY:
Harald Welte805f6442009-07-28 18:25:29 +0200400 if (!rs->proxy.other_sock) {
401 rc = -EIO;
402 goto out_free;
403 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200404 if (rss->bfd.priv_nr == RTP_PRIV_RTP)
405 other_rss = &rs->proxy.other_sock->rtp;
Harald Welte805f6442009-07-28 18:25:29 +0200406 else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200407 other_rss = &rs->proxy.other_sock->rtcp;
Harald Welte805f6442009-07-28 18:25:29 +0200408 /* modify RTCP SDES CNAME */
409 rc = rtcp_mangle(msg, rs);
410 if (rc < 0)
411 goto out_free;
412 } else {
413 rc = -EINVAL;
414 goto out_free;
Harald Welteead7a7b2009-07-28 00:01:58 +0200415 }
416 msgb_enqueue(&other_rss->tx_queue, msg);
417 other_rss->bfd.when |= BSC_FD_WRITE;
418 break;
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200419
420 case RTP_RECV_UPSTREAM:
Harald Welteda7ab742009-12-19 22:23:05 +0100421 if (!rs->receive.callref || !rs->receive.net) {
422 rc = -EIO;
423 goto out_free;
424 }
425 if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
426 if (!mangle_rtcp_cname) {
427 msgb_free(msg);
428 break;
429 }
430 /* modify RTCP SDES CNAME */
431 rc = rtcp_mangle(msg, rs);
432 if (rc < 0)
433 goto out_free;
434 msgb_enqueue(&rss->tx_queue, msg);
435 rss->bfd.when |= BSC_FD_WRITE;
436 break;
437 }
438 if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
439 rc = -EINVAL;
440 goto out_free;
441 }
442 rc = rtp_decode(msg, rs->receive.callref, &new_msg);
443 if (rc < 0)
444 goto out_free;
445 msgb_free(msg);
Harald Welte31c00f72011-03-03 23:29:05 +0100446 trau_tx_to_mncc(rs->receive.net, new_msg);
Harald Welteda7ab742009-12-19 22:23:05 +0100447 break;
448
449 case RTP_NONE: /* if socket exists, but disabled by app */
450 msgb_free(msg);
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200451 break;
Harald Welteead7a7b2009-07-28 00:01:58 +0200452 }
453
Harald Welteda7ab742009-12-19 22:23:05 +0100454 return 0;
Harald Welte805f6442009-07-28 18:25:29 +0200455
456out_free:
457 msgb_free(msg);
458 return rc;
Harald Welteead7a7b2009-07-28 00:01:58 +0200459}
460
461/* write from tx_queue to RTP/RTCP socket */
462static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
463{
464 struct msgb *msg;
465 int written;
466
467 msg = msgb_dequeue(&rss->tx_queue);
468 if (!msg) {
469 rss->bfd.when &= ~BSC_FD_WRITE;
470 return 0;
471 }
472
473 written = write(rss->bfd.fd, msg->data, msg->len);
474 if (written < msg->len) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200475 LOGP(DLMIB, LOGL_ERROR, "short write");
Harald Welteead7a7b2009-07-28 00:01:58 +0200476 msgb_free(msg);
477 return -EIO;
478 }
479
480 msgb_free(msg);
481
482 return 0;
483}
484
485
486/* callback for the select.c:bfd_* layer */
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200487static int rtp_bfd_cb(struct osmo_fd *bfd, unsigned int flags)
Harald Welteead7a7b2009-07-28 00:01:58 +0200488{
489 struct rtp_socket *rs = bfd->data;
490 struct rtp_sub_socket *rss;
491
492 switch (bfd->priv_nr) {
493 case RTP_PRIV_RTP:
494 rss = &rs->rtp;
495 break;
496 case RTP_PRIV_RTCP:
497 rss = &rs->rtcp;
498 break;
499 default:
500 return -EINVAL;
501 }
502
503 if (flags & BSC_FD_READ)
504 rtp_socket_read(rs, rss);
505
506 if (flags & BSC_FD_WRITE)
507 rtp_socket_write(rs, rss);
508
509 return 0;
510}
511
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200512static void init_rss(struct rtp_sub_socket *rss,
Harald Welteead7a7b2009-07-28 00:01:58 +0200513 struct rtp_socket *rs, int fd, int priv_nr)
514{
515 /* initialize bfd */
516 rss->bfd.fd = fd;
517 rss->bfd.data = rs;
518 rss->bfd.priv_nr = priv_nr;
519 rss->bfd.cb = rtp_bfd_cb;
520}
521
522struct rtp_socket *rtp_socket_create(void)
523{
524 int rc;
525 struct rtp_socket *rs;
526
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200527 DEBUGP(DLMUX, "rtp_socket_create(): ");
Harald Welte805f6442009-07-28 18:25:29 +0200528
Harald Welteead7a7b2009-07-28 00:01:58 +0200529 rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
530 if (!rs)
531 return NULL;
532
533 INIT_LLIST_HEAD(&rs->rtp.tx_queue);
534 INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
535
536 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
537 if (rc < 0)
538 goto out_free;
539
540 init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200541 rc = osmo_fd_register(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200542 if (rc < 0)
543 goto out_rtp_socket;
544
545 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
546 if (rc < 0)
547 goto out_rtp_bfd;
548
549 init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200550 rc = osmo_fd_register(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200551 if (rc < 0)
552 goto out_rtcp_socket;
553
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200554 DEBUGPC(DLMUX, "success\n");
Harald Welte805f6442009-07-28 18:25:29 +0200555
556 rc = rtp_socket_bind(rs, INADDR_ANY);
557 if (rc < 0)
558 goto out_rtcp_bfd;
559
Harald Welteead7a7b2009-07-28 00:01:58 +0200560 return rs;
561
Harald Welte805f6442009-07-28 18:25:29 +0200562out_rtcp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200563 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200564out_rtcp_socket:
565 close(rs->rtcp.bfd.fd);
566out_rtp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200567 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200568out_rtp_socket:
569 close(rs->rtp.bfd.fd);
570out_free:
571 talloc_free(rs);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200572 DEBUGPC(DLMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200573 return NULL;
574}
575
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200576static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, uint32_t ip,
577 uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200578{
Harald Welte805f6442009-07-28 18:25:29 +0200579 int rc;
580 socklen_t alen = sizeof(rss->sin_local);
581
Harald Welteead7a7b2009-07-28 00:01:58 +0200582 rss->sin_local.sin_family = AF_INET;
583 rss->sin_local.sin_addr.s_addr = htonl(ip);
584 rss->sin_local.sin_port = htons(port);
585 rss->bfd.when |= BSC_FD_READ;
586
Harald Welte805f6442009-07-28 18:25:29 +0200587 rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
588 sizeof(rss->sin_local));
589 if (rc < 0)
590 return rc;
591
592 /* retrieve the address we actually bound to, in case we
593 * passed INADDR_ANY as IP address */
594 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
595 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200596}
597
598#define RTP_PORT_BASE 30000
Harald Welte805f6442009-07-28 18:25:29 +0200599static unsigned int next_udp_port = RTP_PORT_BASE;
Harald Welteead7a7b2009-07-28 00:01:58 +0200600
601/* bind a RTP socket to a local address */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200602int rtp_socket_bind(struct rtp_socket *rs, uint32_t ip)
Harald Welteead7a7b2009-07-28 00:01:58 +0200603{
Harald Welte805f6442009-07-28 18:25:29 +0200604 int rc = -EIO;
605 struct in_addr ia;
606
607 ia.s_addr = htonl(ip);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200608 DEBUGP(DLMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
Harald Welte805f6442009-07-28 18:25:29 +0200609 inet_ntoa(ia));
Harald Welteead7a7b2009-07-28 00:01:58 +0200610
611 /* try to bind to a consecutive pair of ports */
Harald Welte805f6442009-07-28 18:25:29 +0200612 for (next_udp_port = next_udp_port % 0xffff;
613 next_udp_port < 0xffff; next_udp_port += 2) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200614 rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
615 if (rc != 0)
616 continue;
617
618 rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
619 if (rc == 0)
620 break;
621 }
Harald Welte805f6442009-07-28 18:25:29 +0200622 if (rc < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200623 DEBUGPC(DLMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200624 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200625 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200626
Harald Welte805f6442009-07-28 18:25:29 +0200627 ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200628 DEBUGPC(DLMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
Harald Welte805f6442009-07-28 18:25:29 +0200629 inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
Harald Welteead7a7b2009-07-28 00:01:58 +0200630 return ntohs(rs->rtp.sin_local.sin_port);
631}
632
633static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200634 uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200635{
Harald Welte805f6442009-07-28 18:25:29 +0200636 int rc;
637 socklen_t alen = sizeof(rss->sin_local);
638
Harald Welteead7a7b2009-07-28 00:01:58 +0200639 rss->sin_remote.sin_family = AF_INET;
640 rss->sin_remote.sin_addr.s_addr = htonl(ip);
641 rss->sin_remote.sin_port = htons(port);
642
Harald Welte805f6442009-07-28 18:25:29 +0200643 rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
644 sizeof(rss->sin_remote));
645 if (rc < 0)
646 return rc;
647
648 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
649 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200650}
651
652/* 'connect' a RTP socket to a remote peer */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200653int rtp_socket_connect(struct rtp_socket *rs, uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200654{
655 int rc;
Harald Welte805f6442009-07-28 18:25:29 +0200656 struct in_addr ia;
657
658 ia.s_addr = htonl(ip);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200659 DEBUGP(DLMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
Harald Welte805f6442009-07-28 18:25:29 +0200660 rs, inet_ntoa(ia), port);
Harald Welteead7a7b2009-07-28 00:01:58 +0200661
662 rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
663 if (rc < 0)
664 return rc;
665
666 return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
667}
668
669/* bind two RTP/RTCP sockets together */
670int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
671{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200672 DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
Harald Welte805f6442009-07-28 18:25:29 +0200673 this, other);
674
Harald Welteead7a7b2009-07-28 00:01:58 +0200675 this->rx_action = RTP_PROXY;
676 this->proxy.other_sock = other;
677
678 other->rx_action = RTP_PROXY;
679 other->proxy.other_sock = this;
680
681 return 0;
682}
683
Harald Welteda7ab742009-12-19 22:23:05 +0100684/* bind RTP/RTCP socket to application */
Harald Welte9fb1f102009-12-20 17:07:23 +0100685int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200686 uint32_t callref)
Harald Welteda7ab742009-12-19 22:23:05 +0100687{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200688 DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100689 this, callref);
690
691 if (callref) {
692 this->rx_action = RTP_RECV_UPSTREAM;
693 this->receive.net = net;
694 this->receive.callref = callref;
695 } else
696 this->rx_action = RTP_NONE;
697
698 return 0;
699}
700
Harald Welteead7a7b2009-07-28 00:01:58 +0200701static void free_tx_queue(struct rtp_sub_socket *rss)
702{
703 struct msgb *msg;
704
705 while ((msg = msgb_dequeue(&rss->tx_queue)))
706 msgb_free(msg);
707}
708
709int rtp_socket_free(struct rtp_socket *rs)
710{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200711 DEBUGP(DLMUX, "rtp_socket_free(rs=%p)\n", rs);
Harald Welteead7a7b2009-07-28 00:01:58 +0200712
713 /* make sure we don't leave references dangling to us */
714 if (rs->rx_action == RTP_PROXY &&
715 rs->proxy.other_sock)
716 rs->proxy.other_sock->proxy.other_sock = NULL;
717
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200718 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200719 close(rs->rtp.bfd.fd);
720 free_tx_queue(&rs->rtp);
721
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200722 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200723 close(rs->rtcp.bfd.fd);
724 free_tx_queue(&rs->rtcp);
725
726 talloc_free(rs);
727
728 return 0;
729}