blob: 8c982c97674451729d0fc82f3c1df3c0358d3f57 [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 Freytherd0e171a2015-03-22 09:51:43 +010039
40#include <osmocom/netif/rtp.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020041
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020042/* attempt to determine byte order */
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020043#include <sys/param.h>
44#include <limits.h>
45
Harald Welteead7a7b2009-07-28 00:01:58 +020046static LLIST_HEAD(rtp_sockets);
47
Harald Welte805f6442009-07-28 18:25:29 +020048/* should we mangle the CNAME inside SDES of RTCP packets? We disable
49 * this by default, as it seems to be not needed */
50static int mangle_rtcp_cname = 0;
51
Harald Welteead7a7b2009-07-28 00:01:58 +020052enum rtp_bfd_priv {
53 RTP_PRIV_NONE,
54 RTP_PRIV_RTP,
55 RTP_PRIV_RTCP
56};
57
58#define RTP_ALLOC_SIZE 1500
59
Harald Welte805f6442009-07-28 18:25:29 +020060#define RTCP_TYPE_SDES 202
61
62#define RTCP_IE_CNAME 1
63
Harald Welteda7ab742009-12-19 22:23:05 +010064
65#define RTP_VERSION 2
66
Harald Weltea87f8f92014-05-17 13:43:01 +020067/* 33 for FR, all other codecs have smaller size */
68#define MAX_RTP_PAYLOAD_LEN 33
69
Harald Welteda7ab742009-12-19 22:23:05 +010070/* decode an rtp frame and create a new buffer with payload */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020071static int rtp_decode(struct msgb *msg, uint32_t callref, struct msgb **data)
Harald Welteda7ab742009-12-19 22:23:05 +010072{
73 struct msgb *new_msg;
74 struct gsm_data_frame *frame;
75 struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
76 struct rtp_x_hdr *rtpxh;
Harald Weltea87f8f92014-05-17 13:43:01 +020077 uint8_t *payload, *payload_out;
Harald Welteda7ab742009-12-19 22:23:05 +010078 int payload_len;
79 int msg_type;
80 int x_len;
81
82 if (msg->len < 12) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +020083 DEBUGPC(DLMUX, "received RTP frame too short (len = %d)\n",
Harald Welteda7ab742009-12-19 22:23:05 +010084 msg->len);
85 return -EINVAL;
86 }
87 if (rtph->version != RTP_VERSION) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +020088 DEBUGPC(DLMUX, "received RTP version %d not supported.\n",
Harald Welteda7ab742009-12-19 22:23:05 +010089 rtph->version);
90 return -EINVAL;
91 }
92 payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
93 payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
94 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +020095 DEBUGPC(DLMUX, "received RTP frame too short (len = %d, "
Harald Welteda7ab742009-12-19 22:23:05 +010096 "csrc count = %d)\n", msg->len, rtph->csrc_count);
97 return -EINVAL;
98 }
99 if (rtph->extension) {
100 if (payload_len < sizeof(struct rtp_x_hdr)) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200101 DEBUGPC(DLMUX, "received RTP frame too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100102 "extension header\n");
103 return -EINVAL;
104 }
105 rtpxh = (struct rtp_x_hdr *)payload;
106 x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
107 payload += x_len;
108 payload_len -= x_len;
109 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200110 DEBUGPC(DLMUX, "received RTP frame too short, "
Harald Welteda7ab742009-12-19 22:23:05 +0100111 "extension header exceeds frame length\n");
112 return -EINVAL;
113 }
114 }
115 if (rtph->padding) {
Jacob Erlbeckd9e40392013-11-21 19:05:42 +0100116 if (payload_len < 1) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200117 DEBUGPC(DLMUX, "received RTP frame too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100118 "padding length\n");
119 return -EINVAL;
120 }
121 payload_len -= payload[payload_len - 1];
122 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200123 DEBUGPC(DLMUX, "received RTP frame with padding "
Harald Welteda7ab742009-12-19 22:23:05 +0100124 "greater than payload\n");
125 return -EINVAL;
126 }
127 }
128
129 switch (rtph->payload_type) {
130 case RTP_PT_GSM_FULL:
131 msg_type = GSM_TCHF_FRAME;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100132 if (payload_len != RTP_LEN_GSM_FULL) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200133 DEBUGPC(DLMUX, "received RTP full rate frame with "
Andreas Eversberg88012b62014-01-22 10:05:51 +0100134 "payload length != %d (len = %d)\n",
135 RTP_LEN_GSM_FULL, payload_len);
Harald Welteda7ab742009-12-19 22:23:05 +0100136 return -EINVAL;
137 }
138 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100139 case RTP_PT_GSM_EFR:
140 msg_type = GSM_TCHF_FRAME_EFR;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100141 if (payload_len != RTP_LEN_GSM_EFR) {
142 DEBUGPC(DLMUX, "received RTP extended full rate frame "
143 "with payload length != %d (len = %d)\n",
144 RTP_LEN_GSM_EFR, payload_len);
145 return -EINVAL;
146 }
Harald Welteaca8f152009-12-19 23:06:41 +0100147 break;
Andreas Eversberg63bfdd82014-01-17 19:06:38 +0100148 case RTP_PT_GSM_HALF:
149 msg_type = GSM_TCHH_FRAME;
150 if (payload_len != RTP_LEN_GSM_HALF) {
151 DEBUGPC(DLMUX, "received RTP half rate frame with "
152 "payload length != %d (len = %d)\n",
153 RTP_LEN_GSM_HALF, payload_len);
154 return -EINVAL;
155 }
156 break;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100157 case RTP_PT_AMR:
Harald Welteec757982014-05-18 18:32:05 +0200158 msg_type = GSM_TCH_FRAME_AMR;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100159 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100160 default:
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200161 DEBUGPC(DLMUX, "received RTP frame with unknown payload "
Harald Welteda7ab742009-12-19 22:23:05 +0100162 "type %d\n", rtph->payload_type);
163 return -EINVAL;
164 }
165
Harald Weltea87f8f92014-05-17 13:43:01 +0200166 if (payload_len > MAX_RTP_PAYLOAD_LEN) {
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100167 DEBUGPC(DLMUX, "RTP payload too large (%d octets)\n",
168 payload_len);
169 return -EINVAL;
170 }
171
Harald Weltea87f8f92014-05-17 13:43:01 +0200172 /* always allocate for the maximum possible size to avoid
173 * fragmentation */
174 new_msg = msgb_alloc(sizeof(struct gsm_data_frame) +
175 MAX_RTP_PAYLOAD_LEN, "GSM-DATA (TCH)");
176
Harald Welteda7ab742009-12-19 22:23:05 +0100177 if (!new_msg)
178 return -ENOMEM;
Holger Hans Peter Freyther652cdb42014-07-24 21:09:31 +0200179 frame = (struct gsm_data_frame *) msgb_put(new_msg, sizeof(struct gsm_data_frame));
Harald Welteda7ab742009-12-19 22:23:05 +0100180 frame->msg_type = msg_type;
181 frame->callref = callref;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100182 if (rtph->payload_type == RTP_PT_AMR) {
Harald Weltea87f8f92014-05-17 13:43:01 +0200183 /* for FR/HR/EFR the length is implicit. In AMR, we
184 * need to make it explicit by using the first byte of
185 * the data[] buffer as length byte */
186 uint8_t *data0 = msgb_put(new_msg, 1);
187 *data0 = payload_len;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100188 }
Harald Weltea87f8f92014-05-17 13:43:01 +0200189 payload_out = msgb_put(new_msg, payload_len);
190 memcpy(payload_out, payload, payload_len);
Harald Welteda7ab742009-12-19 22:23:05 +0100191
192 *data = new_msg;
193 return 0;
194}
195
Harald Welte647db842013-02-03 12:06:58 +0100196/*! \brief encode and send a rtp frame
197 * \param[in] rs RTP socket through which we shall send
198 * \param[in] frame GSM RTP frame to be sent
199 */
Harald Welteda7ab742009-12-19 22:23:05 +0100200int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
201{
202 struct rtp_sub_socket *rss = &rs->rtp;
203 struct msgb *msg;
204 struct rtp_hdr *rtph;
Harald Weltea87f8f92014-05-17 13:43:01 +0200205 uint8_t *payload;
Harald Welteda7ab742009-12-19 22:23:05 +0100206 int payload_type;
207 int payload_len;
208 int duration; /* in samples */
Andreas Eversberg9967a572012-01-20 20:33:37 +0100209 int is_bfi = 0;
Harald Welteda7ab742009-12-19 22:23:05 +0100210
211 if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
212 /* initialize sequences */
213 rs->tx_action = RTP_SEND_DOWNSTREAM;
214 rs->transmit.ssrc = rand();
215 rs->transmit.sequence = random();
216 rs->transmit.timestamp = random();
217 }
218
219 switch (frame->msg_type) {
220 case GSM_TCHF_FRAME:
221 payload_type = RTP_PT_GSM_FULL;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100222 payload_len = RTP_LEN_GSM_FULL;
223 duration = RTP_GSM_DURATION;
Harald Welteda7ab742009-12-19 22:23:05 +0100224 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100225 case GSM_TCHF_FRAME_EFR:
226 payload_type = RTP_PT_GSM_EFR;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100227 payload_len = RTP_LEN_GSM_EFR;
228 duration = RTP_GSM_DURATION;
Harald Welteaca8f152009-12-19 23:06:41 +0100229 break;
Andreas Eversberg63bfdd82014-01-17 19:06:38 +0100230 case GSM_TCHH_FRAME:
231 payload_type = RTP_PT_GSM_HALF;
232 payload_len = RTP_LEN_GSM_HALF;
233 duration = RTP_GSM_DURATION;
234 break;
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100235 case GSM_TCH_FRAME_AMR:
236 payload_type = RTP_PT_AMR;
237 payload_len = frame->data[0];
238 duration = RTP_GSM_DURATION;
239 break;
Andreas Eversberg9967a572012-01-20 20:33:37 +0100240 case GSM_BAD_FRAME:
241 payload_type = 0;
242 payload_len = 0;
243 duration = RTP_GSM_DURATION;
244 is_bfi = 1;
245 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100246 default:
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200247 DEBUGPC(DLMUX, "unsupported message type %d\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100248 frame->msg_type);
249 return -EINVAL;
250 }
251
Harald Weltea87f8f92014-05-17 13:43:01 +0200252 if (payload_len > MAX_RTP_PAYLOAD_LEN) {
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100253 DEBUGPC(DLMUX, "RTP payload too large (%d octets)\n",
254 payload_len);
255 return -EINVAL;
256 }
257
Andreas Eversberg9967a572012-01-20 20:33:37 +0100258 if (is_bfi) {
259 /* In case of a bad frame, just count and drop packet. */
260 rs->transmit.timestamp += duration;
261 rs->transmit.sequence++;
262 return 0;
263 }
264
Harald Weltea87f8f92014-05-17 13:43:01 +0200265 msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM");
Harald Welteda7ab742009-12-19 22:23:05 +0100266 if (!msg)
267 return -ENOMEM;
Harald Weltea87f8f92014-05-17 13:43:01 +0200268 rtph = (struct rtp_hdr *) msgb_put(msg, sizeof(struct rtp_hdr));
Harald Welteda7ab742009-12-19 22:23:05 +0100269 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);
Harald Weltea87f8f92014-05-17 13:43:01 +0200279
280 payload = msgb_put(msg, payload_len);
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100281 if (frame->msg_type == GSM_TCH_FRAME_AMR)
Harald Weltea87f8f92014-05-17 13:43:01 +0200282 memcpy(payload, frame->data + 1, payload_len);
Andreas Eversbergd8967f72012-03-08 14:39:19 +0100283 else
Harald Weltea87f8f92014-05-17 13:43:01 +0200284 memcpy(payload, frame->data, payload_len);
Harald Welteda7ab742009-12-19 22:23:05 +0100285 msgb_enqueue(&rss->tx_queue, msg);
286 rss->bfd.when |= BSC_FD_WRITE;
287
288 return 0;
289}
290
Holger Hans Peter Freyther89acf062009-07-29 06:46:26 +0200291/* iterate over all chunks in one RTCP message, look for CNAME IEs and
Harald Welte805f6442009-07-28 18:25:29 +0200292 * replace all of those with 'new_cname' */
293static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200294 uint16_t *rtcp_len, const char *new_cname)
Harald Welte805f6442009-07-28 18:25:29 +0200295{
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200296 uint8_t *rtcp_end;
297 uint8_t *cur = (uint8_t *) rh;
298 uint8_t tag, len = 0;
Harald Welte805f6442009-07-28 18:25:29 +0200299
300 rtcp_end = cur + *rtcp_len;
301 /* move cur to end of RTP header */
Harald Welte198f3f52009-07-29 10:46:41 +0200302 cur += sizeof(*rh);
Harald Welte805f6442009-07-28 18:25:29 +0200303
304 /* iterate over Chunks */
305 while (cur+4 < rtcp_end) {
306 /* skip four bytes SSRC/CSRC */
307 cur += 4;
308
309 /* iterate over IE's inside the chunk */
310 while (cur+1 < rtcp_end) {
311 tag = *cur++;
312 if (tag == 0) {
313 /* end of chunk, skip additional zero */
Daniel Willmann45fcb852014-05-21 15:46:43 +0200314 while ((*cur++ == 0) && (cur < rtcp_end)) { }
Harald Welte805f6442009-07-28 18:25:29 +0200315 break;
316 }
317 len = *cur++;
318
319 if (tag == RTCP_IE_CNAME) {
320 /* we've found the CNAME, lets mangle it */
321 if (len < strlen(new_cname)) {
322 /* we need to make more space */
323 int increase = strlen(new_cname) - len;
324
325 msgb_push(msg, increase);
326 memmove(cur+len+increase, cur+len,
327 rtcp_end - (cur+len));
328 /* FIXME: we have to respect RTCP
329 * padding/alignment rules! */
330 len += increase;
331 *(cur-1) += increase;
332 rtcp_end += increase;
333 *rtcp_len += increase;
334 }
335 /* copy new CNAME into message */
336 memcpy(cur, new_cname, strlen(new_cname));
337 /* FIXME: zero the padding in case new CNAME
338 * is smaller than old one !!! */
339 }
340 cur += len;
341 }
342 }
343
344 return 0;
345}
346
347static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
348{
349 struct rtp_sub_socket *rss = &rs->rtcp;
350 struct rtcp_hdr *rtph;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200351 uint16_t old_len;
Harald Welte805f6442009-07-28 18:25:29 +0200352 int rc;
353
354 if (!mangle_rtcp_cname)
355 return 0;
356
Harald Welteda7ab742009-12-19 22:23:05 +0100357 printf("RTCP\n");
Harald Welte805f6442009-07-28 18:25:29 +0200358 /* iterate over list of RTCP messages */
359 rtph = (struct rtcp_hdr *)msg->data;
Harald Welteda7ab742009-12-19 22:23:05 +0100360 while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
Harald Welte805f6442009-07-28 18:25:29 +0200361 old_len = (ntohs(rtph->length) + 1) * 4;
Harald Welteda7ab742009-12-19 22:23:05 +0100362 if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200363 DEBUGPC(DLMUX, "received RTCP packet too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100364 "length element\n");
365 return -EINVAL;
366 }
Harald Welte805f6442009-07-28 18:25:29 +0200367 if (rtph->type == RTCP_TYPE_SDES) {
368 char new_cname[255];
369 strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
370 sizeof(new_cname));
371 new_cname[sizeof(new_cname)-1] = '\0';
372 rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
373 new_cname);
374 if (rc < 0)
375 return rc;
376 }
377 rtph = (void *)rtph + old_len;
378 }
379
380 return 0;
381}
382
Harald Welteead7a7b2009-07-28 00:01:58 +0200383/* read from incoming RTP/RTCP socket */
384static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
385{
386 int rc;
387 struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
Harald Welteda7ab742009-12-19 22:23:05 +0100388 struct msgb *new_msg;
Harald Welteead7a7b2009-07-28 00:01:58 +0200389 struct rtp_sub_socket *other_rss;
390
391 if (!msg)
392 return -ENOMEM;
393
394 rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
Andreas Eversbergcf7557a2012-03-16 08:14:23 +0100395 if (rc == 0) {
396 rss->bfd.when &= ~BSC_FD_READ;
397 goto out_free;
398 } else if (rc < 0) {
399 /* Ignore "connection refused". this happens, If we open the
400 * socket faster than the remote side. */
401 if (errno == ECONNREFUSED)
402 goto out_free;
403 DEBUGPC(DLMUX, "Read of RTP socket (%p) failed (errno %d, "
404 "%s)\n", rs, errno, strerror(errno));
Harald Welteead7a7b2009-07-28 00:01:58 +0200405 rss->bfd.when &= ~BSC_FD_READ;
Holger Hans Peter Freyther37b5ce52015-03-28 17:30:31 +0100406 goto out_free;
Harald Welteead7a7b2009-07-28 00:01:58 +0200407 }
408
409 msgb_put(msg, rc);
410
411 switch (rs->rx_action) {
412 case RTP_PROXY:
Harald Welte805f6442009-07-28 18:25:29 +0200413 if (!rs->proxy.other_sock) {
414 rc = -EIO;
415 goto out_free;
416 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200417 if (rss->bfd.priv_nr == RTP_PRIV_RTP)
418 other_rss = &rs->proxy.other_sock->rtp;
Harald Welte805f6442009-07-28 18:25:29 +0200419 else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200420 other_rss = &rs->proxy.other_sock->rtcp;
Harald Welte805f6442009-07-28 18:25:29 +0200421 /* modify RTCP SDES CNAME */
422 rc = rtcp_mangle(msg, rs);
423 if (rc < 0)
424 goto out_free;
425 } else {
426 rc = -EINVAL;
427 goto out_free;
Harald Welteead7a7b2009-07-28 00:01:58 +0200428 }
429 msgb_enqueue(&other_rss->tx_queue, msg);
430 other_rss->bfd.when |= BSC_FD_WRITE;
431 break;
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200432
433 case RTP_RECV_UPSTREAM:
Harald Welteda7ab742009-12-19 22:23:05 +0100434 if (!rs->receive.callref || !rs->receive.net) {
435 rc = -EIO;
436 goto out_free;
437 }
438 if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
439 if (!mangle_rtcp_cname) {
440 msgb_free(msg);
441 break;
442 }
443 /* modify RTCP SDES CNAME */
444 rc = rtcp_mangle(msg, rs);
445 if (rc < 0)
446 goto out_free;
447 msgb_enqueue(&rss->tx_queue, msg);
448 rss->bfd.when |= BSC_FD_WRITE;
449 break;
450 }
451 if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
452 rc = -EINVAL;
453 goto out_free;
454 }
455 rc = rtp_decode(msg, rs->receive.callref, &new_msg);
456 if (rc < 0)
457 goto out_free;
458 msgb_free(msg);
Harald Welte31c00f72011-03-03 23:29:05 +0100459 trau_tx_to_mncc(rs->receive.net, new_msg);
Harald Welteda7ab742009-12-19 22:23:05 +0100460 break;
461
462 case RTP_NONE: /* if socket exists, but disabled by app */
463 msgb_free(msg);
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200464 break;
Harald Welteead7a7b2009-07-28 00:01:58 +0200465 }
466
Harald Welteda7ab742009-12-19 22:23:05 +0100467 return 0;
Harald Welte805f6442009-07-28 18:25:29 +0200468
469out_free:
470 msgb_free(msg);
471 return rc;
Harald Welteead7a7b2009-07-28 00:01:58 +0200472}
473
Harald Welte647db842013-02-03 12:06:58 +0100474/* \brief write from tx_queue to RTP/RTCP socket */
Harald Welteead7a7b2009-07-28 00:01:58 +0200475static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
476{
477 struct msgb *msg;
478 int written;
479
480 msg = msgb_dequeue(&rss->tx_queue);
481 if (!msg) {
482 rss->bfd.when &= ~BSC_FD_WRITE;
483 return 0;
484 }
485
486 written = write(rss->bfd.fd, msg->data, msg->len);
487 if (written < msg->len) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200488 LOGP(DLMIB, LOGL_ERROR, "short write");
Harald Welteead7a7b2009-07-28 00:01:58 +0200489 msgb_free(msg);
490 return -EIO;
491 }
492
493 msgb_free(msg);
494
495 return 0;
496}
497
498
Harald Welte647db842013-02-03 12:06:58 +0100499/*! \brief callback for the select.c:bfd_* layer */
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200500static int rtp_bfd_cb(struct osmo_fd *bfd, unsigned int flags)
Harald Welteead7a7b2009-07-28 00:01:58 +0200501{
502 struct rtp_socket *rs = bfd->data;
503 struct rtp_sub_socket *rss;
504
505 switch (bfd->priv_nr) {
506 case RTP_PRIV_RTP:
507 rss = &rs->rtp;
508 break;
509 case RTP_PRIV_RTCP:
510 rss = &rs->rtcp;
511 break;
512 default:
513 return -EINVAL;
514 }
515
516 if (flags & BSC_FD_READ)
517 rtp_socket_read(rs, rss);
518
519 if (flags & BSC_FD_WRITE)
520 rtp_socket_write(rs, rss);
521
522 return 0;
523}
524
Harald Welte647db842013-02-03 12:06:58 +0100525/*! \brief initialize one rtp sub-socket */
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200526static void init_rss(struct rtp_sub_socket *rss,
Harald Welteead7a7b2009-07-28 00:01:58 +0200527 struct rtp_socket *rs, int fd, int priv_nr)
528{
529 /* initialize bfd */
530 rss->bfd.fd = fd;
531 rss->bfd.data = rs;
532 rss->bfd.priv_nr = priv_nr;
533 rss->bfd.cb = rtp_bfd_cb;
534}
535
Harald Welte647db842013-02-03 12:06:58 +0100536/*! \brief create a new RTP/RTCP socket and bind it */
Harald Welteead7a7b2009-07-28 00:01:58 +0200537struct rtp_socket *rtp_socket_create(void)
538{
539 int rc;
540 struct rtp_socket *rs;
541
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200542 DEBUGP(DLMUX, "rtp_socket_create(): ");
Harald Welte805f6442009-07-28 18:25:29 +0200543
Harald Welteead7a7b2009-07-28 00:01:58 +0200544 rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
545 if (!rs)
546 return NULL;
547
548 INIT_LLIST_HEAD(&rs->rtp.tx_queue);
549 INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
550
551 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
552 if (rc < 0)
553 goto out_free;
554
555 init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200556 rc = osmo_fd_register(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200557 if (rc < 0)
558 goto out_rtp_socket;
559
560 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
561 if (rc < 0)
562 goto out_rtp_bfd;
563
564 init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200565 rc = osmo_fd_register(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200566 if (rc < 0)
567 goto out_rtcp_socket;
568
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200569 DEBUGPC(DLMUX, "success\n");
Harald Welte805f6442009-07-28 18:25:29 +0200570
571 rc = rtp_socket_bind(rs, INADDR_ANY);
572 if (rc < 0)
573 goto out_rtcp_bfd;
574
Harald Welteead7a7b2009-07-28 00:01:58 +0200575 return rs;
576
Harald Welte805f6442009-07-28 18:25:29 +0200577out_rtcp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200578 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200579out_rtcp_socket:
580 close(rs->rtcp.bfd.fd);
581out_rtp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200582 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200583out_rtp_socket:
584 close(rs->rtp.bfd.fd);
585out_free:
586 talloc_free(rs);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200587 DEBUGPC(DLMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200588 return NULL;
589}
590
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200591static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, uint32_t ip,
592 uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200593{
Harald Welte805f6442009-07-28 18:25:29 +0200594 int rc;
595 socklen_t alen = sizeof(rss->sin_local);
596
Harald Welteead7a7b2009-07-28 00:01:58 +0200597 rss->sin_local.sin_family = AF_INET;
598 rss->sin_local.sin_addr.s_addr = htonl(ip);
599 rss->sin_local.sin_port = htons(port);
600 rss->bfd.when |= BSC_FD_READ;
601
Harald Welte805f6442009-07-28 18:25:29 +0200602 rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
603 sizeof(rss->sin_local));
604 if (rc < 0)
605 return rc;
606
607 /* retrieve the address we actually bound to, in case we
608 * passed INADDR_ANY as IP address */
609 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
610 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200611}
612
613#define RTP_PORT_BASE 30000
Harald Welte805f6442009-07-28 18:25:29 +0200614static unsigned int next_udp_port = RTP_PORT_BASE;
Harald Welteead7a7b2009-07-28 00:01:58 +0200615
Harald Welte647db842013-02-03 12:06:58 +0100616/*! \brief bind a RTP socket to a specific local address
617 * \param[in] rs RTP socket to be bound
618 * \param[in] ip local IP address to which socket is to be bound
619 */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200620int rtp_socket_bind(struct rtp_socket *rs, uint32_t ip)
Harald Welteead7a7b2009-07-28 00:01:58 +0200621{
Harald Welte805f6442009-07-28 18:25:29 +0200622 int rc = -EIO;
623 struct in_addr ia;
624
625 ia.s_addr = htonl(ip);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200626 DEBUGP(DLMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
Harald Welte805f6442009-07-28 18:25:29 +0200627 inet_ntoa(ia));
Harald Welteead7a7b2009-07-28 00:01:58 +0200628
629 /* try to bind to a consecutive pair of ports */
Harald Welte805f6442009-07-28 18:25:29 +0200630 for (next_udp_port = next_udp_port % 0xffff;
631 next_udp_port < 0xffff; next_udp_port += 2) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200632 rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
633 if (rc != 0)
634 continue;
635
636 rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
637 if (rc == 0)
638 break;
639 }
Harald Welte805f6442009-07-28 18:25:29 +0200640 if (rc < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200641 DEBUGPC(DLMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200642 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200643 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200644
Harald Welte805f6442009-07-28 18:25:29 +0200645 ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200646 DEBUGPC(DLMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
Harald Welte805f6442009-07-28 18:25:29 +0200647 inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
Harald Welteead7a7b2009-07-28 00:01:58 +0200648 return ntohs(rs->rtp.sin_local.sin_port);
649}
650
651static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200652 uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200653{
Harald Welte805f6442009-07-28 18:25:29 +0200654 int rc;
655 socklen_t alen = sizeof(rss->sin_local);
656
Harald Welteead7a7b2009-07-28 00:01:58 +0200657 rss->sin_remote.sin_family = AF_INET;
658 rss->sin_remote.sin_addr.s_addr = htonl(ip);
659 rss->sin_remote.sin_port = htons(port);
660
Harald Welte805f6442009-07-28 18:25:29 +0200661 rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
662 sizeof(rss->sin_remote));
663 if (rc < 0)
664 return rc;
665
666 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
667 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200668}
669
Harald Welte647db842013-02-03 12:06:58 +0100670/*! \brief 'connect' a RTP socket to a remote peer
671 * \param[in] rs RTP socket to be connected
672 * \param[in] ip remote IP address to which to connect
673 * \param[in] port remote UDP port number to which to connect
674 */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200675int rtp_socket_connect(struct rtp_socket *rs, uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200676{
677 int rc;
Harald Welte805f6442009-07-28 18:25:29 +0200678 struct in_addr ia;
679
680 ia.s_addr = htonl(ip);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200681 DEBUGP(DLMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
Harald Welte805f6442009-07-28 18:25:29 +0200682 rs, inet_ntoa(ia), port);
Harald Welteead7a7b2009-07-28 00:01:58 +0200683
684 rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
685 if (rc < 0)
686 return rc;
687
688 return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
689}
690
Harald Welte647db842013-02-03 12:06:58 +0100691/*! \brief bind two RTP/RTCP sockets together in the proxy
692 * \param[in] this First RTP socket
693 * \param[in] other Second RTP socket
694 */
Harald Welteead7a7b2009-07-28 00:01:58 +0200695int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
696{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200697 DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
Harald Welte805f6442009-07-28 18:25:29 +0200698 this, other);
699
Harald Welteead7a7b2009-07-28 00:01:58 +0200700 this->rx_action = RTP_PROXY;
701 this->proxy.other_sock = other;
702
703 other->rx_action = RTP_PROXY;
704 other->proxy.other_sock = this;
705
706 return 0;
707}
708
Harald Welte647db842013-02-03 12:06:58 +0100709/*! \brief bind RTP/RTCP socket to application, disabling proxy
710 * \param[in] this RTP socket
711 * \param[in] net gsm_network argument to trau_tx_to_mncc()
712 * \param[in] callref callref argument to trau_tx_to_mncc()
713 */
Harald Welte9fb1f102009-12-20 17:07:23 +0100714int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200715 uint32_t callref)
Harald Welteda7ab742009-12-19 22:23:05 +0100716{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200717 DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100718 this, callref);
719
720 if (callref) {
721 this->rx_action = RTP_RECV_UPSTREAM;
722 this->receive.net = net;
723 this->receive.callref = callref;
724 } else
725 this->rx_action = RTP_NONE;
726
727 return 0;
728}
729
Harald Welteead7a7b2009-07-28 00:01:58 +0200730static void free_tx_queue(struct rtp_sub_socket *rss)
731{
732 struct msgb *msg;
733
734 while ((msg = msgb_dequeue(&rss->tx_queue)))
735 msgb_free(msg);
736}
737
Harald Welte647db842013-02-03 12:06:58 +0100738/*! \brief Free/release a previously allocated RTP socket
739 * \param[in[] rs RTP/RTCP socket to be released
740 */
Harald Welteead7a7b2009-07-28 00:01:58 +0200741int rtp_socket_free(struct rtp_socket *rs)
742{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200743 DEBUGP(DLMUX, "rtp_socket_free(rs=%p)\n", rs);
Harald Welteead7a7b2009-07-28 00:01:58 +0200744
745 /* make sure we don't leave references dangling to us */
746 if (rs->rx_action == RTP_PROXY &&
747 rs->proxy.other_sock)
748 rs->proxy.other_sock->proxy.other_sock = NULL;
749
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200750 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200751 close(rs->rtp.bfd.fd);
752 free_tx_queue(&rs->rtp);
753
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200754 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200755 close(rs->rtcp.bfd.fd);
756 free_tx_queue(&rs->rtcp);
757
758 talloc_free(rs);
759
760 return 0;
761}