blob: 6b38ee5fee6c0991c18d06ee5c6d70efa1c934ce [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>
Neels Hofmeyr93bafb62017-01-13 03:12:08 +010032#include <osmocom/core/utils.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020033#include <openbsc/gsm_data.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010034#include <osmocom/core/msgb.h>
35#include <osmocom/core/select.h>
Harald Welte805f6442009-07-28 18:25:29 +020036#include <openbsc/debug.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020037#include <openbsc/rtp_proxy.h>
Harald Weltef142c972011-05-24 13:25:38 +020038#include <openbsc/mncc.h>
Alexander Huemer0a654612011-09-06 00:09:49 +020039#include <openbsc/trau_upqueue.h>
Holger Hans Peter Freytherd0e171a2015-03-22 09:51:43 +010040
41#include <osmocom/netif/rtp.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020042
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020043/* attempt to determine byte order */
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020044#include <sys/param.h>
45#include <limits.h>
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#define RTCP_TYPE_SDES 202
62
63#define RTCP_IE_CNAME 1
64
Harald Welteda7ab742009-12-19 22:23:05 +010065
66#define RTP_VERSION 2
67
Harald Weltea87f8f92014-05-17 13:43:01 +020068/* 33 for FR, all other codecs have smaller size */
69#define MAX_RTP_PAYLOAD_LEN 33
70
Harald Welteda7ab742009-12-19 22:23:05 +010071/* decode an rtp frame and create a new buffer with payload */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020072static int rtp_decode(struct msgb *msg, uint32_t callref, struct msgb **data)
Harald Welteda7ab742009-12-19 22:23:05 +010073{
74 struct msgb *new_msg;
75 struct gsm_data_frame *frame;
76 struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
77 struct rtp_x_hdr *rtpxh;
Harald Weltea87f8f92014-05-17 13:43:01 +020078 uint8_t *payload, *payload_out;
Harald Welteda7ab742009-12-19 22:23:05 +010079 int payload_len;
80 int msg_type;
81 int x_len;
82
83 if (msg->len < 12) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +020084 DEBUGPC(DLMUX, "received RTP frame too short (len = %d)\n",
Harald Welteda7ab742009-12-19 22:23:05 +010085 msg->len);
86 return -EINVAL;
87 }
88 if (rtph->version != RTP_VERSION) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +020089 DEBUGPC(DLMUX, "received RTP version %d not supported.\n",
Harald Welteda7ab742009-12-19 22:23:05 +010090 rtph->version);
91 return -EINVAL;
92 }
93 payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
94 payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
95 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +020096 DEBUGPC(DLMUX, "received RTP frame too short (len = %d, "
Harald Welteda7ab742009-12-19 22:23:05 +010097 "csrc count = %d)\n", msg->len, rtph->csrc_count);
98 return -EINVAL;
99 }
100 if (rtph->extension) {
101 if (payload_len < sizeof(struct rtp_x_hdr)) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200102 DEBUGPC(DLMUX, "received RTP frame too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100103 "extension header\n");
104 return -EINVAL;
105 }
106 rtpxh = (struct rtp_x_hdr *)payload;
107 x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
108 payload += x_len;
109 payload_len -= x_len;
110 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200111 DEBUGPC(DLMUX, "received RTP frame too short, "
Harald Welteda7ab742009-12-19 22:23:05 +0100112 "extension header exceeds frame length\n");
113 return -EINVAL;
114 }
115 }
116 if (rtph->padding) {
Jacob Erlbeckd9e40392013-11-21 19:05:42 +0100117 if (payload_len < 1) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200118 DEBUGPC(DLMUX, "received RTP frame too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100119 "padding length\n");
120 return -EINVAL;
121 }
122 payload_len -= payload[payload_len - 1];
123 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200124 DEBUGPC(DLMUX, "received RTP frame with padding "
Harald Welteda7ab742009-12-19 22:23:05 +0100125 "greater than payload\n");
126 return -EINVAL;
127 }
128 }
129
130 switch (rtph->payload_type) {
131 case RTP_PT_GSM_FULL:
132 msg_type = GSM_TCHF_FRAME;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100133 if (payload_len != RTP_LEN_GSM_FULL) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200134 DEBUGPC(DLMUX, "received RTP full rate frame with "
Andreas Eversberg88012b62014-01-22 10:05:51 +0100135 "payload length != %d (len = %d)\n",
136 RTP_LEN_GSM_FULL, payload_len);
Harald Welteda7ab742009-12-19 22:23:05 +0100137 return -EINVAL;
138 }
139 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100140 case RTP_PT_GSM_EFR:
141 msg_type = GSM_TCHF_FRAME_EFR;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100142 if (payload_len != RTP_LEN_GSM_EFR) {
143 DEBUGPC(DLMUX, "received RTP extended full rate frame "
144 "with payload length != %d (len = %d)\n",
145 RTP_LEN_GSM_EFR, payload_len);
146 return -EINVAL;
147 }
Harald Welteaca8f152009-12-19 23:06:41 +0100148 break;
Andreas Eversberg63bfdd82014-01-17 19:06:38 +0100149 case RTP_PT_GSM_HALF:
150 msg_type = GSM_TCHH_FRAME;
151 if (payload_len != RTP_LEN_GSM_HALF) {
152 DEBUGPC(DLMUX, "received RTP half rate frame with "
153 "payload length != %d (len = %d)\n",
154 RTP_LEN_GSM_HALF, payload_len);
155 return -EINVAL;
156 }
157 break;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100158 case RTP_PT_AMR:
Harald Welteec757982014-05-18 18:32:05 +0200159 msg_type = GSM_TCH_FRAME_AMR;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100160 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100161 default:
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200162 DEBUGPC(DLMUX, "received RTP frame with unknown payload "
Harald Welteda7ab742009-12-19 22:23:05 +0100163 "type %d\n", rtph->payload_type);
164 return -EINVAL;
165 }
166
Maxe152ffe2016-06-10 17:21:05 +0200167 if (payload_len > MAX_RTP_PAYLOAD_LEN ||
168 (rtph->payload_type == RTP_PT_AMR &&
169 payload_len > MAX_RTP_PAYLOAD_LEN - 1)) {
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100170 DEBUGPC(DLMUX, "RTP payload too large (%d octets)\n",
171 payload_len);
172 return -EINVAL;
173 }
174
Harald Weltea87f8f92014-05-17 13:43:01 +0200175 /* always allocate for the maximum possible size to avoid
176 * fragmentation */
177 new_msg = msgb_alloc(sizeof(struct gsm_data_frame) +
Harald Welteed04fcc2015-12-28 21:03:10 +0100178 MAX_RTP_PAYLOAD_LEN+1, "GSM-DATA (TCH)");
Harald Weltea87f8f92014-05-17 13:43:01 +0200179
Harald Welteda7ab742009-12-19 22:23:05 +0100180 if (!new_msg)
181 return -ENOMEM;
Holger Hans Peter Freyther652cdb42014-07-24 21:09:31 +0200182 frame = (struct gsm_data_frame *) msgb_put(new_msg, sizeof(struct gsm_data_frame));
Harald Welteda7ab742009-12-19 22:23:05 +0100183 frame->msg_type = msg_type;
184 frame->callref = callref;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100185 if (rtph->payload_type == RTP_PT_AMR) {
Harald Weltea87f8f92014-05-17 13:43:01 +0200186 /* for FR/HR/EFR the length is implicit. In AMR, we
187 * need to make it explicit by using the first byte of
188 * the data[] buffer as length byte */
189 uint8_t *data0 = msgb_put(new_msg, 1);
190 *data0 = payload_len;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100191 }
Harald Weltea87f8f92014-05-17 13:43:01 +0200192 payload_out = msgb_put(new_msg, payload_len);
193 memcpy(payload_out, payload, payload_len);
Harald Welteda7ab742009-12-19 22:23:05 +0100194
195 *data = new_msg;
196 return 0;
197}
198
Harald Welte647db842013-02-03 12:06:58 +0100199/*! \brief encode and send a rtp frame
200 * \param[in] rs RTP socket through which we shall send
201 * \param[in] frame GSM RTP frame to be sent
202 */
Harald Welteda7ab742009-12-19 22:23:05 +0100203int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
204{
205 struct rtp_sub_socket *rss = &rs->rtp;
206 struct msgb *msg;
207 struct rtp_hdr *rtph;
Harald Weltea87f8f92014-05-17 13:43:01 +0200208 uint8_t *payload;
Harald Welteda7ab742009-12-19 22:23:05 +0100209 int payload_type;
210 int payload_len;
211 int duration; /* in samples */
Andreas Eversberg9967a572012-01-20 20:33:37 +0100212 int is_bfi = 0;
Harald Welteda7ab742009-12-19 22:23:05 +0100213
214 if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
215 /* initialize sequences */
216 rs->tx_action = RTP_SEND_DOWNSTREAM;
217 rs->transmit.ssrc = rand();
218 rs->transmit.sequence = random();
219 rs->transmit.timestamp = random();
220 }
221
222 switch (frame->msg_type) {
223 case GSM_TCHF_FRAME:
224 payload_type = RTP_PT_GSM_FULL;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100225 payload_len = RTP_LEN_GSM_FULL;
226 duration = RTP_GSM_DURATION;
Harald Welteda7ab742009-12-19 22:23:05 +0100227 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100228 case GSM_TCHF_FRAME_EFR:
229 payload_type = RTP_PT_GSM_EFR;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100230 payload_len = RTP_LEN_GSM_EFR;
231 duration = RTP_GSM_DURATION;
Harald Welteaca8f152009-12-19 23:06:41 +0100232 break;
Andreas Eversberg63bfdd82014-01-17 19:06:38 +0100233 case GSM_TCHH_FRAME:
234 payload_type = RTP_PT_GSM_HALF;
235 payload_len = RTP_LEN_GSM_HALF;
236 duration = RTP_GSM_DURATION;
237 break;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100238 case GSM_TCH_FRAME_AMR:
239 payload_type = RTP_PT_AMR;
240 payload_len = frame->data[0];
241 duration = RTP_GSM_DURATION;
242 break;
Andreas Eversberg9967a572012-01-20 20:33:37 +0100243 case GSM_BAD_FRAME:
244 payload_type = 0;
245 payload_len = 0;
246 duration = RTP_GSM_DURATION;
247 is_bfi = 1;
248 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100249 default:
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200250 DEBUGPC(DLMUX, "unsupported message type %d\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100251 frame->msg_type);
252 return -EINVAL;
253 }
254
Harald Weltea87f8f92014-05-17 13:43:01 +0200255 if (payload_len > MAX_RTP_PAYLOAD_LEN) {
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100256 DEBUGPC(DLMUX, "RTP payload too large (%d octets)\n",
257 payload_len);
258 return -EINVAL;
259 }
260
Andreas Eversberg9967a572012-01-20 20:33:37 +0100261 if (is_bfi) {
262 /* In case of a bad frame, just count and drop packet. */
263 rs->transmit.timestamp += duration;
264 rs->transmit.sequence++;
265 return 0;
266 }
267
Harald Weltea87f8f92014-05-17 13:43:01 +0200268 msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM");
Harald Welteda7ab742009-12-19 22:23:05 +0100269 if (!msg)
270 return -ENOMEM;
Harald Weltea87f8f92014-05-17 13:43:01 +0200271 rtph = (struct rtp_hdr *) msgb_put(msg, sizeof(struct rtp_hdr));
Harald Welteda7ab742009-12-19 22:23:05 +0100272 rtph->version = RTP_VERSION;
273 rtph->padding = 0;
274 rtph->extension = 0;
275 rtph->csrc_count = 0;
276 rtph->marker = 0;
277 rtph->payload_type = payload_type;
278 rtph->sequence = htons(rs->transmit.sequence++);
279 rtph->timestamp = htonl(rs->transmit.timestamp);
280 rs->transmit.timestamp += duration;
281 rtph->ssrc = htonl(rs->transmit.ssrc);
Harald Weltea87f8f92014-05-17 13:43:01 +0200282
283 payload = msgb_put(msg, payload_len);
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100284 if (frame->msg_type == GSM_TCH_FRAME_AMR)
Harald Weltea87f8f92014-05-17 13:43:01 +0200285 memcpy(payload, frame->data + 1, payload_len);
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100286 else
Harald Weltea87f8f92014-05-17 13:43:01 +0200287 memcpy(payload, frame->data, payload_len);
Harald Welteda7ab742009-12-19 22:23:05 +0100288 msgb_enqueue(&rss->tx_queue, msg);
289 rss->bfd.when |= BSC_FD_WRITE;
290
291 return 0;
292}
293
Holger Hans Peter Freyther89acf062009-07-29 06:46:26 +0200294/* iterate over all chunks in one RTCP message, look for CNAME IEs and
Harald Welte805f6442009-07-28 18:25:29 +0200295 * replace all of those with 'new_cname' */
296static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200297 uint16_t *rtcp_len, const char *new_cname)
Harald Welte805f6442009-07-28 18:25:29 +0200298{
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200299 uint8_t *rtcp_end;
300 uint8_t *cur = (uint8_t *) rh;
301 uint8_t tag, len = 0;
Harald Welte805f6442009-07-28 18:25:29 +0200302
303 rtcp_end = cur + *rtcp_len;
304 /* move cur to end of RTP header */
Harald Welte198f3f52009-07-29 10:46:41 +0200305 cur += sizeof(*rh);
Harald Welte805f6442009-07-28 18:25:29 +0200306
307 /* iterate over Chunks */
308 while (cur+4 < rtcp_end) {
309 /* skip four bytes SSRC/CSRC */
310 cur += 4;
311
312 /* iterate over IE's inside the chunk */
313 while (cur+1 < rtcp_end) {
314 tag = *cur++;
315 if (tag == 0) {
316 /* end of chunk, skip additional zero */
Daniel Willmann45fcb852014-05-21 15:46:43 +0200317 while ((*cur++ == 0) && (cur < rtcp_end)) { }
Harald Welte805f6442009-07-28 18:25:29 +0200318 break;
319 }
320 len = *cur++;
321
322 if (tag == RTCP_IE_CNAME) {
323 /* we've found the CNAME, lets mangle it */
324 if (len < strlen(new_cname)) {
325 /* we need to make more space */
326 int increase = strlen(new_cname) - len;
327
328 msgb_push(msg, increase);
329 memmove(cur+len+increase, cur+len,
330 rtcp_end - (cur+len));
331 /* FIXME: we have to respect RTCP
332 * padding/alignment rules! */
333 len += increase;
334 *(cur-1) += increase;
335 rtcp_end += increase;
336 *rtcp_len += increase;
337 }
338 /* copy new CNAME into message */
339 memcpy(cur, new_cname, strlen(new_cname));
340 /* FIXME: zero the padding in case new CNAME
341 * is smaller than old one !!! */
342 }
343 cur += len;
344 }
345 }
346
347 return 0;
348}
349
350static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
351{
352 struct rtp_sub_socket *rss = &rs->rtcp;
353 struct rtcp_hdr *rtph;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200354 uint16_t old_len;
Harald Welte805f6442009-07-28 18:25:29 +0200355 int rc;
356
357 if (!mangle_rtcp_cname)
358 return 0;
359
Harald Welteda7ab742009-12-19 22:23:05 +0100360 printf("RTCP\n");
Harald Welte805f6442009-07-28 18:25:29 +0200361 /* iterate over list of RTCP messages */
362 rtph = (struct rtcp_hdr *)msg->data;
Harald Welteda7ab742009-12-19 22:23:05 +0100363 while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
Harald Welte805f6442009-07-28 18:25:29 +0200364 old_len = (ntohs(rtph->length) + 1) * 4;
Harald Welteda7ab742009-12-19 22:23:05 +0100365 if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200366 DEBUGPC(DLMUX, "received RTCP packet too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100367 "length element\n");
368 return -EINVAL;
369 }
Harald Welte805f6442009-07-28 18:25:29 +0200370 if (rtph->type == RTCP_TYPE_SDES) {
371 char new_cname[255];
Neels Hofmeyr93bafb62017-01-13 03:12:08 +0100372 osmo_strlcpy(new_cname,
373 inet_ntoa(rss->sin_local.sin_addr),
374 sizeof(new_cname));
Harald Welte805f6442009-07-28 18:25:29 +0200375 rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
376 new_cname);
377 if (rc < 0)
378 return rc;
379 }
380 rtph = (void *)rtph + old_len;
381 }
382
383 return 0;
384}
385
Harald Welteead7a7b2009-07-28 00:01:58 +0200386/* read from incoming RTP/RTCP socket */
387static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
388{
389 int rc;
390 struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
Harald Welteda7ab742009-12-19 22:23:05 +0100391 struct msgb *new_msg;
Harald Welteead7a7b2009-07-28 00:01:58 +0200392 struct rtp_sub_socket *other_rss;
393
394 if (!msg)
395 return -ENOMEM;
396
397 rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
Andreas Eversbergcf7557a2012-03-16 08:14:23 +0100398 if (rc == 0) {
399 rss->bfd.when &= ~BSC_FD_READ;
400 goto out_free;
401 } else if (rc < 0) {
402 /* Ignore "connection refused". this happens, If we open the
403 * socket faster than the remote side. */
404 if (errno == ECONNREFUSED)
405 goto out_free;
406 DEBUGPC(DLMUX, "Read of RTP socket (%p) failed (errno %d, "
407 "%s)\n", rs, errno, strerror(errno));
Harald Welteead7a7b2009-07-28 00:01:58 +0200408 rss->bfd.when &= ~BSC_FD_READ;
Holger Hans Peter Freyther37b5ce52015-03-28 17:30:31 +0100409 goto out_free;
Harald Welteead7a7b2009-07-28 00:01:58 +0200410 }
411
412 msgb_put(msg, rc);
413
414 switch (rs->rx_action) {
415 case RTP_PROXY:
Harald Welte805f6442009-07-28 18:25:29 +0200416 if (!rs->proxy.other_sock) {
417 rc = -EIO;
418 goto out_free;
419 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200420 if (rss->bfd.priv_nr == RTP_PRIV_RTP)
421 other_rss = &rs->proxy.other_sock->rtp;
Harald Welte805f6442009-07-28 18:25:29 +0200422 else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200423 other_rss = &rs->proxy.other_sock->rtcp;
Harald Welte805f6442009-07-28 18:25:29 +0200424 /* modify RTCP SDES CNAME */
425 rc = rtcp_mangle(msg, rs);
426 if (rc < 0)
427 goto out_free;
428 } else {
429 rc = -EINVAL;
430 goto out_free;
Harald Welteead7a7b2009-07-28 00:01:58 +0200431 }
432 msgb_enqueue(&other_rss->tx_queue, msg);
433 other_rss->bfd.when |= BSC_FD_WRITE;
434 break;
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200435
436 case RTP_RECV_UPSTREAM:
Harald Welteda7ab742009-12-19 22:23:05 +0100437 if (!rs->receive.callref || !rs->receive.net) {
438 rc = -EIO;
439 goto out_free;
440 }
441 if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
442 if (!mangle_rtcp_cname) {
443 msgb_free(msg);
444 break;
445 }
446 /* modify RTCP SDES CNAME */
447 rc = rtcp_mangle(msg, rs);
448 if (rc < 0)
449 goto out_free;
450 msgb_enqueue(&rss->tx_queue, msg);
451 rss->bfd.when |= BSC_FD_WRITE;
452 break;
453 }
454 if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
455 rc = -EINVAL;
456 goto out_free;
457 }
458 rc = rtp_decode(msg, rs->receive.callref, &new_msg);
459 if (rc < 0)
460 goto out_free;
461 msgb_free(msg);
Harald Welte31c00f72011-03-03 23:29:05 +0100462 trau_tx_to_mncc(rs->receive.net, new_msg);
Harald Welteda7ab742009-12-19 22:23:05 +0100463 break;
464
465 case RTP_NONE: /* if socket exists, but disabled by app */
466 msgb_free(msg);
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200467 break;
Harald Welteead7a7b2009-07-28 00:01:58 +0200468 }
469
Harald Welteda7ab742009-12-19 22:23:05 +0100470 return 0;
Harald Welte805f6442009-07-28 18:25:29 +0200471
472out_free:
473 msgb_free(msg);
474 return rc;
Harald Welteead7a7b2009-07-28 00:01:58 +0200475}
476
Harald Welte647db842013-02-03 12:06:58 +0100477/* \brief write from tx_queue to RTP/RTCP socket */
Harald Welteead7a7b2009-07-28 00:01:58 +0200478static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
479{
480 struct msgb *msg;
481 int written;
482
483 msg = msgb_dequeue(&rss->tx_queue);
484 if (!msg) {
485 rss->bfd.when &= ~BSC_FD_WRITE;
486 return 0;
487 }
488
489 written = write(rss->bfd.fd, msg->data, msg->len);
490 if (written < msg->len) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200491 LOGP(DLMIB, LOGL_ERROR, "short write");
Harald Welteead7a7b2009-07-28 00:01:58 +0200492 msgb_free(msg);
493 return -EIO;
494 }
495
496 msgb_free(msg);
497
498 return 0;
499}
500
501
Harald Welte647db842013-02-03 12:06:58 +0100502/*! \brief callback for the select.c:bfd_* layer */
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200503static int rtp_bfd_cb(struct osmo_fd *bfd, unsigned int flags)
Harald Welteead7a7b2009-07-28 00:01:58 +0200504{
505 struct rtp_socket *rs = bfd->data;
506 struct rtp_sub_socket *rss;
507
508 switch (bfd->priv_nr) {
509 case RTP_PRIV_RTP:
510 rss = &rs->rtp;
511 break;
512 case RTP_PRIV_RTCP:
513 rss = &rs->rtcp;
514 break;
515 default:
516 return -EINVAL;
517 }
518
519 if (flags & BSC_FD_READ)
520 rtp_socket_read(rs, rss);
521
522 if (flags & BSC_FD_WRITE)
523 rtp_socket_write(rs, rss);
524
525 return 0;
526}
527
Harald Welte647db842013-02-03 12:06:58 +0100528/*! \brief initialize one rtp sub-socket */
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200529static void init_rss(struct rtp_sub_socket *rss,
Harald Welteead7a7b2009-07-28 00:01:58 +0200530 struct rtp_socket *rs, int fd, int priv_nr)
531{
532 /* initialize bfd */
533 rss->bfd.fd = fd;
534 rss->bfd.data = rs;
535 rss->bfd.priv_nr = priv_nr;
536 rss->bfd.cb = rtp_bfd_cb;
537}
538
Harald Welte647db842013-02-03 12:06:58 +0100539/*! \brief create a new RTP/RTCP socket and bind it */
Harald Welteead7a7b2009-07-28 00:01:58 +0200540struct rtp_socket *rtp_socket_create(void)
541{
542 int rc;
543 struct rtp_socket *rs;
544
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200545 DEBUGP(DLMUX, "rtp_socket_create(): ");
Harald Welte805f6442009-07-28 18:25:29 +0200546
Harald Welteead7a7b2009-07-28 00:01:58 +0200547 rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
548 if (!rs)
549 return NULL;
550
551 INIT_LLIST_HEAD(&rs->rtp.tx_queue);
552 INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
553
554 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
555 if (rc < 0)
556 goto out_free;
557
558 init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200559 rc = osmo_fd_register(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200560 if (rc < 0)
561 goto out_rtp_socket;
562
563 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
564 if (rc < 0)
565 goto out_rtp_bfd;
566
567 init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200568 rc = osmo_fd_register(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200569 if (rc < 0)
570 goto out_rtcp_socket;
571
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200572 DEBUGPC(DLMUX, "success\n");
Harald Welte805f6442009-07-28 18:25:29 +0200573
574 rc = rtp_socket_bind(rs, INADDR_ANY);
575 if (rc < 0)
576 goto out_rtcp_bfd;
577
Harald Welteead7a7b2009-07-28 00:01:58 +0200578 return rs;
579
Harald Welte805f6442009-07-28 18:25:29 +0200580out_rtcp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200581 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200582out_rtcp_socket:
583 close(rs->rtcp.bfd.fd);
584out_rtp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200585 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200586out_rtp_socket:
587 close(rs->rtp.bfd.fd);
588out_free:
589 talloc_free(rs);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200590 DEBUGPC(DLMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200591 return NULL;
592}
593
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200594static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, uint32_t ip,
595 uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200596{
Harald Welte805f6442009-07-28 18:25:29 +0200597 int rc;
598 socklen_t alen = sizeof(rss->sin_local);
599
Harald Welteead7a7b2009-07-28 00:01:58 +0200600 rss->sin_local.sin_family = AF_INET;
601 rss->sin_local.sin_addr.s_addr = htonl(ip);
602 rss->sin_local.sin_port = htons(port);
603 rss->bfd.when |= BSC_FD_READ;
604
Harald Welte805f6442009-07-28 18:25:29 +0200605 rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
606 sizeof(rss->sin_local));
607 if (rc < 0)
608 return rc;
609
610 /* retrieve the address we actually bound to, in case we
611 * passed INADDR_ANY as IP address */
612 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
613 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200614}
615
616#define RTP_PORT_BASE 30000
Harald Welte805f6442009-07-28 18:25:29 +0200617static unsigned int next_udp_port = RTP_PORT_BASE;
Harald Welteead7a7b2009-07-28 00:01:58 +0200618
Harald Welte647db842013-02-03 12:06:58 +0100619/*! \brief bind a RTP socket to a specific local address
620 * \param[in] rs RTP socket to be bound
621 * \param[in] ip local IP address to which socket is to be bound
622 */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200623int rtp_socket_bind(struct rtp_socket *rs, uint32_t ip)
Harald Welteead7a7b2009-07-28 00:01:58 +0200624{
Harald Welte805f6442009-07-28 18:25:29 +0200625 int rc = -EIO;
626 struct in_addr ia;
627
628 ia.s_addr = htonl(ip);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200629 DEBUGP(DLMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
Harald Welte805f6442009-07-28 18:25:29 +0200630 inet_ntoa(ia));
Harald Welteead7a7b2009-07-28 00:01:58 +0200631
632 /* try to bind to a consecutive pair of ports */
Harald Welte805f6442009-07-28 18:25:29 +0200633 for (next_udp_port = next_udp_port % 0xffff;
634 next_udp_port < 0xffff; next_udp_port += 2) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200635 rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
636 if (rc != 0)
637 continue;
638
639 rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
640 if (rc == 0)
641 break;
642 }
Harald Welte805f6442009-07-28 18:25:29 +0200643 if (rc < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200644 DEBUGPC(DLMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200645 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200646 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200647
Harald Welte805f6442009-07-28 18:25:29 +0200648 ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200649 DEBUGPC(DLMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
Harald Welte805f6442009-07-28 18:25:29 +0200650 inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
Harald Welteead7a7b2009-07-28 00:01:58 +0200651 return ntohs(rs->rtp.sin_local.sin_port);
652}
653
654static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200655 uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200656{
Harald Welte805f6442009-07-28 18:25:29 +0200657 int rc;
658 socklen_t alen = sizeof(rss->sin_local);
659
Harald Welteead7a7b2009-07-28 00:01:58 +0200660 rss->sin_remote.sin_family = AF_INET;
661 rss->sin_remote.sin_addr.s_addr = htonl(ip);
662 rss->sin_remote.sin_port = htons(port);
663
Harald Welte805f6442009-07-28 18:25:29 +0200664 rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
665 sizeof(rss->sin_remote));
666 if (rc < 0)
667 return rc;
668
669 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
670 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200671}
672
Harald Welte647db842013-02-03 12:06:58 +0100673/*! \brief 'connect' a RTP socket to a remote peer
674 * \param[in] rs RTP socket to be connected
675 * \param[in] ip remote IP address to which to connect
676 * \param[in] port remote UDP port number to which to connect
677 */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200678int rtp_socket_connect(struct rtp_socket *rs, uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200679{
680 int rc;
Harald Welte805f6442009-07-28 18:25:29 +0200681 struct in_addr ia;
682
683 ia.s_addr = htonl(ip);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200684 DEBUGP(DLMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
Harald Welte805f6442009-07-28 18:25:29 +0200685 rs, inet_ntoa(ia), port);
Harald Welteead7a7b2009-07-28 00:01:58 +0200686
687 rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
688 if (rc < 0)
689 return rc;
690
691 return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
692}
693
Harald Welte647db842013-02-03 12:06:58 +0100694/*! \brief bind two RTP/RTCP sockets together in the proxy
695 * \param[in] this First RTP socket
696 * \param[in] other Second RTP socket
697 */
Harald Welteead7a7b2009-07-28 00:01:58 +0200698int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
699{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200700 DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
Harald Welte805f6442009-07-28 18:25:29 +0200701 this, other);
702
Harald Welteead7a7b2009-07-28 00:01:58 +0200703 this->rx_action = RTP_PROXY;
704 this->proxy.other_sock = other;
705
706 other->rx_action = RTP_PROXY;
707 other->proxy.other_sock = this;
708
709 return 0;
710}
711
Harald Welte647db842013-02-03 12:06:58 +0100712/*! \brief bind RTP/RTCP socket to application, disabling proxy
713 * \param[in] this RTP socket
714 * \param[in] net gsm_network argument to trau_tx_to_mncc()
715 * \param[in] callref callref argument to trau_tx_to_mncc()
716 */
Harald Welte9fb1f102009-12-20 17:07:23 +0100717int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200718 uint32_t callref)
Harald Welteda7ab742009-12-19 22:23:05 +0100719{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200720 DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100721 this, callref);
722
723 if (callref) {
724 this->rx_action = RTP_RECV_UPSTREAM;
725 this->receive.net = net;
726 this->receive.callref = callref;
727 } else
728 this->rx_action = RTP_NONE;
729
730 return 0;
731}
732
Harald Welteead7a7b2009-07-28 00:01:58 +0200733static void free_tx_queue(struct rtp_sub_socket *rss)
734{
735 struct msgb *msg;
736
737 while ((msg = msgb_dequeue(&rss->tx_queue)))
738 msgb_free(msg);
739}
740
Harald Welte647db842013-02-03 12:06:58 +0100741/*! \brief Free/release a previously allocated RTP socket
742 * \param[in[] rs RTP/RTCP socket to be released
743 */
Harald Welteead7a7b2009-07-28 00:01:58 +0200744int rtp_socket_free(struct rtp_socket *rs)
745{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200746 DEBUGP(DLMUX, "rtp_socket_free(rs=%p)\n", rs);
Harald Welteead7a7b2009-07-28 00:01:58 +0200747
748 /* make sure we don't leave references dangling to us */
749 if (rs->rx_action == RTP_PROXY &&
750 rs->proxy.other_sock)
751 rs->proxy.other_sock->proxy.other_sock = NULL;
752
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200753 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200754 close(rs->rtp.bfd.fd);
755 free_tx_queue(&rs->rtp);
756
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200757 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200758 close(rs->rtcp.bfd.fd);
759 free_tx_queue(&rs->rtcp);
760
761 talloc_free(rs);
762
763 return 0;
764}