blob: 61fc385755b0d64ac399ca640116bfd1b1bdcb05 [file] [log] [blame]
Harald Welteead7a7b2009-07-28 00:01:58 +02001/* RTP proxy handling for ip.access nanoBTS */
2
3/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 */
21
22#include <errno.h>
23#include <unistd.h>
24#include <sys/socket.h>
25#include <netinet/in.h>
Harald Welte805f6442009-07-28 18:25:29 +020026#include <arpa/inet.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020027
28#include <openbsc/talloc.h>
29#include <openbsc/gsm_data.h>
30#include <openbsc/msgb.h>
31#include <openbsc/select.h>
Harald Welte805f6442009-07-28 18:25:29 +020032#include <openbsc/debug.h>
Harald Welteead7a7b2009-07-28 00:01:58 +020033#include <openbsc/rtp_proxy.h>
34
35static LLIST_HEAD(rtp_sockets);
36
Harald Welte805f6442009-07-28 18:25:29 +020037/* should we mangle the CNAME inside SDES of RTCP packets? We disable
38 * this by default, as it seems to be not needed */
39static int mangle_rtcp_cname = 0;
40
Harald Welteead7a7b2009-07-28 00:01:58 +020041enum rtp_bfd_priv {
42 RTP_PRIV_NONE,
43 RTP_PRIV_RTP,
44 RTP_PRIV_RTCP
45};
46
47#define RTP_ALLOC_SIZE 1500
48
Harald Welte805f6442009-07-28 18:25:29 +020049/* according to RFC 1889 */
50struct rtcp_hdr {
51 u_int8_t byte0;
52 u_int8_t type;
53 u_int16_t length;
54} __attribute__((packed));
55
56#define RTCP_TYPE_SDES 202
57
58#define RTCP_IE_CNAME 1
59
60/* iterate over all chunks in one RTCP message, kook for CNAME IEs and
61 * replace all of those with 'new_cname' */
62static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh,
63 u_int16_t *rtcp_len, const char *new_cname)
64{
65 u_int8_t *rtcp_end;
66 u_int8_t *cur = (u_int8_t *) rh;
67 u_int8_t tag, len = 0;
68
69 rtcp_end = cur + *rtcp_len;
70 /* move cur to end of RTP header */
71 cur += sizeof(rh);
72
73 /* iterate over Chunks */
74 while (cur+4 < rtcp_end) {
75 /* skip four bytes SSRC/CSRC */
76 cur += 4;
77
78 /* iterate over IE's inside the chunk */
79 while (cur+1 < rtcp_end) {
80 tag = *cur++;
81 if (tag == 0) {
82 /* end of chunk, skip additional zero */
83 while (*cur++ == 0) { }
84 break;
85 }
86 len = *cur++;
87
88 if (tag == RTCP_IE_CNAME) {
89 /* we've found the CNAME, lets mangle it */
90 if (len < strlen(new_cname)) {
91 /* we need to make more space */
92 int increase = strlen(new_cname) - len;
93
94 msgb_push(msg, increase);
95 memmove(cur+len+increase, cur+len,
96 rtcp_end - (cur+len));
97 /* FIXME: we have to respect RTCP
98 * padding/alignment rules! */
99 len += increase;
100 *(cur-1) += increase;
101 rtcp_end += increase;
102 *rtcp_len += increase;
103 }
104 /* copy new CNAME into message */
105 memcpy(cur, new_cname, strlen(new_cname));
106 /* FIXME: zero the padding in case new CNAME
107 * is smaller than old one !!! */
108 }
109 cur += len;
110 }
111 }
112
113 return 0;
114}
115
116static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs)
117{
118 struct rtp_sub_socket *rss = &rs->rtcp;
119 struct rtcp_hdr *rtph;
120 u_int16_t old_len;
121 int rc;
122
123 if (!mangle_rtcp_cname)
124 return 0;
125
126 /* iterate over list of RTCP messages */
127 rtph = (struct rtcp_hdr *)msg->data;
128 while ((void *)rtph + sizeof(*rtph) < (void *)msg->data + msg->len) {
129 old_len = (ntohs(rtph->length) + 1) * 4;
130 if (rtph->type == RTCP_TYPE_SDES) {
131 char new_cname[255];
132 strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr),
133 sizeof(new_cname));
134 new_cname[sizeof(new_cname)-1] = '\0';
135 rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len,
136 new_cname);
137 if (rc < 0)
138 return rc;
139 }
140 rtph = (void *)rtph + old_len;
141 }
142
143 return 0;
144}
145
Harald Welteead7a7b2009-07-28 00:01:58 +0200146/* read from incoming RTP/RTCP socket */
147static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss)
148{
149 int rc;
150 struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP");
151 struct rtp_sub_socket *other_rss;
152
153 if (!msg)
154 return -ENOMEM;
155
156 rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE);
157 if (rc <= 0) {
158 rss->bfd.when &= ~BSC_FD_READ;
159 return rc;
160 }
161
162 msgb_put(msg, rc);
163
164 switch (rs->rx_action) {
165 case RTP_PROXY:
Harald Welte805f6442009-07-28 18:25:29 +0200166 if (!rs->proxy.other_sock) {
167 rc = -EIO;
168 goto out_free;
169 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200170 if (rss->bfd.priv_nr == RTP_PRIV_RTP)
171 other_rss = &rs->proxy.other_sock->rtp;
Harald Welte805f6442009-07-28 18:25:29 +0200172 else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200173 other_rss = &rs->proxy.other_sock->rtcp;
Harald Welte805f6442009-07-28 18:25:29 +0200174 /* modify RTCP SDES CNAME */
175 rc = rtcp_mangle(msg, rs);
176 if (rc < 0)
177 goto out_free;
178 } else {
179 rc = -EINVAL;
180 goto out_free;
Harald Welteead7a7b2009-07-28 00:01:58 +0200181 }
182 msgb_enqueue(&other_rss->tx_queue, msg);
183 other_rss->bfd.when |= BSC_FD_WRITE;
184 break;
185 /* FIXME: other cases */
186 }
187
188 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200189
190out_free:
191 msgb_free(msg);
192 return rc;
Harald Welteead7a7b2009-07-28 00:01:58 +0200193}
194
195/* write from tx_queue to RTP/RTCP socket */
196static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
197{
198 struct msgb *msg;
199 int written;
200
201 msg = msgb_dequeue(&rss->tx_queue);
202 if (!msg) {
203 rss->bfd.when &= ~BSC_FD_WRITE;
204 return 0;
205 }
206
207 written = write(rss->bfd.fd, msg->data, msg->len);
208 if (written < msg->len) {
209 perror("short write");
210 msgb_free(msg);
211 return -EIO;
212 }
213
214 msgb_free(msg);
215
216 return 0;
217}
218
219
220/* callback for the select.c:bfd_* layer */
221static int rtp_bfd_cb(struct bsc_fd *bfd, unsigned int flags)
222{
223 struct rtp_socket *rs = bfd->data;
224 struct rtp_sub_socket *rss;
225
226 switch (bfd->priv_nr) {
227 case RTP_PRIV_RTP:
228 rss = &rs->rtp;
229 break;
230 case RTP_PRIV_RTCP:
231 rss = &rs->rtcp;
232 break;
233 default:
234 return -EINVAL;
235 }
236
237 if (flags & BSC_FD_READ)
238 rtp_socket_read(rs, rss);
239
240 if (flags & BSC_FD_WRITE)
241 rtp_socket_write(rs, rss);
242
243 return 0;
244}
245
246static void init_rss(struct rtp_sub_socket *rss,
247 struct rtp_socket *rs, int fd, int priv_nr)
248{
249 /* initialize bfd */
250 rss->bfd.fd = fd;
251 rss->bfd.data = rs;
252 rss->bfd.priv_nr = priv_nr;
253 rss->bfd.cb = rtp_bfd_cb;
254}
255
256struct rtp_socket *rtp_socket_create(void)
257{
258 int rc;
259 struct rtp_socket *rs;
260
Harald Welte805f6442009-07-28 18:25:29 +0200261 DEBUGP(DMUX, "rtp_socket_create(): ");
262
Harald Welteead7a7b2009-07-28 00:01:58 +0200263 rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
264 if (!rs)
265 return NULL;
266
267 INIT_LLIST_HEAD(&rs->rtp.tx_queue);
268 INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
269
270 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
271 if (rc < 0)
272 goto out_free;
273
274 init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
275 rc = bsc_register_fd(&rs->rtp.bfd);
276 if (rc < 0)
277 goto out_rtp_socket;
278
279 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
280 if (rc < 0)
281 goto out_rtp_bfd;
282
283 init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
284 rc = bsc_register_fd(&rs->rtcp.bfd);
285 if (rc < 0)
286 goto out_rtcp_socket;
287
Harald Welte805f6442009-07-28 18:25:29 +0200288 DEBUGPC(DMUX, "success\n");
289
290 rc = rtp_socket_bind(rs, INADDR_ANY);
291 if (rc < 0)
292 goto out_rtcp_bfd;
293
Harald Welteead7a7b2009-07-28 00:01:58 +0200294 return rs;
295
Harald Welte805f6442009-07-28 18:25:29 +0200296out_rtcp_bfd:
297 bsc_unregister_fd(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200298out_rtcp_socket:
299 close(rs->rtcp.bfd.fd);
300out_rtp_bfd:
301 bsc_unregister_fd(&rs->rtp.bfd);
302out_rtp_socket:
303 close(rs->rtp.bfd.fd);
304out_free:
305 talloc_free(rs);
Harald Welte805f6442009-07-28 18:25:29 +0200306 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200307 return NULL;
308}
309
310static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, u_int32_t ip,
311 u_int16_t port)
312{
Harald Welte805f6442009-07-28 18:25:29 +0200313 int rc;
314 socklen_t alen = sizeof(rss->sin_local);
315
Harald Welteead7a7b2009-07-28 00:01:58 +0200316 rss->sin_local.sin_family = AF_INET;
317 rss->sin_local.sin_addr.s_addr = htonl(ip);
318 rss->sin_local.sin_port = htons(port);
319 rss->bfd.when |= BSC_FD_READ;
320
Harald Welte805f6442009-07-28 18:25:29 +0200321 rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
322 sizeof(rss->sin_local));
323 if (rc < 0)
324 return rc;
325
326 /* retrieve the address we actually bound to, in case we
327 * passed INADDR_ANY as IP address */
328 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
329 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200330}
331
332#define RTP_PORT_BASE 30000
Harald Welte805f6442009-07-28 18:25:29 +0200333static unsigned int next_udp_port = RTP_PORT_BASE;
Harald Welteead7a7b2009-07-28 00:01:58 +0200334
335/* bind a RTP socket to a local address */
336int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip)
337{
Harald Welte805f6442009-07-28 18:25:29 +0200338 int rc = -EIO;
339 struct in_addr ia;
340
341 ia.s_addr = htonl(ip);
342 DEBUGP(DMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
343 inet_ntoa(ia));
Harald Welteead7a7b2009-07-28 00:01:58 +0200344
345 /* try to bind to a consecutive pair of ports */
Harald Welte805f6442009-07-28 18:25:29 +0200346 for (next_udp_port = next_udp_port % 0xffff;
347 next_udp_port < 0xffff; next_udp_port += 2) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200348 rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
349 if (rc != 0)
350 continue;
351
352 rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
353 if (rc == 0)
354 break;
355 }
Harald Welte805f6442009-07-28 18:25:29 +0200356 if (rc < 0) {
357 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200358 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200359 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200360
Harald Welte805f6442009-07-28 18:25:29 +0200361 ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
362 DEBUGPC(DMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
363 inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
Harald Welteead7a7b2009-07-28 00:01:58 +0200364 return ntohs(rs->rtp.sin_local.sin_port);
365}
366
367static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
368 u_int32_t ip, u_int16_t port)
369{
Harald Welte805f6442009-07-28 18:25:29 +0200370 int rc;
371 socklen_t alen = sizeof(rss->sin_local);
372
Harald Welteead7a7b2009-07-28 00:01:58 +0200373 rss->sin_remote.sin_family = AF_INET;
374 rss->sin_remote.sin_addr.s_addr = htonl(ip);
375 rss->sin_remote.sin_port = htons(port);
376
Harald Welte805f6442009-07-28 18:25:29 +0200377 rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
378 sizeof(rss->sin_remote));
379 if (rc < 0)
380 return rc;
381
382 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
383 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200384}
385
386/* 'connect' a RTP socket to a remote peer */
387int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port)
388{
389 int rc;
Harald Welte805f6442009-07-28 18:25:29 +0200390 struct in_addr ia;
391
392 ia.s_addr = htonl(ip);
393 DEBUGP(DMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
394 rs, inet_ntoa(ia), port);
Harald Welteead7a7b2009-07-28 00:01:58 +0200395
396 rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
397 if (rc < 0)
398 return rc;
399
400 return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
401}
402
403/* bind two RTP/RTCP sockets together */
404int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
405{
Harald Welte805f6442009-07-28 18:25:29 +0200406 DEBUGP(DMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
407 this, other);
408
Harald Welteead7a7b2009-07-28 00:01:58 +0200409 this->rx_action = RTP_PROXY;
410 this->proxy.other_sock = other;
411
412 other->rx_action = RTP_PROXY;
413 other->proxy.other_sock = this;
414
415 return 0;
416}
417
418static void free_tx_queue(struct rtp_sub_socket *rss)
419{
420 struct msgb *msg;
421
422 while ((msg = msgb_dequeue(&rss->tx_queue)))
423 msgb_free(msg);
424}
425
426int rtp_socket_free(struct rtp_socket *rs)
427{
Harald Welte805f6442009-07-28 18:25:29 +0200428 DEBUGP(DMUX, "rtp_socket_free(rs=%p)\n", rs);
Harald Welteead7a7b2009-07-28 00:01:58 +0200429
430 /* make sure we don't leave references dangling to us */
431 if (rs->rx_action == RTP_PROXY &&
432 rs->proxy.other_sock)
433 rs->proxy.other_sock->proxy.other_sock = NULL;
434
435 bsc_unregister_fd(&rs->rtp.bfd);
436 close(rs->rtp.bfd.fd);
437 free_tx_queue(&rs->rtp);
438
439 bsc_unregister_fd(&rs->rtcp.bfd);
440 close(rs->rtcp.bfd.fd);
441 free_tx_queue(&rs->rtcp);
442
443 talloc_free(rs);
444
445 return 0;
446}