blob: 143bfa0065071ade5833ee2d73e7ebe02e20da0a [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>
Harald Welteead7a7b2009-07-28 00:01:58 +020039
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020040/* attempt to determine byte order */
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020041#include <sys/param.h>
42#include <limits.h>
43
44#ifndef __BYTE_ORDER
Tobias Engelaff20712012-10-24 17:53:50 +020045# ifdef __APPLE__
46# define __BYTE_ORDER __DARWIN_BYTE_ORDER
47# define __LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN
48# define __BIG_ENDIAN __DARWIN_BIG_ENDIAN
49# else
50# error "__BYTE_ORDER should be defined by someone"
51# endif
Holger Hans Peter Freyther3cb28792010-10-12 15:39:46 +020052#endif
53
Harald Welteead7a7b2009-07-28 00:01:58 +020054static LLIST_HEAD(rtp_sockets);
55
Harald Welte805f6442009-07-28 18:25:29 +020056/* should we mangle the CNAME inside SDES of RTCP packets? We disable
57 * this by default, as it seems to be not needed */
58static int mangle_rtcp_cname = 0;
59
Harald Welteead7a7b2009-07-28 00:01:58 +020060enum rtp_bfd_priv {
61 RTP_PRIV_NONE,
62 RTP_PRIV_RTP,
63 RTP_PRIV_RTCP
64};
65
66#define RTP_ALLOC_SIZE 1500
67
Harald Welte805f6442009-07-28 18:25:29 +020068/* according to RFC 1889 */
69struct rtcp_hdr {
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020070 uint8_t byte0;
71 uint8_t type;
72 uint16_t length;
Harald Welte805f6442009-07-28 18:25:29 +020073} __attribute__((packed));
74
75#define RTCP_TYPE_SDES 202
76
77#define RTCP_IE_CNAME 1
78
Harald Welteda7ab742009-12-19 22:23:05 +010079/* according to RFC 3550 */
80struct rtp_hdr {
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010081#if __BYTE_ORDER == __LITTLE_ENDIAN
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020082 uint8_t csrc_count:4,
Harald Welteda7ab742009-12-19 22:23:05 +010083 extension:1,
84 padding:1,
85 version:2;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020086 uint8_t payload_type:7,
Harald Welteda7ab742009-12-19 22:23:05 +010087 marker:1;
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010088#elif __BYTE_ORDER == __BIG_ENDIAN
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020089 uint8_t version:2,
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010090 padding:1,
91 extension:1,
92 csrc_count:4;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020093 uint8_t marker:1,
Holger Hans Peter Freyther2890d102010-02-26 13:12:41 +010094 payload_type:7;
95#endif
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +020096 uint16_t sequence;
97 uint32_t timestamp;
98 uint32_t ssrc;
Harald Welteda7ab742009-12-19 22:23:05 +010099} __attribute__((packed));
100
101struct rtp_x_hdr {
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200102 uint16_t by_profile;
103 uint16_t length;
Harald Welteda7ab742009-12-19 22:23:05 +0100104} __attribute__((packed));
105
106#define RTP_VERSION 2
107
Harald Welteda7ab742009-12-19 22:23:05 +0100108/* decode an rtp frame and create a new buffer with payload */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200109static int rtp_decode(struct msgb *msg, uint32_t callref, struct msgb **data)
Harald Welteda7ab742009-12-19 22:23:05 +0100110{
111 struct msgb *new_msg;
112 struct gsm_data_frame *frame;
113 struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data;
114 struct rtp_x_hdr *rtpxh;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200115 uint8_t *payload;
Harald Welteda7ab742009-12-19 22:23:05 +0100116 int payload_len;
117 int msg_type;
118 int x_len;
119
120 if (msg->len < 12) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200121 DEBUGPC(DLMUX, "received RTP frame too short (len = %d)\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100122 msg->len);
123 return -EINVAL;
124 }
125 if (rtph->version != RTP_VERSION) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200126 DEBUGPC(DLMUX, "received RTP version %d not supported.\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100127 rtph->version);
128 return -EINVAL;
129 }
130 payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2);
131 payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2);
132 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200133 DEBUGPC(DLMUX, "received RTP frame too short (len = %d, "
Harald Welteda7ab742009-12-19 22:23:05 +0100134 "csrc count = %d)\n", msg->len, rtph->csrc_count);
135 return -EINVAL;
136 }
137 if (rtph->extension) {
138 if (payload_len < sizeof(struct rtp_x_hdr)) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200139 DEBUGPC(DLMUX, "received RTP frame too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100140 "extension header\n");
141 return -EINVAL;
142 }
143 rtpxh = (struct rtp_x_hdr *)payload;
144 x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr);
145 payload += x_len;
146 payload_len -= x_len;
147 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200148 DEBUGPC(DLMUX, "received RTP frame too short, "
Harald Welteda7ab742009-12-19 22:23:05 +0100149 "extension header exceeds frame length\n");
150 return -EINVAL;
151 }
152 }
153 if (rtph->padding) {
Jacob Erlbeckd9e40392013-11-21 19:05:42 +0100154 if (payload_len < 1) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200155 DEBUGPC(DLMUX, "received RTP frame too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100156 "padding length\n");
157 return -EINVAL;
158 }
159 payload_len -= payload[payload_len - 1];
160 if (payload_len < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200161 DEBUGPC(DLMUX, "received RTP frame with padding "
Harald Welteda7ab742009-12-19 22:23:05 +0100162 "greater than payload\n");
163 return -EINVAL;
164 }
165 }
166
167 switch (rtph->payload_type) {
168 case RTP_PT_GSM_FULL:
169 msg_type = GSM_TCHF_FRAME;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100170 if (payload_len != RTP_LEN_GSM_FULL) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200171 DEBUGPC(DLMUX, "received RTP full rate frame with "
Andreas Eversberg88012b62014-01-22 10:05:51 +0100172 "payload length != %d (len = %d)\n",
173 RTP_LEN_GSM_FULL, payload_len);
Harald Welteda7ab742009-12-19 22:23:05 +0100174 return -EINVAL;
175 }
176 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100177 case RTP_PT_GSM_EFR:
178 msg_type = GSM_TCHF_FRAME_EFR;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100179 if (payload_len != RTP_LEN_GSM_EFR) {
180 DEBUGPC(DLMUX, "received RTP extended full rate frame "
181 "with payload length != %d (len = %d)\n",
182 RTP_LEN_GSM_EFR, payload_len);
183 return -EINVAL;
184 }
Harald Welteaca8f152009-12-19 23:06:41 +0100185 break;
Andreas Eversberg63bfdd82014-01-17 19:06:38 +0100186 case RTP_PT_GSM_HALF:
187 msg_type = GSM_TCHH_FRAME;
188 if (payload_len != RTP_LEN_GSM_HALF) {
189 DEBUGPC(DLMUX, "received RTP half rate frame with "
190 "payload length != %d (len = %d)\n",
191 RTP_LEN_GSM_HALF, payload_len);
192 return -EINVAL;
193 }
194 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100195 default:
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200196 DEBUGPC(DLMUX, "received RTP frame with unknown payload "
Harald Welteda7ab742009-12-19 22:23:05 +0100197 "type %d\n", rtph->payload_type);
198 return -EINVAL;
199 }
200
201 new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len,
202 "GSM-DATA");
203 if (!new_msg)
204 return -ENOMEM;
205 frame = (struct gsm_data_frame *)(new_msg->data);
206 frame->msg_type = msg_type;
207 frame->callref = callref;
208 memcpy(frame->data, payload, payload_len);
209 msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len);
210
211 *data = new_msg;
212 return 0;
213}
214
Harald Welte392736d2009-12-20 13:16:14 +0100215/* "to - from" */
216static void tv_difference(struct timeval *diff, const struct timeval *from,
217 const struct timeval *__to)
218{
219 struct timeval _to = *__to, *to = &_to;
220
221 if (to->tv_usec < from->tv_usec) {
222 to->tv_sec -= 1;
223 to->tv_usec += 1000000;
224 }
225
226 diff->tv_usec = to->tv_usec - from->tv_usec;
227 diff->tv_sec = to->tv_sec - from->tv_sec;
228}
229
Harald Welte647db842013-02-03 12:06:58 +0100230/*! \brief encode and send a rtp frame
231 * \param[in] rs RTP socket through which we shall send
232 * \param[in] frame GSM RTP frame to be sent
233 */
Harald Welteda7ab742009-12-19 22:23:05 +0100234int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame)
235{
236 struct rtp_sub_socket *rss = &rs->rtp;
237 struct msgb *msg;
238 struct rtp_hdr *rtph;
239 int payload_type;
240 int payload_len;
241 int duration; /* in samples */
242
243 if (rs->tx_action != RTP_SEND_DOWNSTREAM) {
244 /* initialize sequences */
245 rs->tx_action = RTP_SEND_DOWNSTREAM;
246 rs->transmit.ssrc = rand();
247 rs->transmit.sequence = random();
248 rs->transmit.timestamp = random();
249 }
250
251 switch (frame->msg_type) {
252 case GSM_TCHF_FRAME:
253 payload_type = RTP_PT_GSM_FULL;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100254 payload_len = RTP_LEN_GSM_FULL;
255 duration = RTP_GSM_DURATION;
Harald Welteda7ab742009-12-19 22:23:05 +0100256 break;
Harald Welteaca8f152009-12-19 23:06:41 +0100257 case GSM_TCHF_FRAME_EFR:
258 payload_type = RTP_PT_GSM_EFR;
Andreas Eversberg88012b62014-01-22 10:05:51 +0100259 payload_len = RTP_LEN_GSM_EFR;
260 duration = RTP_GSM_DURATION;
Harald Welteaca8f152009-12-19 23:06:41 +0100261 break;
Andreas Eversberg63bfdd82014-01-17 19:06:38 +0100262 case GSM_TCHH_FRAME:
263 payload_type = RTP_PT_GSM_HALF;
264 payload_len = RTP_LEN_GSM_HALF;
265 duration = RTP_GSM_DURATION;
266 break;
Harald Welteda7ab742009-12-19 22:23:05 +0100267 default:
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200268 DEBUGPC(DLMUX, "unsupported message type %d\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100269 frame->msg_type);
270 return -EINVAL;
271 }
272
Harald Welte392736d2009-12-20 13:16:14 +0100273 {
274 struct timeval tv, tv_diff;
275 long int usec_diff, frame_diff;
276
277 gettimeofday(&tv, NULL);
278 tv_difference(&tv_diff, &rs->transmit.last_tv, &tv);
279 rs->transmit.last_tv = tv;
280
281 usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec;
282 frame_diff = (usec_diff / 20000);
283
284 if (abs(frame_diff) > 1) {
285 long int frame_diff_excess = frame_diff - 1;
286
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200287 LOGP(DLMUX, LOGL_NOTICE,
Harald Welte (local)9fcf6d72009-12-27 17:01:40 +0100288 "Correcting frame difference of %ld frames\n", frame_diff_excess);
Harald Welte392736d2009-12-20 13:16:14 +0100289 rs->transmit.sequence += frame_diff_excess;
290 rs->transmit.timestamp += frame_diff_excess * duration;
291 }
292 }
293
Harald Welteda7ab742009-12-19 22:23:05 +0100294 msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL");
295 if (!msg)
296 return -ENOMEM;
297 rtph = (struct rtp_hdr *)msg->data;
298 rtph->version = RTP_VERSION;
299 rtph->padding = 0;
300 rtph->extension = 0;
301 rtph->csrc_count = 0;
302 rtph->marker = 0;
303 rtph->payload_type = payload_type;
304 rtph->sequence = htons(rs->transmit.sequence++);
305 rtph->timestamp = htonl(rs->transmit.timestamp);
306 rs->transmit.timestamp += duration;
307 rtph->ssrc = htonl(rs->transmit.ssrc);
308 memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len);
309 msgb_put(msg, sizeof(struct rtp_hdr) + payload_len);
310 msgb_enqueue(&rss->tx_queue, msg);
311 rss->bfd.when |= BSC_FD_WRITE;
312
313 return 0;
314}
315
Holger Hans Peter Freyther89acf062009-07-29 06:46:26 +0200316/* iterate over all chunks in one RTCP message, look for CNAME IEs and
Harald Welte805f6442009-07-28 18:25:29 +0200317 * replace all of those with 'new_cname' */
318static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200319 uint16_t *rtcp_len, const char *new_cname)
Harald Welte805f6442009-07-28 18:25:29 +0200320{
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200321 uint8_t *rtcp_end;
322 uint8_t *cur = (uint8_t *) rh;
323 uint8_t tag, len = 0;
Harald Welte805f6442009-07-28 18:25:29 +0200324
325 rtcp_end = cur + *rtcp_len;
326 /* move cur to end of RTP header */
Harald Welte198f3f52009-07-29 10:46:41 +0200327 cur += sizeof(*rh);
Harald Welte805f6442009-07-28 18:25:29 +0200328
329 /* iterate over Chunks */
330 while (cur+4 < rtcp_end) {
331 /* skip four bytes SSRC/CSRC */
332 cur += 4;
333
334 /* iterate over IE's inside the chunk */
335 while (cur+1 < rtcp_end) {
336 tag = *cur++;
337 if (tag == 0) {
338 /* end of chunk, skip additional zero */
339 while (*cur++ == 0) { }
340 break;
341 }
342 len = *cur++;
343
344 if (tag == RTCP_IE_CNAME) {
345 /* we've found the CNAME, lets mangle it */
346 if (len < strlen(new_cname)) {
347 /* we need to make more space */
348 int increase = strlen(new_cname) - len;
349
350 msgb_push(msg, increase);
351 memmove(cur+len+increase, cur+len,
352 rtcp_end - (cur+len));
353 /* FIXME: we have to respect RTCP
354 * padding/alignment rules! */
355 len += increase;
356 *(cur-1) += increase;
357 rtcp_end += increase;
358 *rtcp_len += increase;
359 }
360 /* copy new CNAME into message */
361 memcpy(cur, new_cname, strlen(new_cname));
362 /* FIXME: zero the padding in case new CNAME
363 * is smaller than old one !!! */
364 }
365 cur += len;
366 }
367 }
368
369 return 0;
370}
371
372static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
373{
374 struct rtp_sub_socket *rss = &rs->rtcp;
375 struct rtcp_hdr *rtph;
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200376 uint16_t old_len;
Harald Welte805f6442009-07-28 18:25:29 +0200377 int rc;
378
379 if (!mangle_rtcp_cname)
380 return 0;
381
Harald Welteda7ab742009-12-19 22:23:05 +0100382 printf("RTCP\n");
Harald Welte805f6442009-07-28 18:25:29 +0200383 /* iterate over list of RTCP messages */
384 rtph = (struct rtcp_hdr *)msg->data;
Harald Welteda7ab742009-12-19 22:23:05 +0100385 while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) {
Harald Welte805f6442009-07-28 18:25:29 +0200386 old_len = (ntohs(rtph->length) + 1) * 4;
Harald Welteda7ab742009-12-19 22:23:05 +0100387 if ((void *)rtph + old_len > (void *)msg->data + msg->len) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200388 DEBUGPC(DLMUX, "received RTCP packet too short for "
Harald Welteda7ab742009-12-19 22:23:05 +0100389 "length element\n");
390 return -EINVAL;
391 }
Harald Welte805f6442009-07-28 18:25:29 +0200392 if (rtph->type == RTCP_TYPE_SDES) {
393 char new_cname[255];
394 strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
395 sizeof(new_cname));
396 new_cname[sizeof(new_cname)-1] = '\0';
397 rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
398 new_cname);
399 if (rc < 0)
400 return rc;
401 }
402 rtph = (void *)rtph + old_len;
403 }
404
405 return 0;
406}
407
Harald Welteead7a7b2009-07-28 00:01:58 +0200408/* read from incoming RTP/RTCP socket */
409static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
410{
411 int rc;
412 struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
Harald Welteda7ab742009-12-19 22:23:05 +0100413 struct msgb *new_msg;
Harald Welteead7a7b2009-07-28 00:01:58 +0200414 struct rtp_sub_socket *other_rss;
415
416 if (!msg)
417 return -ENOMEM;
418
419 rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
420 if (rc <= 0) {
421 rss->bfd.when &= ~BSC_FD_READ;
422 return rc;
423 }
424
425 msgb_put(msg, rc);
426
427 switch (rs->rx_action) {
428 case RTP_PROXY:
Harald Welte805f6442009-07-28 18:25:29 +0200429 if (!rs->proxy.other_sock) {
430 rc = -EIO;
431 goto out_free;
432 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200433 if (rss->bfd.priv_nr == RTP_PRIV_RTP)
434 other_rss = &rs->proxy.other_sock->rtp;
Harald Welte805f6442009-07-28 18:25:29 +0200435 else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200436 other_rss = &rs->proxy.other_sock->rtcp;
Harald Welte805f6442009-07-28 18:25:29 +0200437 /* modify RTCP SDES CNAME */
438 rc = rtcp_mangle(msg, rs);
439 if (rc < 0)
440 goto out_free;
441 } else {
442 rc = -EINVAL;
443 goto out_free;
Harald Welteead7a7b2009-07-28 00:01:58 +0200444 }
445 msgb_enqueue(&other_rss->tx_queue, msg);
446 other_rss->bfd.when |= BSC_FD_WRITE;
447 break;
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200448
449 case RTP_RECV_UPSTREAM:
Harald Welteda7ab742009-12-19 22:23:05 +0100450 if (!rs->receive.callref || !rs->receive.net) {
451 rc = -EIO;
452 goto out_free;
453 }
454 if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
455 if (!mangle_rtcp_cname) {
456 msgb_free(msg);
457 break;
458 }
459 /* modify RTCP SDES CNAME */
460 rc = rtcp_mangle(msg, rs);
461 if (rc < 0)
462 goto out_free;
463 msgb_enqueue(&rss->tx_queue, msg);
464 rss->bfd.when |= BSC_FD_WRITE;
465 break;
466 }
467 if (rss->bfd.priv_nr != RTP_PRIV_RTP) {
468 rc = -EINVAL;
469 goto out_free;
470 }
471 rc = rtp_decode(msg, rs->receive.callref, &new_msg);
472 if (rc < 0)
473 goto out_free;
474 msgb_free(msg);
Harald Welte31c00f72011-03-03 23:29:05 +0100475 trau_tx_to_mncc(rs->receive.net, new_msg);
Harald Welteda7ab742009-12-19 22:23:05 +0100476 break;
477
478 case RTP_NONE: /* if socket exists, but disabled by app */
479 msgb_free(msg);
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200480 break;
Harald Welteead7a7b2009-07-28 00:01:58 +0200481 }
482
Harald Welteda7ab742009-12-19 22:23:05 +0100483 return 0;
Harald Welte805f6442009-07-28 18:25:29 +0200484
485out_free:
486 msgb_free(msg);
487 return rc;
Harald Welteead7a7b2009-07-28 00:01:58 +0200488}
489
Harald Welte647db842013-02-03 12:06:58 +0100490/* \brief write from tx_queue to RTP/RTCP socket */
Harald Welteead7a7b2009-07-28 00:01:58 +0200491static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
492{
493 struct msgb *msg;
494 int written;
495
496 msg = msgb_dequeue(&rss->tx_queue);
497 if (!msg) {
498 rss->bfd.when &= ~BSC_FD_WRITE;
499 return 0;
500 }
501
502 written = write(rss->bfd.fd, msg->data, msg->len);
503 if (written < msg->len) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200504 LOGP(DLMIB, LOGL_ERROR, "short write");
Harald Welteead7a7b2009-07-28 00:01:58 +0200505 msgb_free(msg);
506 return -EIO;
507 }
508
509 msgb_free(msg);
510
511 return 0;
512}
513
514
Harald Welte647db842013-02-03 12:06:58 +0100515/*! \brief callback for the select.c:bfd_* layer */
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200516static int rtp_bfd_cb(struct osmo_fd *bfd, unsigned int flags)
Harald Welteead7a7b2009-07-28 00:01:58 +0200517{
518 struct rtp_socket *rs = bfd->data;
519 struct rtp_sub_socket *rss;
520
521 switch (bfd->priv_nr) {
522 case RTP_PRIV_RTP:
523 rss = &rs->rtp;
524 break;
525 case RTP_PRIV_RTCP:
526 rss = &rs->rtcp;
527 break;
528 default:
529 return -EINVAL;
530 }
531
532 if (flags & BSC_FD_READ)
533 rtp_socket_read(rs, rss);
534
535 if (flags & BSC_FD_WRITE)
536 rtp_socket_write(rs, rss);
537
538 return 0;
539}
540
Harald Welte647db842013-02-03 12:06:58 +0100541/*! \brief initialize one rtp sub-socket */
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200542static void init_rss(struct rtp_sub_socket *rss,
Harald Welteead7a7b2009-07-28 00:01:58 +0200543 struct rtp_socket *rs, int fd, int priv_nr)
544{
545 /* initialize bfd */
546 rss->bfd.fd = fd;
547 rss->bfd.data = rs;
548 rss->bfd.priv_nr = priv_nr;
549 rss->bfd.cb = rtp_bfd_cb;
550}
551
Harald Welte647db842013-02-03 12:06:58 +0100552/*! \brief create a new RTP/RTCP socket and bind it */
Harald Welteead7a7b2009-07-28 00:01:58 +0200553struct rtp_socket *rtp_socket_create(void)
554{
555 int rc;
556 struct rtp_socket *rs;
557
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200558 DEBUGP(DLMUX, "rtp_socket_create(): ");
Harald Welte805f6442009-07-28 18:25:29 +0200559
Harald Welteead7a7b2009-07-28 00:01:58 +0200560 rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
561 if (!rs)
562 return NULL;
563
564 INIT_LLIST_HEAD(&rs->rtp.tx_queue);
565 INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
566
567 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
568 if (rc < 0)
569 goto out_free;
570
571 init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200572 rc = osmo_fd_register(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200573 if (rc < 0)
574 goto out_rtp_socket;
575
576 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
577 if (rc < 0)
578 goto out_rtp_bfd;
579
580 init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200581 rc = osmo_fd_register(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200582 if (rc < 0)
583 goto out_rtcp_socket;
584
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200585 DEBUGPC(DLMUX, "success\n");
Harald Welte805f6442009-07-28 18:25:29 +0200586
587 rc = rtp_socket_bind(rs, INADDR_ANY);
588 if (rc < 0)
589 goto out_rtcp_bfd;
590
Harald Welteead7a7b2009-07-28 00:01:58 +0200591 return rs;
592
Harald Welte805f6442009-07-28 18:25:29 +0200593out_rtcp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200594 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200595out_rtcp_socket:
596 close(rs->rtcp.bfd.fd);
597out_rtp_bfd:
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200598 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200599out_rtp_socket:
600 close(rs->rtp.bfd.fd);
601out_free:
602 talloc_free(rs);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200603 DEBUGPC(DLMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200604 return NULL;
605}
606
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200607static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, uint32_t ip,
608 uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200609{
Harald Welte805f6442009-07-28 18:25:29 +0200610 int rc;
611 socklen_t alen = sizeof(rss->sin_local);
612
Harald Welteead7a7b2009-07-28 00:01:58 +0200613 rss->sin_local.sin_family = AF_INET;
614 rss->sin_local.sin_addr.s_addr = htonl(ip);
615 rss->sin_local.sin_port = htons(port);
616 rss->bfd.when |= BSC_FD_READ;
617
Harald Welte805f6442009-07-28 18:25:29 +0200618 rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
619 sizeof(rss->sin_local));
620 if (rc < 0)
621 return rc;
622
623 /* retrieve the address we actually bound to, in case we
624 * passed INADDR_ANY as IP address */
625 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
626 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200627}
628
629#define RTP_PORT_BASE 30000
Harald Welte805f6442009-07-28 18:25:29 +0200630static unsigned int next_udp_port = RTP_PORT_BASE;
Harald Welteead7a7b2009-07-28 00:01:58 +0200631
Harald Welte647db842013-02-03 12:06:58 +0100632/*! \brief bind a RTP socket to a specific local address
633 * \param[in] rs RTP socket to be bound
634 * \param[in] ip local IP address to which socket is to be bound
635 */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200636int rtp_socket_bind(struct rtp_socket *rs, uint32_t ip)
Harald Welteead7a7b2009-07-28 00:01:58 +0200637{
Harald Welte805f6442009-07-28 18:25:29 +0200638 int rc = -EIO;
639 struct in_addr ia;
640
641 ia.s_addr = htonl(ip);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200642 DEBUGP(DLMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
Harald Welte805f6442009-07-28 18:25:29 +0200643 inet_ntoa(ia));
Harald Welteead7a7b2009-07-28 00:01:58 +0200644
645 /* try to bind to a consecutive pair of ports */
Harald Welte805f6442009-07-28 18:25:29 +0200646 for (next_udp_port = next_udp_port % 0xffff;
647 next_udp_port < 0xffff; next_udp_port += 2) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200648 rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
649 if (rc != 0)
650 continue;
651
652 rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
653 if (rc == 0)
654 break;
655 }
Harald Welte805f6442009-07-28 18:25:29 +0200656 if (rc < 0) {
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200657 DEBUGPC(DLMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200658 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200659 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200660
Harald Welte805f6442009-07-28 18:25:29 +0200661 ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200662 DEBUGPC(DLMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
Harald Welte805f6442009-07-28 18:25:29 +0200663 inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
Harald Welteead7a7b2009-07-28 00:01:58 +0200664 return ntohs(rs->rtp.sin_local.sin_port);
665}
666
667static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200668 uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200669{
Harald Welte805f6442009-07-28 18:25:29 +0200670 int rc;
671 socklen_t alen = sizeof(rss->sin_local);
672
Harald Welteead7a7b2009-07-28 00:01:58 +0200673 rss->sin_remote.sin_family = AF_INET;
674 rss->sin_remote.sin_addr.s_addr = htonl(ip);
675 rss->sin_remote.sin_port = htons(port);
676
Harald Welte805f6442009-07-28 18:25:29 +0200677 rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
678 sizeof(rss->sin_remote));
679 if (rc < 0)
680 return rc;
681
682 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
683 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200684}
685
Harald Welte647db842013-02-03 12:06:58 +0100686/*! \brief 'connect' a RTP socket to a remote peer
687 * \param[in] rs RTP socket to be connected
688 * \param[in] ip remote IP address to which to connect
689 * \param[in] port remote UDP port number to which to connect
690 */
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200691int rtp_socket_connect(struct rtp_socket *rs, uint32_t ip, uint16_t port)
Harald Welteead7a7b2009-07-28 00:01:58 +0200692{
693 int rc;
Harald Welte805f6442009-07-28 18:25:29 +0200694 struct in_addr ia;
695
696 ia.s_addr = htonl(ip);
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200697 DEBUGP(DLMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
Harald Welte805f6442009-07-28 18:25:29 +0200698 rs, inet_ntoa(ia), port);
Harald Welteead7a7b2009-07-28 00:01:58 +0200699
700 rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
701 if (rc < 0)
702 return rc;
703
704 return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
705}
706
Harald Welte647db842013-02-03 12:06:58 +0100707/*! \brief bind two RTP/RTCP sockets together in the proxy
708 * \param[in] this First RTP socket
709 * \param[in] other Second RTP socket
710 */
Harald Welteead7a7b2009-07-28 00:01:58 +0200711int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
712{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200713 DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
Harald Welte805f6442009-07-28 18:25:29 +0200714 this, other);
715
Harald Welteead7a7b2009-07-28 00:01:58 +0200716 this->rx_action = RTP_PROXY;
717 this->proxy.other_sock = other;
718
719 other->rx_action = RTP_PROXY;
720 other->proxy.other_sock = this;
721
722 return 0;
723}
724
Harald Welte647db842013-02-03 12:06:58 +0100725/*! \brief bind RTP/RTCP socket to application, disabling proxy
726 * \param[in] this RTP socket
727 * \param[in] net gsm_network argument to trau_tx_to_mncc()
728 * \param[in] callref callref argument to trau_tx_to_mncc()
729 */
Harald Welte9fb1f102009-12-20 17:07:23 +0100730int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net,
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200731 uint32_t callref)
Harald Welteda7ab742009-12-19 22:23:05 +0100732{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200733 DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, callref=%u)\n",
Harald Welteda7ab742009-12-19 22:23:05 +0100734 this, callref);
735
736 if (callref) {
737 this->rx_action = RTP_RECV_UPSTREAM;
738 this->receive.net = net;
739 this->receive.callref = callref;
740 } else
741 this->rx_action = RTP_NONE;
742
743 return 0;
744}
745
Harald Welteead7a7b2009-07-28 00:01:58 +0200746static void free_tx_queue(struct rtp_sub_socket *rss)
747{
748 struct msgb *msg;
749
750 while ((msg = msgb_dequeue(&rss->tx_queue)))
751 msgb_free(msg);
752}
753
Harald Welte647db842013-02-03 12:06:58 +0100754/*! \brief Free/release a previously allocated RTP socket
755 * \param[in[] rs RTP/RTCP socket to be released
756 */
Harald Welteead7a7b2009-07-28 00:01:58 +0200757int rtp_socket_free(struct rtp_socket *rs)
758{
Pablo Neira Ayusoed5cacb2011-08-17 22:44:07 +0200759 DEBUGP(DLMUX, "rtp_socket_free(rs=%p)\n", rs);
Harald Welteead7a7b2009-07-28 00:01:58 +0200760
761 /* make sure we don't leave references dangling to us */
762 if (rs->rx_action == RTP_PROXY &&
763 rs->proxy.other_sock)
764 rs->proxy.other_sock->proxy.other_sock = NULL;
765
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200766 osmo_fd_unregister(&rs->rtp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200767 close(rs->rtp.bfd.fd);
768 free_tx_queue(&rs->rtp);
769
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200770 osmo_fd_unregister(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200771 close(rs->rtcp.bfd.fd);
772 free_tx_queue(&rs->rtcp);
773
774 talloc_free(rs);
775
776 return 0;
777}