blob: 33169ff9d46735dd5bff1b72f8c8eb915dbd5d8b [file] [log] [blame]
Harald Welteead7a7b2009-07-28 00:01:58 +02001/* RTP proxy handling for ip.access nanoBTS */
2
Harald Welte647db842013-02-03 12:06:58 +01003/* (C) 2009-2013 by Harald Welte <laforge@gnumonks.org>
Harald Welteead7a7b2009-07-28 00:01:58 +02004 * 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>
Holger Hans Peter Freyther983c9912014-06-22 22:30:28 +020039#include <openbsc/rtp.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020040
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020041/* attempt to determine byte order */
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020042#include <sys/param.h>
43#include <limits.h>
44
45#ifndef __BYTE_ORDER
Tobias Engelaff20712012-10-24 17:53:50 +020046# ifdef __APPLE__
47# define __BYTE_ORDER __DARWIN_BYTE_ORDER
48# define __LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN
49# define __BIG_ENDIAN __DARWIN_BIG_ENDIAN
50# else
51# error "__BYTE_ORDER should be defined by someone"
52# endif
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020053#endif
54
Harald Welteead7a7b2009-07-28 00:01:58 +020055static LLIST_HEAD(rtp_sockets);
56
Harald Welte805f6442009-07-28 18:25:29 +020057/* should we mangle the CNAME inside SDES of RTCP packets? We disable
58 * this by default, as it seems to be not needed */
59static int mangle_rtcp_cname = 0;
60
Harald Welteead7a7b2009-07-28 00:01:58 +020061enum rtp_bfd_priv {
62 RTP_PRIV_NONE,
63 RTP_PRIV_RTP,
64 RTP_PRIV_RTCP
65};
66
67#define RTP_ALLOC_SIZE 1500
68
Harald Welte805f6442009-07-28 18:25:29 +020069/* according to RFC 1889 */
70struct rtcp_hdr {
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020071 uint8_t byte0;
72 uint8_t type;
73 uint16_t length;
Harald Welte805f6442009-07-28 18:25:29 +020074} __attribute__((packed));
75
76#define RTCP_TYPE_SDES 202
77
78#define RTCP_IE_CNAME 1
79
Harald Welteda7ab742009-12-19 22:23:05 +010080struct rtp_x_hdr {
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020081 uint16_t by_profile;
82 uint16_t length;
Harald Welteda7ab742009-12-19 22:23:05 +010083} __attribute__((packed));
84
85#define RTP_VERSION 2
86
Harald Weltea87f8f92014-05-17 13:43:01 +020087/* 33 for FR, all other codecs have smaller size */
88#define MAX_RTP_PAYLOAD_LEN 33
89
Harald Welteda7ab742009-12-19 22:23:05 +010090/* decode an rtp frame and create a new buffer with payload */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020091static int rtp_decode(struct msgb *msg, uint32_t callref, struct msgb **data)
Harald Welteda7ab742009-12-19 22:23:05 +010092{
93 struct msgb *new_msg;
94 struct gsm_data_frame *frame;
95 struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
96 struct rtp_x_hdr *rtpxh;
Harald Weltea87f8f92014-05-17 13:43:01 +020097 uint8_t *payload, *payload_out;
Harald Welteda7ab742009-12-19 22:23:05 +010098 int payload_len;
99 int msg_type;
100 int x_len;
101
102 if (msg->len < 12) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200103 DEBUGPC(DLMUX, "received RTP frame too short (len = %d)\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100104 msg->len);
105 return -EINVAL;
106 }
107 if (rtph->version != RTP_VERSION) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200108 DEBUGPC(DLMUX, "received RTP version %d not supported.\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100109 rtph->version);
110 return -EINVAL;
111 }
112 payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
113 payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
114 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200115 DEBUGPC(DLMUX, "received RTP frame too short (len = %d, "
Harald Welteda7ab742009-12-19 22:23:05 +0100116 "csrc count = %d)\n", msg->len, rtph->csrc_count);
117 return -EINVAL;
118 }
119 if (rtph->extension) {
120 if (payload_len < sizeof(struct rtp_x_hdr)) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200121 DEBUGPC(DLMUX, "received RTP frame too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100122 "extension header\n");
123 return -EINVAL;
124 }
125 rtpxh = (struct rtp_x_hdr *)payload;
126 x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
127 payload += x_len;
128 payload_len -= x_len;
129 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200130 DEBUGPC(DLMUX, "received RTP frame too short, "
Harald Welteda7ab742009-12-19 22:23:05 +0100131 "extension header exceeds frame length\n");
132 return -EINVAL;
133 }
134 }
135 if (rtph->padding) {
Jacob Erlbeckd9e40392013-11-21 19:05:42 +0100136 if (payload_len < 1) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200137 DEBUGPC(DLMUX, "received RTP frame too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100138 "padding length\n");
139 return -EINVAL;
140 }
141 payload_len -= payload[payload_len - 1];
142 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200143 DEBUGPC(DLMUX, "received RTP frame with padding "
Harald Welteda7ab742009-12-19 22:23:05 +0100144 "greater than payload\n");
145 return -EINVAL;
146 }
147 }
148
149 switch (rtph->payload_type) {
150 case RTP_PT_GSM_FULL:
151 msg_type = GSM_TCHF_FRAME;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100152 if (payload_len != RTP_LEN_GSM_FULL) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200153 DEBUGPC(DLMUX, "received RTP full rate frame with "
Andreas Eversberg88012b62014-01-22 10:05:51 +0100154 "payload length != %d (len = %d)\n",
155 RTP_LEN_GSM_FULL, payload_len);
Harald Welteda7ab742009-12-19 22:23:05 +0100156 return -EINVAL;
157 }
158 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100159 case RTP_PT_GSM_EFR:
160 msg_type = GSM_TCHF_FRAME_EFR;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100161 if (payload_len != RTP_LEN_GSM_EFR) {
162 DEBUGPC(DLMUX, "received RTP extended full rate frame "
163 "with payload length != %d (len = %d)\n",
164 RTP_LEN_GSM_EFR, payload_len);
165 return -EINVAL;
166 }
Harald Welteaca8f152009-12-19 23:06:41 +0100167 break;
Andreas Eversberg63bfdd82014-01-17 19:06:38 +0100168 case RTP_PT_GSM_HALF:
169 msg_type = GSM_TCHH_FRAME;
170 if (payload_len != RTP_LEN_GSM_HALF) {
171 DEBUGPC(DLMUX, "received RTP half rate frame with "
172 "payload length != %d (len = %d)\n",
173 RTP_LEN_GSM_HALF, payload_len);
174 return -EINVAL;
175 }
176 break;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100177 case RTP_PT_AMR:
Harald Welteec757982014-05-18 18:32:05 +0200178 msg_type = GSM_TCH_FRAME_AMR;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100179 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100180 default:
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200181 DEBUGPC(DLMUX, "received RTP frame with unknown payload "
Harald Welteda7ab742009-12-19 22:23:05 +0100182 "type %d\n", rtph->payload_type);
183 return -EINVAL;
184 }
185
Harald Weltea87f8f92014-05-17 13:43:01 +0200186 if (payload_len > MAX_RTP_PAYLOAD_LEN) {
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100187 DEBUGPC(DLMUX, "RTP payload too large (%d octets)\n",
188 payload_len);
189 return -EINVAL;
190 }
191
Harald Weltea87f8f92014-05-17 13:43:01 +0200192 /* always allocate for the maximum possible size to avoid
193 * fragmentation */
194 new_msg = msgb_alloc(sizeof(struct gsm_data_frame) +
195 MAX_RTP_PAYLOAD_LEN, "GSM-DATA (TCH)");
196
Harald Welteda7ab742009-12-19 22:23:05 +0100197 if (!new_msg)
198 return -ENOMEM;
Holger Hans Peter Freyther652cdb42014-07-24 21:09:31 +0200199 frame = (struct gsm_data_frame *) msgb_put(new_msg, sizeof(struct gsm_data_frame));
Harald Welteda7ab742009-12-19 22:23:05 +0100200 frame->msg_type = msg_type;
201 frame->callref = callref;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100202 if (rtph->payload_type == RTP_PT_AMR) {
Harald Weltea87f8f92014-05-17 13:43:01 +0200203 /* for FR/HR/EFR the length is implicit. In AMR, we
204 * need to make it explicit by using the first byte of
205 * the data[] buffer as length byte */
206 uint8_t *data0 = msgb_put(new_msg, 1);
207 *data0 = payload_len;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100208 }
Harald Weltea87f8f92014-05-17 13:43:01 +0200209 payload_out = msgb_put(new_msg, payload_len);
210 memcpy(payload_out, payload, payload_len);
Harald Welteda7ab742009-12-19 22:23:05 +0100211
212 *data = new_msg;
213 return 0;
214}
215
Harald Welte392736d2009-12-20 13:16:14 +0100216/* "to - from" */
217static void tv_difference(struct timeval *diff, const struct timeval *from,
218 const struct timeval *__to)
219{
220 struct timeval _to = *__to, *to = &_to;
221
222 if (to->tv_usec < from->tv_usec) {
223 to->tv_sec -= 1;
224 to->tv_usec += 1000000;
225 }
226
227 diff->tv_usec = to->tv_usec - from->tv_usec;
228 diff->tv_sec = to->tv_sec - from->tv_sec;
229}
230
Harald Welte647db842013-02-03 12:06:58 +0100231/*! \brief encode and send a rtp frame
232 * \param[in] rs RTP socket through which we shall send
233 * \param[in] frame GSM RTP frame to be sent
234 */
Harald Welteda7ab742009-12-19 22:23:05 +0100235int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
236{
237 struct rtp_sub_socket *rss = &rs->rtp;
238 struct msgb *msg;
239 struct rtp_hdr *rtph;
Harald Weltea87f8f92014-05-17 13:43:01 +0200240 uint8_t *payload;
Harald Welteda7ab742009-12-19 22:23:05 +0100241 int payload_type;
242 int payload_len;
243 int duration; /* in samples */
244
245 if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
246 /* initialize sequences */
247 rs->tx_action = RTP_SEND_DOWNSTREAM;
248 rs->transmit.ssrc = rand();
249 rs->transmit.sequence = random();
250 rs->transmit.timestamp = random();
251 }
252
253 switch (frame->msg_type) {
254 case GSM_TCHF_FRAME:
255 payload_type = RTP_PT_GSM_FULL;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100256 payload_len = RTP_LEN_GSM_FULL;
257 duration = RTP_GSM_DURATION;
Harald Welteda7ab742009-12-19 22:23:05 +0100258 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100259 case GSM_TCHF_FRAME_EFR:
260 payload_type = RTP_PT_GSM_EFR;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100261 payload_len = RTP_LEN_GSM_EFR;
262 duration = RTP_GSM_DURATION;
Harald Welteaca8f152009-12-19 23:06:41 +0100263 break;
Andreas Eversberg63bfdd82014-01-17 19:06:38 +0100264 case GSM_TCHH_FRAME:
265 payload_type = RTP_PT_GSM_HALF;
266 payload_len = RTP_LEN_GSM_HALF;
267 duration = RTP_GSM_DURATION;
268 break;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100269 case GSM_TCH_FRAME_AMR:
270 payload_type = RTP_PT_AMR;
271 payload_len = frame->data[0];
272 duration = RTP_GSM_DURATION;
273 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100274 default:
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200275 DEBUGPC(DLMUX, "unsupported message type %d\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100276 frame->msg_type);
277 return -EINVAL;
278 }
279
Harald Welte392736d2009-12-20 13:16:14 +0100280 {
281 struct timeval tv, tv_diff;
282 long int usec_diff, frame_diff;
283
284 gettimeofday(&tv, NULL);
285 tv_difference(&tv_diff, &rs->transmit.last_tv, &tv);
286 rs->transmit.last_tv = tv;
287
288 usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
289 frame_diff = (usec_diff / 20000);
290
291 if (abs(frame_diff) > 1) {
292 long int frame_diff_excess = frame_diff - 1;
293
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200294 LOGP(DLMUX, LOGL_NOTICE,
Harald Welte (local)9fcf6d72009-12-27 17:01:40 +0100295 "Correcting frame difference of %ld frames\n", frame_diff_excess);
Harald Welte392736d2009-12-20 13:16:14 +0100296 rs->transmit.sequence += frame_diff_excess;
297 rs->transmit.timestamp += frame_diff_excess * duration;
298 }
299 }
300
Harald Weltea87f8f92014-05-17 13:43:01 +0200301 if (payload_len > MAX_RTP_PAYLOAD_LEN) {
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100302 DEBUGPC(DLMUX, "RTP payload too large (%d octets)\n",
303 payload_len);
304 return -EINVAL;
305 }
306
Harald Weltea87f8f92014-05-17 13:43:01 +0200307 msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM");
Harald Welteda7ab742009-12-19 22:23:05 +0100308 if (!msg)
309 return -ENOMEM;
Harald Weltea87f8f92014-05-17 13:43:01 +0200310 rtph = (struct rtp_hdr *) msgb_put(msg, sizeof(struct rtp_hdr));
Harald Welteda7ab742009-12-19 22:23:05 +0100311 rtph->version = RTP_VERSION;
312 rtph->padding = 0;
313 rtph->extension = 0;
314 rtph->csrc_count = 0;
315 rtph->marker = 0;
316 rtph->payload_type = payload_type;
317 rtph->sequence = htons(rs->transmit.sequence++);
318 rtph->timestamp = htonl(rs->transmit.timestamp);
319 rs->transmit.timestamp += duration;
320 rtph->ssrc = htonl(rs->transmit.ssrc);
Harald Weltea87f8f92014-05-17 13:43:01 +0200321
322 payload = msgb_put(msg, payload_len);
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100323 if (frame->msg_type == GSM_TCH_FRAME_AMR)
Harald Weltea87f8f92014-05-17 13:43:01 +0200324 memcpy(payload, frame->data + 1, payload_len);
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100325 else
Harald Weltea87f8f92014-05-17 13:43:01 +0200326 memcpy(payload, frame->data, payload_len);
Harald Welteda7ab742009-12-19 22:23:05 +0100327 msgb_enqueue(&rss->tx_queue, msg);
328 rss->bfd.when |= BSC_FD_WRITE;
329
330 return 0;
331}
332
Holger Hans Peter Freyther89acf062009-07-29 06:46:26 +0200333/* iterate over all chunks in one RTCP message, look for CNAME IEs and
Harald Welte805f6442009-07-28 18:25:29 +0200334 * replace all of those with 'new_cname' */
335static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200336 uint16_t *rtcp_len, const char *new_cname)
Harald Welte805f6442009-07-28 18:25:29 +0200337{
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200338 uint8_t *rtcp_end;
339 uint8_t *cur = (uint8_t *) rh;
340 uint8_t tag, len = 0;
Harald Welte805f6442009-07-28 18:25:29 +0200341
342 rtcp_end = cur + *rtcp_len;
343 /* move cur to end of RTP header */
Harald Welte198f3f52009-07-29 10:46:41 +0200344 cur += sizeof(*rh);
Harald Welte805f6442009-07-28 18:25:29 +0200345
346 /* iterate over Chunks */
347 while (cur+4 < rtcp_end) {
348 /* skip four bytes SSRC/CSRC */
349 cur += 4;
350
351 /* iterate over IE's inside the chunk */
352 while (cur+1 < rtcp_end) {
353 tag = *cur++;
354 if (tag == 0) {
355 /* end of chunk, skip additional zero */
Daniel Willmann45fcb852014-05-21 15:46:43 +0200356 while ((*cur++ == 0) && (cur < rtcp_end)) { }
Harald Welte805f6442009-07-28 18:25:29 +0200357 break;
358 }
359 len = *cur++;
360
361 if (tag == RTCP_IE_CNAME) {
362 /* we've found the CNAME, lets mangle it */
363 if (len < strlen(new_cname)) {
364 /* we need to make more space */
365 int increase = strlen(new_cname) - len;
366
367 msgb_push(msg, increase);
368 memmove(cur+len+increase, cur+len,
369 rtcp_end - (cur+len));
370 /* FIXME: we have to respect RTCP
371 * padding/alignment rules! */
372 len += increase;
373 *(cur-1) += increase;
374 rtcp_end += increase;
375 *rtcp_len += increase;
376 }
377 /* copy new CNAME into message */
378 memcpy(cur, new_cname, strlen(new_cname));
379 /* FIXME: zero the padding in case new CNAME
380 * is smaller than old one !!! */
381 }
382 cur += len;
383 }
384 }
385
386 return 0;
387}
388
389static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
390{
391 struct rtp_sub_socket *rss = &rs->rtcp;
392 struct rtcp_hdr *rtph;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200393 uint16_t old_len;
Harald Welte805f6442009-07-28 18:25:29 +0200394 int rc;
395
396 if (!mangle_rtcp_cname)
397 return 0;
398
Harald Welteda7ab742009-12-19 22:23:05 +0100399 printf("RTCP\n");
Harald Welte805f6442009-07-28 18:25:29 +0200400 /* iterate over list of RTCP messages */
401 rtph = (struct rtcp_hdr *)msg->data;
Harald Welteda7ab742009-12-19 22:23:05 +0100402 while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
Harald Welte805f6442009-07-28 18:25:29 +0200403 old_len = (ntohs(rtph->length) + 1) * 4;
Harald Welteda7ab742009-12-19 22:23:05 +0100404 if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200405 DEBUGPC(DLMUX, "received RTCP packet too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100406 "length element\n");
407 return -EINVAL;
408 }
Harald Welte805f6442009-07-28 18:25:29 +0200409 if (rtph->type == RTCP_TYPE_SDES) {
410 char new_cname[255];
411 strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
412 sizeof(new_cname));
413 new_cname[sizeof(new_cname)-1] = '\0';
414 rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
415 new_cname);
416 if (rc < 0)
417 return rc;
418 }
419 rtph = (void *)rtph + old_len;
420 }
421
422 return 0;
423}
424
Harald Welteead7a7b2009-07-28 00:01:58 +0200425/* read from incoming RTP/RTCP socket */
426static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
427{
428 int rc;
429 struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
Harald Welteda7ab742009-12-19 22:23:05 +0100430 struct msgb *new_msg;
Harald Welteead7a7b2009-07-28 00:01:58 +0200431 struct rtp_sub_socket *other_rss;
432
433 if (!msg)
434 return -ENOMEM;
435
436 rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
437 if (rc <= 0) {
438 rss->bfd.when &= ~BSC_FD_READ;
439 return rc;
440 }
441
442 msgb_put(msg, rc);
443
444 switch (rs->rx_action) {
445 case RTP_PROXY:
Harald Welte805f6442009-07-28 18:25:29 +0200446 if (!rs->proxy.other_sock) {
447 rc = -EIO;
448 goto out_free;
449 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200450 if (rss->bfd.priv_nr == RTP_PRIV_RTP)
451 other_rss = &rs->proxy.other_sock->rtp;
Harald Welte805f6442009-07-28 18:25:29 +0200452 else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200453 other_rss = &rs->proxy.other_sock->rtcp;
Harald Welte805f6442009-07-28 18:25:29 +0200454 /* modify RTCP SDES CNAME */
455 rc = rtcp_mangle(msg, rs);
456 if (rc < 0)
457 goto out_free;
458 } else {
459 rc = -EINVAL;
460 goto out_free;
Harald Welteead7a7b2009-07-28 00:01:58 +0200461 }
462 msgb_enqueue(&other_rss->tx_queue, msg);
463 other_rss->bfd.when |= BSC_FD_WRITE;
464 break;
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200465
466 case RTP_RECV_UPSTREAM:
Harald Welteda7ab742009-12-19 22:23:05 +0100467 if (!rs->receive.callref || !rs->receive.net) {
468 rc = -EIO;
469 goto out_free;
470 }
471 if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
472 if (!mangle_rtcp_cname) {
473 msgb_free(msg);
474 break;
475 }
476 /* modify RTCP SDES CNAME */
477 rc = rtcp_mangle(msg, rs);
478 if (rc < 0)
479 goto out_free;
480 msgb_enqueue(&rss->tx_queue, msg);
481 rss->bfd.when |= BSC_FD_WRITE;
482 break;
483 }
484 if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
485 rc = -EINVAL;
486 goto out_free;
487 }
488 rc = rtp_decode(msg, rs->receive.callref, &new_msg);
489 if (rc < 0)
490 goto out_free;
491 msgb_free(msg);
Harald Welte31c00f72011-03-03 23:29:05 +0100492 trau_tx_to_mncc(rs->receive.net, new_msg);
Harald Welteda7ab742009-12-19 22:23:05 +0100493 break;
494
495 case RTP_NONE: /* if socket exists, but disabled by app */
496 msgb_free(msg);
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200497 break;
Harald Welteead7a7b2009-07-28 00:01:58 +0200498 }
499
Harald Welteda7ab742009-12-19 22:23:05 +0100500 return 0;
Harald Welte805f6442009-07-28 18:25:29 +0200501
502out_free:
503 msgb_free(msg);
504 return rc;
Harald Welteead7a7b2009-07-28 00:01:58 +0200505}
506
Harald Welte647db842013-02-03 12:06:58 +0100507/* \brief write from tx_queue to RTP/RTCP socket */
Harald Welteead7a7b2009-07-28 00:01:58 +0200508static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
509{
510 struct msgb *msg;
511 int written;
512
513 msg = msgb_dequeue(&rss->tx_queue);
514 if (!msg) {
515 rss->bfd.when &= ~BSC_FD_WRITE;
516 return 0;
517 }
518
519 written = write(rss->bfd.fd, msg->data, msg->len);
520 if (written < msg->len) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200521 LOGP(DLMIB, LOGL_ERROR, "short write");
Harald Welteead7a7b2009-07-28 00:01:58 +0200522 msgb_free(msg);
523 return -EIO;
524 }
525
526 msgb_free(msg);
527
528 return 0;
529}
530
531
Harald Welte647db842013-02-03 12:06:58 +0100532/*! \brief callback for the select.c:bfd_* layer */
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200533static int rtp_bfd_cb(struct osmo_fd *bfd, unsigned int flags)
Harald Welteead7a7b2009-07-28 00:01:58 +0200534{
535 struct rtp_socket *rs = bfd->data;
536 struct rtp_sub_socket *rss;
537
538 switch (bfd->priv_nr) {
539 case RTP_PRIV_RTP:
540 rss = &rs->rtp;
541 break;
542 case RTP_PRIV_RTCP:
543 rss = &rs->rtcp;
544 break;
545 default:
546 return -EINVAL;
547 }
548
549 if (flags & BSC_FD_READ)
550 rtp_socket_read(rs, rss);
551
552 if (flags & BSC_FD_WRITE)
553 rtp_socket_write(rs, rss);
554
555 return 0;
556}
557
Harald Welte647db842013-02-03 12:06:58 +0100558/*! \brief initialize one rtp sub-socket */
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200559static void init_rss(struct rtp_sub_socket *rss,
Harald Welteead7a7b2009-07-28 00:01:58 +0200560 struct rtp_socket *rs, int fd, int priv_nr)
561{
562 /* initialize bfd */
563 rss->bfd.fd = fd;
564 rss->bfd.data = rs;
565 rss->bfd.priv_nr = priv_nr;
566 rss->bfd.cb = rtp_bfd_cb;
567}
568
Harald Welte647db842013-02-03 12:06:58 +0100569/*! \brief create a new RTP/RTCP socket and bind it */
Harald Welteead7a7b2009-07-28 00:01:58 +0200570struct rtp_socket *rtp_socket_create(void)
571{
572 int rc;
573 struct rtp_socket *rs;
574
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200575 DEBUGP(DLMUX, "rtp_socket_create(): ");
Harald Welte805f6442009-07-28 18:25:29 +0200576
Harald Welteead7a7b2009-07-28 00:01:58 +0200577 rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
578 if (!rs)
579 return NULL;
580
581 INIT_LLIST_HEAD(&rs->rtp.tx_queue);
582 INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
583
584 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
585 if (rc < 0)
586 goto out_free;
587
588 init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200589 rc = osmo_fd_register(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200590 if (rc < 0)
591 goto out_rtp_socket;
592
593 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
594 if (rc < 0)
595 goto out_rtp_bfd;
596
597 init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200598 rc = osmo_fd_register(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200599 if (rc < 0)
600 goto out_rtcp_socket;
601
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200602 DEBUGPC(DLMUX, "success\n");
Harald Welte805f6442009-07-28 18:25:29 +0200603
604 rc = rtp_socket_bind(rs, INADDR_ANY);
605 if (rc < 0)
606 goto out_rtcp_bfd;
607
Harald Welteead7a7b2009-07-28 00:01:58 +0200608 return rs;
609
Harald Welte805f6442009-07-28 18:25:29 +0200610out_rtcp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200611 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200612out_rtcp_socket:
613 close(rs->rtcp.bfd.fd);
614out_rtp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200615 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200616out_rtp_socket:
617 close(rs->rtp.bfd.fd);
618out_free:
619 talloc_free(rs);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200620 DEBUGPC(DLMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200621 return NULL;
622}
623
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200624static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, uint32_t ip,
625 uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200626{
Harald Welte805f6442009-07-28 18:25:29 +0200627 int rc;
628 socklen_t alen = sizeof(rss->sin_local);
629
Harald Welteead7a7b2009-07-28 00:01:58 +0200630 rss->sin_local.sin_family = AF_INET;
631 rss->sin_local.sin_addr.s_addr = htonl(ip);
632 rss->sin_local.sin_port = htons(port);
633 rss->bfd.when |= BSC_FD_READ;
634
Harald Welte805f6442009-07-28 18:25:29 +0200635 rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
636 sizeof(rss->sin_local));
637 if (rc < 0)
638 return rc;
639
640 /* retrieve the address we actually bound to, in case we
641 * passed INADDR_ANY as IP address */
642 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
643 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200644}
645
646#define RTP_PORT_BASE 30000
Harald Welte805f6442009-07-28 18:25:29 +0200647static unsigned int next_udp_port = RTP_PORT_BASE;
Harald Welteead7a7b2009-07-28 00:01:58 +0200648
Harald Welte647db842013-02-03 12:06:58 +0100649/*! \brief bind a RTP socket to a specific local address
650 * \param[in] rs RTP socket to be bound
651 * \param[in] ip local IP address to which socket is to be bound
652 */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200653int rtp_socket_bind(struct rtp_socket *rs, uint32_t ip)
Harald Welteead7a7b2009-07-28 00:01:58 +0200654{
Harald Welte805f6442009-07-28 18:25:29 +0200655 int rc = -EIO;
656 struct in_addr ia;
657
658 ia.s_addr = htonl(ip);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200659 DEBUGP(DLMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
Harald Welte805f6442009-07-28 18:25:29 +0200660 inet_ntoa(ia));
Harald Welteead7a7b2009-07-28 00:01:58 +0200661
662 /* try to bind to a consecutive pair of ports */
Harald Welte805f6442009-07-28 18:25:29 +0200663 for (next_udp_port = next_udp_port % 0xffff;
664 next_udp_port < 0xffff; next_udp_port += 2) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200665 rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
666 if (rc != 0)
667 continue;
668
669 rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
670 if (rc == 0)
671 break;
672 }
Harald Welte805f6442009-07-28 18:25:29 +0200673 if (rc < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200674 DEBUGPC(DLMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200675 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200676 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200677
Harald Welte805f6442009-07-28 18:25:29 +0200678 ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200679 DEBUGPC(DLMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
Harald Welte805f6442009-07-28 18:25:29 +0200680 inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
Harald Welteead7a7b2009-07-28 00:01:58 +0200681 return ntohs(rs->rtp.sin_local.sin_port);
682}
683
684static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200685 uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200686{
Harald Welte805f6442009-07-28 18:25:29 +0200687 int rc;
688 socklen_t alen = sizeof(rss->sin_local);
689
Harald Welteead7a7b2009-07-28 00:01:58 +0200690 rss->sin_remote.sin_family = AF_INET;
691 rss->sin_remote.sin_addr.s_addr = htonl(ip);
692 rss->sin_remote.sin_port = htons(port);
693
Harald Welte805f6442009-07-28 18:25:29 +0200694 rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
695 sizeof(rss->sin_remote));
696 if (rc < 0)
697 return rc;
698
699 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
700 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200701}
702
Harald Welte647db842013-02-03 12:06:58 +0100703/*! \brief 'connect' a RTP socket to a remote peer
704 * \param[in] rs RTP socket to be connected
705 * \param[in] ip remote IP address to which to connect
706 * \param[in] port remote UDP port number to which to connect
707 */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200708int rtp_socket_connect(struct rtp_socket *rs, uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200709{
710 int rc;
Harald Welte805f6442009-07-28 18:25:29 +0200711 struct in_addr ia;
712
713 ia.s_addr = htonl(ip);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200714 DEBUGP(DLMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
Harald Welte805f6442009-07-28 18:25:29 +0200715 rs, inet_ntoa(ia), port);
Harald Welteead7a7b2009-07-28 00:01:58 +0200716
717 rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
718 if (rc < 0)
719 return rc;
720
721 return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
722}
723
Harald Welte647db842013-02-03 12:06:58 +0100724/*! \brief bind two RTP/RTCP sockets together in the proxy
725 * \param[in] this First RTP socket
726 * \param[in] other Second RTP socket
727 */
Harald Welteead7a7b2009-07-28 00:01:58 +0200728int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
729{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200730 DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
Harald Welte805f6442009-07-28 18:25:29 +0200731 this, other);
732
Harald Welteead7a7b2009-07-28 00:01:58 +0200733 this->rx_action = RTP_PROXY;
734 this->proxy.other_sock = other;
735
736 other->rx_action = RTP_PROXY;
737 other->proxy.other_sock = this;
738
739 return 0;
740}
741
Harald Welte647db842013-02-03 12:06:58 +0100742/*! \brief bind RTP/RTCP socket to application, disabling proxy
743 * \param[in] this RTP socket
744 * \param[in] net gsm_network argument to trau_tx_to_mncc()
745 * \param[in] callref callref argument to trau_tx_to_mncc()
746 */
Harald Welte9fb1f102009-12-20 17:07:23 +0100747int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200748 uint32_t callref)
Harald Welteda7ab742009-12-19 22:23:05 +0100749{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200750 DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100751 this, callref);
752
753 if (callref) {
754 this->rx_action = RTP_RECV_UPSTREAM;
755 this->receive.net = net;
756 this->receive.callref = callref;
757 } else
758 this->rx_action = RTP_NONE;
759
760 return 0;
761}
762
Harald Welteead7a7b2009-07-28 00:01:58 +0200763static void free_tx_queue(struct rtp_sub_socket *rss)
764{
765 struct msgb *msg;
766
767 while ((msg = msgb_dequeue(&rss->tx_queue)))
768 msgb_free(msg);
769}
770
Harald Welte647db842013-02-03 12:06:58 +0100771/*! \brief Free/release a previously allocated RTP socket
772 * \param[in[] rs RTP/RTCP socket to be released
773 */
Harald Welteead7a7b2009-07-28 00:01:58 +0200774int rtp_socket_free(struct rtp_socket *rs)
775{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200776 DEBUGP(DLMUX, "rtp_socket_free(rs=%p)\n", rs);
Harald Welteead7a7b2009-07-28 00:01:58 +0200777
778 /* make sure we don't leave references dangling to us */
779 if (rs->rx_action == RTP_PROXY &&
780 rs->proxy.other_sock)
781 rs->proxy.other_sock->proxy.other_sock = NULL;
782
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200783 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200784 close(rs->rtp.bfd.fd);
785 free_tx_queue(&rs->rtp);
786
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200787 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200788 close(rs->rtcp.bfd.fd);
789 free_tx_queue(&rs->rtcp);
790
791 talloc_free(rs);
792
793 return 0;
794}