blob: de2cf629d74be602217fc880c75b5518679b7951 [file] [log] [blame]
Harald Welte2ca7c312009-12-23 22:44:04 +01001/* OpenBSC Abis/IP proxy ip.access nanoBTS */
2
3/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08004 * (C) 2010 by On Waves
5 * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
Harald Welte2ca7c312009-12-23 22:44:04 +01006 *
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 */
24
25#include <stdio.h>
26#include <unistd.h>
27#include <stdlib.h>
28#include <errno.h>
29#include <string.h>
30#include <signal.h>
31#include <time.h>
32#include <sys/fcntl.h>
33#include <sys/types.h>
34#include <sys/socket.h>
35#include <sys/ioctl.h>
36#include <arpa/inet.h>
37#include <netinet/in.h>
38
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +080039#define _GNU_SOURCE
40#include <getopt.h>
41
Harald Welte2ca7c312009-12-23 22:44:04 +010042#include <openbsc/gsm_data.h>
Harald Weltedfe6c7d2010-02-20 16:24:02 +010043#include <osmocore/select.h>
44#include <osmocore/tlv.h>
45#include <osmocore/msgb.h>
Harald Welte2ca7c312009-12-23 22:44:04 +010046#include <openbsc/debug.h>
47#include <openbsc/ipaccess.h>
Harald Weltedfe6c7d2010-02-20 16:24:02 +010048#include <osmocore/talloc.h>
Harald Welte2ca7c312009-12-23 22:44:04 +010049
Harald Weltedc5062b2010-03-26 21:28:59 +080050static struct log_target *stderr_target;
Harald Welte2ca7c312009-12-23 22:44:04 +010051
52/* one instance of an ip.access protocol proxy */
53struct ipa_proxy {
54 /* socket where we listen for incoming OML from BTS */
55 struct bsc_fd oml_listen_fd;
56 /* socket where we listen for incoming RSL from BTS */
57 struct bsc_fd rsl_listen_fd;
58 /* list of BTS's (struct ipa_bts_conn */
59 struct llist_head bts_list;
60 /* the BSC reconnect timer */
61 struct timer_list reconn_timer;
62};
63
64/* global pointer to the proxy structure */
65static struct ipa_proxy *ipp;
66
67struct ipa_proxy_conn {
68 struct bsc_fd fd;
69 struct llist_head tx_queue;
70 struct ipa_bts_conn *bts_conn;
71};
72
73#define MAX_TRX 4
74
75/* represents a particular BTS in our proxy */
76struct ipa_bts_conn {
77 /* list of BTS's (ipa_proxy->bts_list) */
78 struct llist_head list;
79 /* back pointer to the proxy which we belong to */
80 struct ipa_proxy *ipp;
81 /* the unit ID as determined by CCM */
82 struct {
83 u_int16_t site_id;
84 u_int16_t bts_id;
85 } unit_id;
86
87 /* incoming connections from BTS */
88 struct ipa_proxy_conn *oml_conn;
89 struct ipa_proxy_conn *rsl_conn[MAX_TRX];
90
91 /* outgoing connections to BSC */
92 struct ipa_proxy_conn *bsc_oml_conn;
93 struct ipa_proxy_conn *bsc_rsl_conn[MAX_TRX];
94
95 /* UDP sockets for BTS and BSC injection */
96 struct bsc_fd udp_bts_fd;
97 struct bsc_fd udp_bsc_fd;
98
99 char *id_tags[0xff];
100 u_int8_t *id_resp;
101 unsigned int id_resp_len;
102};
103
104enum ipp_fd_type {
105 OML_FROM_BTS = 1,
106 RSL_FROM_BTS = 2,
107 OML_TO_BSC = 3,
108 RSL_TO_BSC = 4,
109 UDP_TO_BTS = 5,
110 UDP_TO_BSC = 6,
111};
112
113/* some of the code against we link from OpenBSC needs this */
114void *tall_bsc_ctx;
115
116static char *listen_ipaddr;
117static char *bsc_ipaddr;
118
Holger Hans Peter Freyther3dac8812010-06-09 14:39:22 +0800119#define PROXY_ALLOC_SIZE 1200
Harald Welte2ca7c312009-12-23 22:44:04 +0100120
121static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG };
122static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK };
123static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
124 0x01, IPAC_IDTAG_UNIT,
125 0x01, IPAC_IDTAG_MACADDR,
126 0x01, IPAC_IDTAG_LOCATION1,
127 0x01, IPAC_IDTAG_LOCATION2,
128 0x01, IPAC_IDTAG_EQUIPVERS,
129 0x01, IPAC_IDTAG_SWVERSION,
130 0x01, IPAC_IDTAG_UNITNAME,
131 0x01, IPAC_IDTAG_SERNR,
132 };
133
134static const char *idtag_names[] = {
135 [IPAC_IDTAG_SERNR] = "Serial_Number",
136 [IPAC_IDTAG_UNITNAME] = "Unit_Name",
137 [IPAC_IDTAG_LOCATION1] = "Location_1",
138 [IPAC_IDTAG_LOCATION2] = "Location_2",
139 [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
140 [IPAC_IDTAG_SWVERSION] = "Software_Version",
141 [IPAC_IDTAG_IPADDR] = "IP_Address",
142 [IPAC_IDTAG_MACADDR] = "MAC_Address",
143 [IPAC_IDTAG_UNIT] = "Unit_ID",
144};
145
146static const char *ipac_idtag_name(int tag)
147{
148 if (tag >= ARRAY_SIZE(idtag_names))
149 return "unknown";
150
151 return idtag_names[tag];
152}
153
154static int ipac_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
155{
156 u_int8_t t_len;
157 u_int8_t t_tag;
158 u_int8_t *cur = buf;
159
160 while (cur < buf + len) {
161 t_len = *cur++;
162 t_tag = *cur++;
163
164 DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur);
165
166 dec->lv[t_tag].len = t_len;
167 dec->lv[t_tag].val = cur;
168
169 cur += t_len;
170 }
171 return 0;
172}
173
174static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id,
175 u_int16_t *trx_id)
176{
177 unsigned long ul;
178 char *endptr;
179 const char *nptr;
180
181 nptr = str;
182 ul = strtoul(nptr, &endptr, 10);
183 if (endptr <= nptr)
184 return -EINVAL;
185 if (site_id)
186 *site_id = ul & 0xffff;
187
188 if (*endptr++ != '/')
189 return -EINVAL;
190
191 nptr = endptr;
192 ul = strtoul(nptr, &endptr, 10);
193 if (endptr <= nptr)
194 return -EINVAL;
195 if (bts_id)
196 *bts_id = ul & 0xffff;
197
198 if (*endptr++ != '/')
199 return -EINVAL;
200
201 nptr = endptr;
202 ul = strtoul(nptr, &endptr, 10);
203 if (endptr <= nptr)
204 return -EINVAL;
205 if (trx_id)
206 *trx_id = ul & 0xffff;
207
208 return 0;
209}
210
211static struct ipa_bts_conn *find_bts_by_unitid(struct ipa_proxy *ipp,
212 u_int16_t site_id,
213 u_int16_t bts_id)
214{
215 struct ipa_bts_conn *ipbc;
216
217 llist_for_each_entry(ipbc, &ipp->bts_list, list) {
218 if (ipbc->unit_id.site_id == site_id &&
219 ipbc->unit_id.bts_id == bts_id)
220 return ipbc;
221 }
222
223 return NULL;
224}
225
226struct ipa_proxy_conn *alloc_conn(void)
227{
228 struct ipa_proxy_conn *ipc;
229
230 ipc = talloc_zero(tall_bsc_ctx, struct ipa_proxy_conn);
231 if (!ipc)
232 return NULL;
233
234 INIT_LLIST_HEAD(&ipc->tx_queue);
235
236 return ipc;
237}
238
239static int store_idtags(struct ipa_bts_conn *ipbc, struct tlv_parsed *tlvp)
240{
241 unsigned int i, len;
242
243 for (i = 0; i <= 0xff; i++) {
244 if (!TLVP_PRESENT(tlvp, i))
245 continue;
246
247 len = TLVP_LEN(tlvp, i);
248#if 0
249 if (!ipbc->id_tags[i])
250 ipbc->id_tags[i] = talloc_size(tall_bsc_ctx, len);
251 else
252#endif
Harald Weltea16ef3d2009-12-23 23:35:51 +0100253 ipbc->id_tags[i] = talloc_realloc_size(ipbc,
Harald Welte2ca7c312009-12-23 22:44:04 +0100254 ipbc->id_tags[i], len);
255 if (!ipbc->id_tags[i])
256 return -ENOMEM;
257
258 memset(ipbc->id_tags[i], 0, len);
259 //memcpy(ipbc->id_tags[i], TLVP_VAL(tlvp, i), len);
260 }
261 return 0;
262}
263
264
265static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data);
266
267#define logp_ipbc_uid(ss, lvl, ipbc, trx_id) _logp_ipbc_uid(ss, lvl, __FILE__, __LINE__, ipbc, trx_id)
268
269static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int line,
270 struct ipa_bts_conn *ipbc, u_int8_t trx_id)
271{
272 if (ipbc)
Harald Weltedc5062b2010-03-26 21:28:59 +0800273 logp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id,
Harald Welte2ca7c312009-12-23 22:44:04 +0100274 ipbc->unit_id.bts_id, trx_id);
275 else
Harald Weltedc5062b2010-03-26 21:28:59 +0800276 logp2(ss, lvl, file, line, 0, "unknown ");
Harald Welte2ca7c312009-12-23 22:44:04 +0100277}
278
279/* UDP socket handling */
280
281static int make_sock(struct bsc_fd *bfd, u_int16_t port, int proto, int priv_nr,
282 int (*cb)(struct bsc_fd *fd, unsigned int what),
283 void *data)
284{
285 struct sockaddr_in addr;
286 int ret, on = 1;
287
288 bfd->fd = socket(AF_INET, SOCK_DGRAM, proto);
289 bfd->cb = cb;
290 bfd->when = BSC_FD_READ;
291 bfd->data = data;
292 bfd->priv_nr = priv_nr;
293
294 memset(&addr, 0, sizeof(addr));
295 addr.sin_family = AF_INET;
296 addr.sin_port = htons(port);
297 addr.sin_addr.s_addr = INADDR_ANY;
298
299 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
300
301 ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
302 if (ret < 0) {
303 LOGP(DINP, LOGL_ERROR, "could not bind socket: %s\n",
304 strerror(errno));
305 return -EIO;
306 }
307
308 ret = bsc_register_fd(bfd);
309 if (ret < 0) {
310 perror("register UDP fd");
311 return ret;
312 }
313 return 0;
314}
315
316static int handle_udp_read(struct bsc_fd *bfd)
317{
318 struct ipa_bts_conn *ipbc = bfd->data;
319 struct ipa_proxy_conn *other_conn = NULL;
320 struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP");
321 struct ipaccess_head *hh;
322 int ret;
323
324 /* with UDP sockets, we cannot read partial packets but have to read
325 * all of it in one go */
326 hh = (struct ipaccess_head *) msg->data;
327 ret = recv(bfd->fd, msg->data, msg->data_len, 0);
328 if (ret < 0) {
Harald Weltefb339572009-12-24 13:35:18 +0100329 if (errno != EAGAIN)
330 LOGP(DINP, LOGL_ERROR, "recv error %s\n", strerror(errno));
Harald Welte2ca7c312009-12-23 22:44:04 +0100331 msgb_free(msg);
332 return ret;
333 }
334 if (ret == 0) {
335 DEBUGP(DINP, "UDP peer disappeared, dead socket\n");
336 bsc_unregister_fd(bfd);
337 close(bfd->fd);
338 bfd->fd = -1;
339 msgb_free(msg);
340 return -EIO;
341 }
342 if (ret < sizeof(*hh)) {
343 DEBUGP(DINP, "could not even read header!?!\n");
344 msgb_free(msg);
345 return -EIO;
346 }
347 msgb_put(msg, ret);
348 msg->l2h = msg->data + sizeof(*hh);
349 DEBUGP(DMI, "UDP RX: %s\n", hexdump(msg->data, msg->len));
350
351 if (hh->len != msg->len - sizeof(*hh)) {
352 DEBUGP(DINP, "length (%u/%u) disagrees with header(%u)\n",
353 msg->len, msg->len - 3, hh->len);
354 msgb_free(msg);
355 return -EIO;
356 }
357
358 switch (bfd->priv_nr & 0xff) {
359 case UDP_TO_BTS:
360 /* injection towards BTS */
361 switch (hh->proto) {
362 case IPAC_PROTO_RSL:
363 /* FIXME: what to do about TRX > 0 */
364 other_conn = ipbc->rsl_conn[0];
365 break;
366 default:
367 DEBUGP(DINP, "Unknown protocol 0x%02x, sending to "
368 "OML FD\n", hh->proto);
369 /* fall through */
370 case IPAC_PROTO_IPACCESS:
371 case IPAC_PROTO_OML:
372 other_conn = ipbc->oml_conn;
373 break;
374 }
375 break;
376 case UDP_TO_BSC:
377 /* injection towards BSC */
378 switch (hh->proto) {
379 case IPAC_PROTO_RSL:
380 /* FIXME: what to do about TRX > 0 */
381 other_conn = ipbc->bsc_rsl_conn[0];
382 break;
383 default:
384 DEBUGP(DINP, "Unknown protocol 0x%02x, sending to "
385 "OML FD\n", hh->proto);
386 case IPAC_PROTO_IPACCESS:
387 case IPAC_PROTO_OML:
388 other_conn = ipbc->bsc_oml_conn;
389 break;
390 }
391 break;
392 default:
393 DEBUGP(DINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr);
394 break;
395 }
396
397 if (other_conn) {
398 /* enqueue the message for TX on the respective FD */
399 msgb_enqueue(&other_conn->tx_queue, msg);
400 other_conn->fd.when |= BSC_FD_WRITE;
401 } else
402 msgb_free(msg);
403
404 return 0;
405}
406
407static int handle_udp_write(struct bsc_fd *bfd)
408{
409 /* not implemented yet */
410 bfd->when &= ~BSC_FD_WRITE;
411
412 return -EIO;
413}
414
415/* callback from select.c in case one of the fd's can be read/written */
416static int udp_fd_cb(struct bsc_fd *bfd, unsigned int what)
417{
418 int rc = 0;
419
420 if (what & BSC_FD_READ)
421 rc = handle_udp_read(bfd);
422 if (what & BSC_FD_WRITE)
423 rc = handle_udp_write(bfd);
424
425 return rc;
426}
427
428
429static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct bsc_fd *bfd,
430 u_int16_t site_id, u_int16_t bts_id,
431 u_int16_t trx_id, struct tlv_parsed *tlvp,
432 struct msgb *msg)
433{
434 struct ipa_bts_conn *ipbc;
435 u_int16_t udp_port;
436 int ret = 0;
437 struct sockaddr_in sin;
438
439 memset(&sin, 0, sizeof(sin));
440 sin.sin_family = AF_INET;
441 inet_aton(bsc_ipaddr, &sin.sin_addr);
442
443 DEBUGP(DINP, "(%u/%u/%u) New BTS connection: ",
444 site_id, bts_id, trx_id);
445
446 /* OML needs to be established before RSL */
447 if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) {
448 DEBUGPC(DINP, "Not a OML connection ?!?\n");
449 return -EIO;
450 }
451
452 /* allocate new BTS connection data structure */
453 ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn);
454 if (!ipbc) {
455 ret = -ENOMEM;
456 goto err_out;
457 }
458
459 DEBUGPC(DINP, "Created BTS Conn data structure\n");
460 ipbc->ipp = ipp;
461 ipbc->unit_id.site_id = site_id;
462 ipbc->unit_id.bts_id = bts_id;
463 ipbc->oml_conn = ipc;
464 ipc->bts_conn = ipbc;
465
466 /* store the content of the ID TAGS for later reference */
467 store_idtags(ipbc, tlvp);
468 ipbc->id_resp_len = msg->len;
469 ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len);
470 memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len);
471
472 /* Create OML TCP connection towards BSC */
Harald Welte87ed5cd2009-12-23 22:47:53 +0100473 sin.sin_port = htons(IPA_TCP_PORT_OML);
Harald Welte2ca7c312009-12-23 22:44:04 +0100474 ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
475 if (!ipbc->bsc_oml_conn) {
476 ret = -EIO;
477 goto err_bsc_conn;
478 }
479
480 DEBUGP(DINP, "(%u/%u/%u) OML Connected to BSC\n",
481 site_id, bts_id, trx_id);
482
483 /* Create UDP socket for BTS packet injection */
484 udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100);
485 ret = make_sock(&ipbc->udp_bts_fd, udp_port, IPPROTO_UDP,
486 UDP_TO_BTS, udp_fd_cb, ipbc);
487 if (ret < 0)
488 goto err_udp_bts;
489 DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection "
490 "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port);
491
492 /* Create UDP socket for BSC packet injection */
493 udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100);
494 ret = make_sock(&ipbc->udp_bsc_fd, udp_port, IPPROTO_UDP,
495 UDP_TO_BSC, udp_fd_cb, ipbc);
496 if (ret < 0)
497 goto err_udp_bsc;
498 DEBUGP(DINP, "(%u/%u/%u) Created UDP socket for injection "
499 "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port);
500 llist_add(&ipbc->list, &ipp->bts_list);
501
502 return 0;
503
504err_udp_bsc:
505 bsc_unregister_fd(&ipbc->udp_bts_fd);
506err_udp_bts:
507 bsc_unregister_fd(&ipbc->bsc_oml_conn->fd);
508 close(ipbc->bsc_oml_conn->fd.fd);
509 talloc_free(ipbc->bsc_oml_conn);
510 ipbc->bsc_oml_conn = NULL;
511err_bsc_conn:
512 talloc_free(ipbc->id_resp);
513 talloc_free(ipbc);
514#if 0
515 bsc_unregister_fd(bfd);
516 close(bfd->fd);
517 talloc_free(bfd);
518#endif
519err_out:
520 return ret;
521}
522
523static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg,
524 struct bsc_fd *bfd)
525{
526 struct tlv_parsed tlvp;
527 u_int8_t msg_type = *(msg->l2h);
528 u_int16_t site_id, bts_id, trx_id;
529 struct ipa_bts_conn *ipbc;
530 int ret = 0;
531
532 switch (msg_type) {
533 case IPAC_MSGT_PING:
534 ret = write(bfd->fd, pong, sizeof(pong));
535 if (ret < 0)
536 return ret;
537 if (ret < sizeof(pong)) {
538 DEBUGP(DINP, "short write\n");
539 return -EIO;
540 }
541 break;
542 case IPAC_MSGT_PONG:
543 DEBUGP(DMI, "PONG!\n");
544 break;
545 case IPAC_MSGT_ID_RESP:
546 DEBUGP(DMI, "ID_RESP ");
547 /* parse tags, search for Unit ID */
548 ipac_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2,
549 msgb_l2len(msg)-2);
550 DEBUGP(DMI, "\n");
551
552 if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) {
553 LOGP(DINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n");
554 return -EIO;
555 }
556
557 /* lookup BTS, create sign_link, ... */
Holger Hans Peter Freyther82ae7162010-03-30 15:20:46 +0200558 site_id = bts_id = trx_id = 0;
Harald Welte2ca7c312009-12-23 22:44:04 +0100559 parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT),
560 &site_id, &bts_id, &trx_id);
561 ipbc = find_bts_by_unitid(ipp, site_id, bts_id);
562 if (!ipbc) {
563 /* We have not found an ipbc (per-bts proxy instance)
564 * for this BTS yet. The first connection of a new BTS must
565 * be a OML connection. We allocate the associated data structures,
566 * and try to connect to the remote end */
567
568 return ipbc_alloc_connect(ipc, bfd, site_id, bts_id,
569 trx_id, &tlvp, msg);
570 /* if this fails, the caller will clean up bfd */
571 } else {
572 struct sockaddr_in sin;
573 memset(&sin, 0, sizeof(sin));
574 sin.sin_family = AF_INET;
575 inet_aton(bsc_ipaddr, &sin.sin_addr);
576
577 DEBUGP(DINP, "Identified BTS %u/%u/%u\n",
578 site_id, bts_id, trx_id);
579
580 if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) {
581 LOGP(DINP, LOGL_ERROR, "Second OML connection from "
582 "same BTS ?!?\n");
583 return 0;
584 }
585
586 if (trx_id > MAX_TRX) {
587 LOGP(DINP, LOGL_ERROR, "We don't support more "
588 "than %u TRX\n", MAX_TRX);
589 return -EINVAL;
590 }
591
592 ipc->bts_conn = ipbc;
593 /* store TRX number in higher 8 bit of the bfd private number */
594 bfd->priv_nr |= trx_id << 8;
595 ipbc->rsl_conn[trx_id] = ipc;
596
597 /* Create RSL TCP connection towards BSC */
Harald Welte87ed5cd2009-12-23 22:47:53 +0100598 sin.sin_port = htons(IPA_TCP_PORT_RSL);
Harald Welte2ca7c312009-12-23 22:44:04 +0100599 ipbc->bsc_rsl_conn[trx_id] =
600 connect_bsc(&sin, RSL_TO_BSC | (trx_id << 8), ipbc);
601 if (!ipbc->bsc_oml_conn)
602 return -EIO;
603 DEBUGP(DINP, "(%u/%u/%u) Connected RSL to BSC\n",
604 site_id, bts_id, trx_id);
605 }
606 break;
607 case IPAC_MSGT_ID_GET:
608 DEBUGP(DMI, "ID_GET\n");
609 if ((bfd->priv_nr & 0xff) != OML_TO_BSC &&
610 (bfd->priv_nr & 0xff) != RSL_TO_BSC) {
611 DEBUGP(DINP, "IDentity REQuest from BTS ?!?\n");
612 return -EIO;
613 }
614 ipbc = ipc->bts_conn;
615 if (!ipbc) {
616 DEBUGP(DINP, "ID_GET from BSC before we have ID_RESP from BTS\n");
617 return -EIO;
618 }
619 ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len);
620 break;
621 case IPAC_MSGT_ID_ACK:
622 DEBUGP(DMI, "ID_ACK? -> ACK!\n");
623 ret = write(bfd->fd, id_ack, sizeof(id_ack));
624 break;
625 }
626 return 0;
627}
628
629struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
630{
631 struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP");
632 struct ipaccess_head *hh;
633 int len, ret = 0;
634
635 if (!msg) {
636 *error = -ENOMEM;
637 return NULL;
638 }
639
640 /* first read our 3-byte header */
641 hh = (struct ipaccess_head *) msg->data;
642 ret = recv(bfd->fd, msg->data, 3, 0);
643 if (ret < 0) {
Harald Weltefb339572009-12-24 13:35:18 +0100644 if (errno != EAGAIN)
Harald Welteda956932009-12-24 12:49:43 +0100645 LOGP(DINP, LOGL_ERROR, "recv error: %s\n", strerror(errno));
Harald Welte2ca7c312009-12-23 22:44:04 +0100646 msgb_free(msg);
647 *error = ret;
648 return NULL;
649 } else if (ret == 0) {
650 msgb_free(msg);
651 *error = ret;
652 return NULL;
653 }
654
655 msgb_put(msg, ret);
656
657 /* then read te length as specified in header */
658 msg->l2h = msg->data + sizeof(*hh);
659 len = ntohs(hh->len);
660 ret = recv(bfd->fd, msg->l2h, len, 0);
661 if (ret < len) {
662 LOGP(DINP, LOGL_ERROR, "short read!\n");
663 msgb_free(msg);
664 *error = -EIO;
665 return NULL;
666 }
667 msgb_put(msg, ret);
668
669 return msg;
670}
671
672static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc,
673 unsigned int priv_nr)
674{
675 struct ipa_proxy_conn *bsc_conn;
676 unsigned int trx_id = priv_nr >> 8;
677
678 switch (priv_nr & 0xff) {
679 case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
680 bsc_conn = ipbc->bsc_oml_conn;
681 break;
682 case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
683 bsc_conn = ipbc->bsc_rsl_conn[trx_id];
684 break;
685 case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
686 bsc_conn = ipbc->oml_conn;
687 break;
688 case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
689 bsc_conn = ipbc->rsl_conn[trx_id];
690 break;
691 default:
692 bsc_conn = NULL;
693 break;
694 }
695 return bsc_conn;
696}
697
698static void reconn_tmr_cb(void *data)
699{
700 struct ipa_proxy *ipp = data;
701 struct ipa_bts_conn *ipbc;
702 struct sockaddr_in sin;
703 int i;
704
705 DEBUGP(DINP, "Running reconnect timer\n");
706
707 memset(&sin, 0, sizeof(sin));
708 sin.sin_family = AF_INET;
709 inet_aton(bsc_ipaddr, &sin.sin_addr);
710
711 llist_for_each_entry(ipbc, &ipp->bts_list, list) {
712 /* if OML to BSC is dead, try to restore it */
713 if (ipbc->oml_conn && !ipbc->bsc_oml_conn) {
Harald Welte87ed5cd2009-12-23 22:47:53 +0100714 sin.sin_port = htons(IPA_TCP_PORT_OML);
Harald Welte2ca7c312009-12-23 22:44:04 +0100715 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0);
716 LOGPC(DINP, LOGL_NOTICE, "OML Trying to reconnect\n");
717 ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
718 if (!ipbc->bsc_oml_conn)
719 goto reschedule;
720 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, 0);
721 LOGPC(DINP, LOGL_NOTICE, "OML Reconnected\n");
722 }
723 /* if we (still) don't have a OML connection, skip RSL */
724 if (!ipbc->oml_conn || !ipbc->bsc_oml_conn)
725 continue;
726
727 for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) {
728 unsigned int priv_nr;
729 /* don't establish RSL links which we don't have */
730 if (!ipbc->rsl_conn[i])
731 continue;
732 if (ipbc->bsc_rsl_conn[i])
733 continue;
734 priv_nr = ipbc->rsl_conn[i]->fd.priv_nr;
735 priv_nr &= ~0xff;
736 priv_nr |= RSL_TO_BSC;
Harald Welte87ed5cd2009-12-23 22:47:53 +0100737 sin.sin_port = htons(IPA_TCP_PORT_RSL);
Harald Welte2ca7c312009-12-23 22:44:04 +0100738 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
739 LOGPC(DINP, LOGL_NOTICE, "RSL Trying to reconnect\n");
740 ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc);
741 if (!ipbc->bsc_rsl_conn)
742 goto reschedule;
743 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
744 LOGPC(DINP, LOGL_NOTICE, "RSL Reconnected\n");
745 }
746 }
747 return;
748
749reschedule:
750 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
751}
752
753static void handle_dead_socket(struct bsc_fd *bfd)
754{
755 struct ipa_proxy_conn *ipc = bfd->data; /* local conn */
756 struct ipa_proxy_conn *bsc_conn; /* remote conn */
757 struct ipa_bts_conn *ipbc = ipc->bts_conn;
758 unsigned int trx_id = bfd->priv_nr >> 8;
759 struct msgb *msg, *msg2;
760
761 bsc_unregister_fd(bfd);
762 close(bfd->fd);
763 bfd->fd = -1;
764
765 /* FIXME: clear tx_queue, remove all references, etc. */
766 llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list)
767 msgb_free(msg);
768
769 switch (bfd->priv_nr & 0xff) {
770 case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
771 ipbc->oml_conn = NULL;
772 bsc_conn = ipbc->bsc_oml_conn;
773 /* close the connection to the BSC */
774 bsc_unregister_fd(&bsc_conn->fd);
775 close(bsc_conn->fd.fd);
776 llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
777 msgb_free(msg);
778 talloc_free(bsc_conn);
779 ipbc->bsc_oml_conn = NULL;
780 /* FIXME: do we need to delete the entire ipbc ? */
781 break;
782 case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
783 ipbc->rsl_conn[trx_id] = NULL;
784 bsc_conn = ipbc->bsc_rsl_conn[trx_id];
785 /* close the connection to the BSC */
786 bsc_unregister_fd(&bsc_conn->fd);
787 close(bsc_conn->fd.fd);
788 llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
789 msgb_free(msg);
790 talloc_free(bsc_conn);
791 ipbc->bsc_rsl_conn[trx_id] = NULL;
792 break;
793 case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
794 ipbc->bsc_oml_conn = NULL;
795 bsc_conn = ipbc->oml_conn;
796 /* start reconnect timer */
797 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
798 break;
799 case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
800 ipbc->bsc_rsl_conn[trx_id] = NULL;
801 bsc_conn = ipbc->rsl_conn[trx_id];
802 /* start reconnect timer */
803 bsc_schedule_timer(&ipp->reconn_timer, 5, 0);
804 break;
805 default:
806 bsc_conn = NULL;
807 break;
808 }
809
810 talloc_free(ipc);
811}
812
813static int handle_tcp_read(struct bsc_fd *bfd)
814{
815 struct ipa_proxy_conn *ipc = bfd->data;
816 struct ipa_bts_conn *ipbc = ipc->bts_conn;
817 struct ipa_proxy_conn *bsc_conn;
Harald Weltea16ef3d2009-12-23 23:35:51 +0100818 struct msgb *msg;
Harald Welte2ca7c312009-12-23 22:44:04 +0100819 struct ipaccess_head *hh;
820 int ret = 0;
821 char *btsbsc;
822
Harald Welte2ca7c312009-12-23 22:44:04 +0100823 if ((bfd->priv_nr & 0xff) <= 2)
824 btsbsc = "BTS";
825 else
826 btsbsc = "BSC";
827
828 msg = ipaccess_read_msg(bfd, &ret);
829 if (!msg) {
830 if (ret == 0) {
831 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
832 LOGPC(DINP, LOGL_NOTICE, "%s disappeared, "
833 "dead socket\n", btsbsc);
834 handle_dead_socket(bfd);
835 }
836 return ret;
837 }
838
839 msgb_put(msg, ret);
840 logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
841 DEBUGPC(DMI, "RX<-%s: %s\n", btsbsc, hexdump(msg->data, msg->len));
842
843 hh = (struct ipaccess_head *) msg->data;
844 if (hh->proto == IPAC_PROTO_IPACCESS) {
845 ret = ipaccess_rcvmsg(ipc, msg, bfd);
846 if (ret < 0) {
847 bsc_unregister_fd(bfd);
848 close(bfd->fd);
849 bfd->fd = -1;
850 talloc_free(bfd);
851 }
852 /* we do not forward the CCM protocol through the
853 * proxy but rather terminate it ourselves */
854 msgb_free(msg);
855 return ret;
856 }
857
858 if (!ipbc) {
859 LOGP(DINP, LOGL_ERROR,
860 "received %s packet but no ipc->bts_conn?!?\n", btsbsc);
861 msgb_free(msg);
862 return -EIO;
863 }
864
865 bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr);
866 if (bsc_conn) {
867 /* enqueue packet towards BSC */
868 msgb_enqueue(&bsc_conn->tx_queue, msg);
869 /* mark respective filedescriptor as 'we want to write' */
870 bsc_conn->fd.when |= BSC_FD_WRITE;
871 } else {
872 logp_ipbc_uid(DINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8);
873 LOGPC(DINP, LOGL_INFO, "Dropping packet from %s, "
874 "since remote connection is dead\n", btsbsc);
875 msgb_free(msg);
876 }
877
878 return ret;
879}
880
881/* a TCP socket is ready to be written to */
882static int handle_tcp_write(struct bsc_fd *bfd)
883{
884 struct ipa_proxy_conn *ipc = bfd->data;
885 struct ipa_bts_conn *ipbc = ipc->bts_conn;
886 struct llist_head *lh;
887 struct msgb *msg;
888 char *btsbsc;
889 int ret;
890
891 if ((bfd->priv_nr & 0xff) <= 2)
892 btsbsc = "BTS";
893 else
894 btsbsc = "BSC";
895
896
897 /* get the next msg for this timeslot */
898 if (llist_empty(&ipc->tx_queue)) {
899 bfd->when &= ~BSC_FD_WRITE;
900 return 0;
901 }
902 lh = ipc->tx_queue.next;
903 llist_del(lh);
904 msg = llist_entry(lh, struct msgb, list);
905
906 logp_ipbc_uid(DMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
907 DEBUGPC(DMI, "TX %04x: %s\n", bfd->priv_nr,
908 hexdump(msg->data, msg->len));
909
910 ret = send(bfd->fd, msg->data, msg->len, 0);
911 msgb_free(msg);
912
913 if (ret == 0) {
914 logp_ipbc_uid(DINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
915 LOGP(DINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc);
916 handle_dead_socket(bfd);
917 }
918
919 return ret;
920}
921
922/* callback from select.c in case one of the fd's can be read/written */
923static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
924{
925 int rc = 0;
926
927 if (what & BSC_FD_READ) {
928 rc = handle_tcp_read(bfd);
929 if (rc < 0)
930 return rc;
931 }
932 if (what & BSC_FD_WRITE)
933 rc = handle_tcp_write(bfd);
934
935 return rc;
936}
937
938/* callback of the listening filedescriptor */
939static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
940{
941 int ret;
942 struct ipa_proxy_conn *ipc;
943 struct bsc_fd *bfd;
944 struct sockaddr_in sa;
945 socklen_t sa_len = sizeof(sa);
946
947 if (!(what & BSC_FD_READ))
948 return 0;
949
950 ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
951 if (ret < 0) {
952 perror("accept");
953 return ret;
954 }
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200955 DEBUGP(DINP, "accept()ed new %s link from %s\n",
Harald Welte2ca7c312009-12-23 22:44:04 +0100956 (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL",
957 inet_ntoa(sa.sin_addr));
958
959 ipc = alloc_conn();
960 if (!ipc) {
961 close(ret);
962 return -ENOMEM;
963 }
964
965 bfd = &ipc->fd;
966 bfd->fd = ret;
967 bfd->data = ipc;
968 bfd->priv_nr = listen_bfd->priv_nr;
969 bfd->cb = ipaccess_fd_cb;
970 bfd->when = BSC_FD_READ;
971 ret = bsc_register_fd(bfd);
972 if (ret < 0) {
973 LOGP(DINP, LOGL_ERROR, "could not register FD\n");
974 close(bfd->fd);
975 talloc_free(ipc);
976 return ret;
977 }
978
979 /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
980 ret = write(bfd->fd, id_req, sizeof(id_req));
981
982 return 0;
983}
984
985static int make_listen_sock(struct bsc_fd *bfd, u_int16_t port, int priv_nr,
986 int (*cb)(struct bsc_fd *fd, unsigned int what))
987{
988 struct sockaddr_in addr;
989 int ret, on = 1;
990
991 bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
992 bfd->cb = cb;
993 bfd->when = BSC_FD_READ;
994 bfd->priv_nr = priv_nr;
995
996 memset(&addr, 0, sizeof(addr));
997 addr.sin_family = AF_INET;
998 addr.sin_port = htons(port);
999 if (!listen_ipaddr)
1000 addr.sin_addr.s_addr = INADDR_ANY;
1001 else
1002 inet_aton(listen_ipaddr, &addr.sin_addr);
1003
1004 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
1005
1006 ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
1007 if (ret < 0) {
1008 LOGP(DINP, LOGL_ERROR, "could not bind listen socket %s\n",
1009 strerror(errno));
1010 return -EIO;
1011 }
1012
1013 ret = listen(bfd->fd, 1);
1014 if (ret < 0) {
1015 perror("listen");
1016 return ret;
1017 }
1018
1019 ret = bsc_register_fd(bfd);
1020 if (ret < 0) {
1021 perror("register_listen_fd");
1022 return ret;
1023 }
1024 return 0;
1025}
1026
1027/* Actively connect to a BSC. */
1028static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data)
1029{
1030 struct ipa_proxy_conn *ipc;
1031 struct bsc_fd *bfd;
1032 int ret, on = 1;
1033
1034 ipc = alloc_conn();
1035 if (!ipc)
1036 return NULL;
1037
1038 ipc->bts_conn = data;
1039
1040 bfd = &ipc->fd;
1041 bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1042 bfd->cb = ipaccess_fd_cb;
1043 bfd->when = BSC_FD_READ | BSC_FD_WRITE;
1044 bfd->data = ipc;
1045 bfd->priv_nr = priv_nr;
1046
1047 setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
1048
1049 ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
1050 if (ret < 0) {
Holger Hans Peter Freyther6cb9b232010-06-09 15:15:11 +08001051 LOGP(DINP, LOGL_ERROR, "Could not connect socket: %s\n",
1052 inet_ntoa(sa->sin_addr));
Harald Welte2ca7c312009-12-23 22:44:04 +01001053 close(bfd->fd);
1054 talloc_free(ipc);
1055 return NULL;
1056 }
1057
1058 /* pre-fill tx_queue with identity request */
1059 ret = bsc_register_fd(bfd);
1060 if (ret < 0) {
1061 close(bfd->fd);
1062 talloc_free(ipc);
1063 return NULL;
1064 }
1065
1066 return ipc;
1067}
1068
1069static int ipaccess_proxy_setup(void)
1070{
1071 int ret;
1072
1073 ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy);
1074 if (!ipp)
1075 return -ENOMEM;
1076 INIT_LLIST_HEAD(&ipp->bts_list);
1077 ipp->reconn_timer.cb = reconn_tmr_cb;
1078 ipp->reconn_timer.data = ipp;
1079
1080 /* Listen for OML connections */
Harald Welte87ed5cd2009-12-23 22:47:53 +01001081 ret = make_listen_sock(&ipp->oml_listen_fd, IPA_TCP_PORT_OML,
1082 OML_FROM_BTS, listen_fd_cb);
Harald Welte2ca7c312009-12-23 22:44:04 +01001083 if (ret < 0)
1084 return ret;
1085
1086 /* Listen for RSL connections */
Harald Welte87ed5cd2009-12-23 22:47:53 +01001087 ret = make_listen_sock(&ipp->rsl_listen_fd, IPA_TCP_PORT_RSL,
1088 RSL_FROM_BTS, listen_fd_cb);
Harald Welte2ca7c312009-12-23 22:44:04 +01001089
1090 return ret;
1091}
1092
1093static void signal_handler(int signal)
1094{
1095 fprintf(stdout, "signal %u received\n", signal);
1096
1097 switch (signal) {
1098 case SIGABRT:
1099 /* in case of abort, we want to obtain a talloc report
1100 * and then return to the caller, who will abort the process */
1101 case SIGUSR1:
1102 talloc_report_full(tall_bsc_ctx, stderr);
1103 break;
1104 default:
1105 break;
1106 }
1107}
1108
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001109static void print_help()
1110{
1111 printf(" ipaccess-proxy is a proxy BTS.\n");
1112 printf(" -h --help. This help text.\n");
1113 printf(" -l --listen IP. The ip to listen to.\n");
1114 printf(" -b --bsc IP. The BSC IP address.\n\n");
1115 printf(" -s --disable-color. Disable the color inside the logging message.\n");
1116 printf(" -e --log-level number. Set the global loglevel.\n");
1117 printf(" -T --timestamp. Prefix every log message with a timestamp.\n");
1118 printf(" -V --version. Print the version of OpenBSC.\n");
1119}
1120
1121static void print_usage()
1122{
1123 printf("Usage: ipaccess-proxy\n");
1124}
1125
1126static void handle_options(int argc, char** argv)
1127{
1128 while (1) {
1129 int option_index = 0, c;
1130 static struct option long_options[] = {
1131 {"help", 0, 0, 'h'},
1132 {"disable-color", 0, 0, 's'},
1133 {"timestamp", 0, 0, 'T'},
1134 {"log-level", 1, 0, 'e'},
1135 {"listen", 1, 0, 'l'},
1136 {"bsc", 1, 0, 'b'},
1137 {0, 0, 0, 0}
1138 };
1139
1140 c = getopt_long(argc, argv, "hsTe:l:b:",
1141 long_options, &option_index);
1142 if (c == -1)
1143 break;
1144
1145 switch (c) {
1146 case 'h':
1147 print_usage();
1148 print_help();
1149 exit(0);
1150 case 'l':
1151 listen_ipaddr = optarg;
1152 break;
1153 case 'b':
1154 bsc_ipaddr = optarg;
1155 break;
1156 case 's':
1157 log_set_use_color(stderr_target, 0);
1158 break;
1159 case 'T':
1160 log_set_print_timestamp(stderr_target, 1);
1161 break;
1162 case 'e':
1163 log_set_log_level(stderr_target, atoi(optarg));
1164 break;
1165 default:
1166 /* ignore */
1167 break;
1168 }
1169 }
1170}
1171
Harald Welte2ca7c312009-12-23 22:44:04 +01001172int main(int argc, char **argv)
1173{
1174 int rc;
1175
1176 listen_ipaddr = "192.168.100.11";
1177 bsc_ipaddr = "192.168.100.239";
1178
1179 tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy");
1180
Harald Weltedc5062b2010-03-26 21:28:59 +08001181 log_init(&log_info);
1182 stderr_target = log_target_create_stderr();
1183 log_add_target(stderr_target);
1184 log_set_all_filter(stderr_target, 1);
1185 log_parse_category_mask(stderr_target, "DINP:DMI");
Harald Welte2ca7c312009-12-23 22:44:04 +01001186
Holger Hans Peter Freyther1a0c2b72010-06-09 14:59:09 +08001187 handle_options(argc, argv);
1188
Harald Welte2ca7c312009-12-23 22:44:04 +01001189 rc = ipaccess_proxy_setup();
1190 if (rc < 0)
1191 exit(1);
1192
1193 signal(SIGUSR1, &signal_handler);
1194 signal(SIGABRT, &signal_handler);
1195
1196 while (1) {
1197 bsc_select_main(0);
1198 }
1199}