blob: 59c0735a5d0ecc24b9584f669630ef92e1f32a46 [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
Holger Hans Peter Freyther89acf062009-07-29 06:46:26 +020060/* iterate over all chunks in one RTCP message, look for CNAME IEs and
Harald Welte805f6442009-07-28 18:25:29 +020061 * 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 */
Harald Welte198f3f52009-07-29 10:46:41 +020071 cur += sizeof(*rh);
Harald Welte805f6442009-07-28 18:25:29 +020072
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;
Holger Hans Peter Freyther1ddb3562009-08-10 07:57:13 +0200185
186 case RTP_RECV_UPSTREAM:
187 case RTP_NONE:
188 /* FIXME: other cases */
189 DEBUGP(DMUX, "unhandled action: %d\n", rs->rx_action);
190 break;
Harald Welteead7a7b2009-07-28 00:01:58 +0200191 }
192
193 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200194
195out_free:
196 msgb_free(msg);
197 return rc;
Harald Welteead7a7b2009-07-28 00:01:58 +0200198}
199
200/* write from tx_queue to RTP/RTCP socket */
201static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss)
202{
203 struct msgb *msg;
204 int written;
205
206 msg = msgb_dequeue(&rss->tx_queue);
207 if (!msg) {
208 rss->bfd.when &= ~BSC_FD_WRITE;
209 return 0;
210 }
211
212 written = write(rss->bfd.fd, msg->data, msg->len);
213 if (written < msg->len) {
214 perror("short write");
215 msgb_free(msg);
216 return -EIO;
217 }
218
219 msgb_free(msg);
220
221 return 0;
222}
223
224
225/* callback for the select.c:bfd_* layer */
226static int rtp_bfd_cb(struct bsc_fd *bfd, unsigned int flags)
227{
228 struct rtp_socket *rs = bfd->data;
229 struct rtp_sub_socket *rss;
230
231 switch (bfd->priv_nr) {
232 case RTP_PRIV_RTP:
233 rss = &rs->rtp;
234 break;
235 case RTP_PRIV_RTCP:
236 rss = &rs->rtcp;
237 break;
238 default:
239 return -EINVAL;
240 }
241
242 if (flags & BSC_FD_READ)
243 rtp_socket_read(rs, rss);
244
245 if (flags & BSC_FD_WRITE)
246 rtp_socket_write(rs, rss);
247
248 return 0;
249}
250
251static void init_rss(struct rtp_sub_socket *rss,
252 struct rtp_socket *rs, int fd, int priv_nr)
253{
254 /* initialize bfd */
255 rss->bfd.fd = fd;
256 rss->bfd.data = rs;
257 rss->bfd.priv_nr = priv_nr;
258 rss->bfd.cb = rtp_bfd_cb;
259}
260
261struct rtp_socket *rtp_socket_create(void)
262{
263 int rc;
264 struct rtp_socket *rs;
265
Harald Welte805f6442009-07-28 18:25:29 +0200266 DEBUGP(DMUX, "rtp_socket_create(): ");
267
Harald Welteead7a7b2009-07-28 00:01:58 +0200268 rs = talloc_zero(tall_bsc_ctx, struct rtp_socket);
269 if (!rs)
270 return NULL;
271
272 INIT_LLIST_HEAD(&rs->rtp.tx_queue);
273 INIT_LLIST_HEAD(&rs->rtcp.tx_queue);
274
275 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
276 if (rc < 0)
277 goto out_free;
278
279 init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP);
280 rc = bsc_register_fd(&rs->rtp.bfd);
281 if (rc < 0)
282 goto out_rtp_socket;
283
284 rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
285 if (rc < 0)
286 goto out_rtp_bfd;
287
288 init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP);
289 rc = bsc_register_fd(&rs->rtcp.bfd);
290 if (rc < 0)
291 goto out_rtcp_socket;
292
Harald Welte805f6442009-07-28 18:25:29 +0200293 DEBUGPC(DMUX, "success\n");
294
295 rc = rtp_socket_bind(rs, INADDR_ANY);
296 if (rc < 0)
297 goto out_rtcp_bfd;
298
Harald Welteead7a7b2009-07-28 00:01:58 +0200299 return rs;
300
Harald Welte805f6442009-07-28 18:25:29 +0200301out_rtcp_bfd:
302 bsc_unregister_fd(&rs->rtcp.bfd);
Harald Welteead7a7b2009-07-28 00:01:58 +0200303out_rtcp_socket:
304 close(rs->rtcp.bfd.fd);
305out_rtp_bfd:
306 bsc_unregister_fd(&rs->rtp.bfd);
307out_rtp_socket:
308 close(rs->rtp.bfd.fd);
309out_free:
310 talloc_free(rs);
Harald Welte805f6442009-07-28 18:25:29 +0200311 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200312 return NULL;
313}
314
315static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, u_int32_t ip,
316 u_int16_t port)
317{
Harald Welte805f6442009-07-28 18:25:29 +0200318 int rc;
319 socklen_t alen = sizeof(rss->sin_local);
320
Harald Welteead7a7b2009-07-28 00:01:58 +0200321 rss->sin_local.sin_family = AF_INET;
322 rss->sin_local.sin_addr.s_addr = htonl(ip);
323 rss->sin_local.sin_port = htons(port);
324 rss->bfd.when |= BSC_FD_READ;
325
Harald Welte805f6442009-07-28 18:25:29 +0200326 rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
327 sizeof(rss->sin_local));
328 if (rc < 0)
329 return rc;
330
331 /* retrieve the address we actually bound to, in case we
332 * passed INADDR_ANY as IP address */
333 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
334 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200335}
336
337#define RTP_PORT_BASE 30000
Harald Welte805f6442009-07-28 18:25:29 +0200338static unsigned int next_udp_port = RTP_PORT_BASE;
Harald Welteead7a7b2009-07-28 00:01:58 +0200339
340/* bind a RTP socket to a local address */
341int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip)
342{
Harald Welte805f6442009-07-28 18:25:29 +0200343 int rc = -EIO;
344 struct in_addr ia;
345
346 ia.s_addr = htonl(ip);
347 DEBUGP(DMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs,
348 inet_ntoa(ia));
Harald Welteead7a7b2009-07-28 00:01:58 +0200349
350 /* try to bind to a consecutive pair of ports */
Harald Welte805f6442009-07-28 18:25:29 +0200351 for (next_udp_port = next_udp_port % 0xffff;
352 next_udp_port < 0xffff; next_udp_port += 2) {
Harald Welteead7a7b2009-07-28 00:01:58 +0200353 rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port);
354 if (rc != 0)
355 continue;
356
357 rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1);
358 if (rc == 0)
359 break;
360 }
Harald Welte805f6442009-07-28 18:25:29 +0200361 if (rc < 0) {
362 DEBUGPC(DMUX, "failed\n");
Harald Welteead7a7b2009-07-28 00:01:58 +0200363 return rc;
Harald Welte805f6442009-07-28 18:25:29 +0200364 }
Harald Welteead7a7b2009-07-28 00:01:58 +0200365
Harald Welte805f6442009-07-28 18:25:29 +0200366 ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr;
367 DEBUGPC(DMUX, "BOUND_IP=%s, BOUND_PORT=%u\n",
368 inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port));
Harald Welteead7a7b2009-07-28 00:01:58 +0200369 return ntohs(rs->rtp.sin_local.sin_port);
370}
371
372static int rtp_sub_socket_connect(struct rtp_sub_socket *rss,
373 u_int32_t ip, u_int16_t port)
374{
Harald Welte805f6442009-07-28 18:25:29 +0200375 int rc;
376 socklen_t alen = sizeof(rss->sin_local);
377
Harald Welteead7a7b2009-07-28 00:01:58 +0200378 rss->sin_remote.sin_family = AF_INET;
379 rss->sin_remote.sin_addr.s_addr = htonl(ip);
380 rss->sin_remote.sin_port = htons(port);
381
Harald Welte805f6442009-07-28 18:25:29 +0200382 rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote,
383 sizeof(rss->sin_remote));
384 if (rc < 0)
385 return rc;
386
387 return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local,
388 &alen);
Harald Welteead7a7b2009-07-28 00:01:58 +0200389}
390
391/* 'connect' a RTP socket to a remote peer */
392int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port)
393{
394 int rc;
Harald Welte805f6442009-07-28 18:25:29 +0200395 struct in_addr ia;
396
397 ia.s_addr = htonl(ip);
398 DEBUGP(DMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n",
399 rs, inet_ntoa(ia), port);
Harald Welteead7a7b2009-07-28 00:01:58 +0200400
401 rc = rtp_sub_socket_connect(&rs->rtp, ip, port);
402 if (rc < 0)
403 return rc;
404
405 return rtp_sub_socket_connect(&rs->rtcp, ip, port+1);
406}
407
408/* bind two RTP/RTCP sockets together */
409int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other)
410{
Harald Welte805f6442009-07-28 18:25:29 +0200411 DEBUGP(DMUX, "rtp_socket_proxy(this=%p, other=%p)\n",
412 this, other);
413
Harald Welteead7a7b2009-07-28 00:01:58 +0200414 this->rx_action = RTP_PROXY;
415 this->proxy.other_sock = other;
416
417 other->rx_action = RTP_PROXY;
418 other->proxy.other_sock = this;
419
420 return 0;
421}
422
423static void free_tx_queue(struct rtp_sub_socket *rss)
424{
425 struct msgb *msg;
426
427 while ((msg = msgb_dequeue(&rss->tx_queue)))
428 msgb_free(msg);
429}
430
431int rtp_socket_free(struct rtp_socket *rs)
432{
Harald Welte805f6442009-07-28 18:25:29 +0200433 DEBUGP(DMUX, "rtp_socket_free(rs=%p)\n", rs);
Harald Welteead7a7b2009-07-28 00:01:58 +0200434
435 /* make sure we don't leave references dangling to us */
436 if (rs->rx_action == RTP_PROXY &&
437 rs->proxy.other_sock)
438 rs->proxy.other_sock->proxy.other_sock = NULL;
439
440 bsc_unregister_fd(&rs->rtp.bfd);
441 close(rs->rtp.bfd.fd);
442 free_tx_queue(&rs->rtp);
443
444 bsc_unregister_fd(&rs->rtcp.bfd);
445 close(rs->rtcp.bfd.fd);
446 free_tx_queue(&rs->rtcp);
447
448 talloc_free(rs);
449
450 return 0;
451}