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