aboutsummaryrefslogtreecommitdiffstats
path: root/src/ipaccess
diff options
context:
space:
mode:
authorNeels Hofmeyr <neels@hofmeyr.de>2017-07-04 23:08:44 +0200
committerNeels Hofmeyr <neels@hofmeyr.de>2017-08-27 03:52:43 +0200
commit218e4b4aa0fc6de842ff820dec8e97d1f083268a (patch)
tree268a6e509270b1c80a36dd1a526da41a9b01a8e0 /src/ipaccess
parent5ea6bfce56d6ae7be6d85e05b5e4eaebc94d1005 (diff)
move openbsc/* to repos root
This is the first step in creating this repository from the legacy openbsc.git. Like all other Osmocom repositories, keep the autoconf and automake files in the repository root. openbsc.git has been the sole exception, which ends now. Change-Id: I9c6f2a448d9cb1cc088cf1cf6918b69d7e69b4e7
Diffstat (limited to 'src/ipaccess')
-rw-r--r--src/ipaccess/Makefile.am66
-rw-r--r--src/ipaccess/abisip-find.c216
-rw-r--r--src/ipaccess/ipaccess-config.c1019
-rw-r--r--src/ipaccess/ipaccess-firmware.c135
-rw-r--r--src/ipaccess/ipaccess-proxy.c1226
-rw-r--r--src/ipaccess/network_listen.c257
6 files changed, 2919 insertions, 0 deletions
diff --git a/src/ipaccess/Makefile.am b/src/ipaccess/Makefile.am
new file mode 100644
index 000000000..4dfe24738
--- /dev/null
+++ b/src/ipaccess/Makefile.am
@@ -0,0 +1,66 @@
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir) \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOGSM_CFLAGS) \
+ $(LIBOSMOABIS_CFLAGS) \
+ $(COVERAGE_CFLAGS) \
+ $(NULL)
+
+AM_LDFLAGS = \
+ $(COVERAGE_LDFLAGS) \
+ $(NULL)
+
+OSMO_LIBS = \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOGSM_LIBS) \
+ $(LIBOSMOABIS_LIBS) \
+ $(NULL)
+
+bin_PROGRAMS = \
+ abisip-find \
+ ipaccess-config \
+ ipaccess-proxy \
+ $(NULL)
+
+abisip_find_LDADD = \
+ $(top_builddir)/src/libbsc/libbsc.a \
+ $(top_builddir)/src/libtrau/libtrau.a \
+ $(top_builddir)/src/libcommon/libcommon.a \
+ $(OSMO_LIBS) \
+ $(NULL)
+
+abisip_find_SOURCES = \
+ abisip-find.c \
+ $(NULL)
+
+ipaccess_config_SOURCES = \
+ ipaccess-config.c \
+ ipaccess-firmware.c \
+ network_listen.c \
+ $(NULL)
+
+# FIXME: resolve the bogus dependencies patched around here:
+ipaccess_config_LDADD = \
+ $(top_builddir)/src/libbsc/libbsc.a \
+ $(top_builddir)/src/libcommon-cs/libcommon-cs.a \
+ $(top_builddir)/src/libtrau/libtrau.a \
+ $(top_builddir)/src/libcommon/libcommon.a \
+ $(OSMO_LIBS) \
+ $(NULL)
+
+ipaccess_proxy_SOURCES = \
+ ipaccess-proxy.c \
+ $(NULL)
+
+ipaccess_proxy_LDADD = \
+ $(top_builddir)/src/libbsc/libbsc.a \
+ $(top_builddir)/src/libtrau/libtrau.a \
+ $(top_builddir)/src/libcommon/libcommon.a \
+ $(OSMO_LIBS) \
+ $(NULL)
diff --git a/src/ipaccess/abisip-find.c b/src/ipaccess/abisip-find.c
new file mode 100644
index 000000000..21d9f2290
--- /dev/null
+++ b/src/ipaccess/abisip-find.c
@@ -0,0 +1,216 @@
+/* ip.access nanoBTS configuration tool */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/gsm/protocol/ipaccess.h>
+#include <osmocom/gsm/ipa.h>
+#include <openbsc/gsm_data.h>
+
+static int udp_sock(const char *ifname)
+{
+ int fd, rc, bc = 1;
+ struct sockaddr_in sa;
+
+ fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (fd < 0)
+ return fd;
+
+ if (ifname) {
+#ifdef __FreeBSD__
+ rc = setsockopt(fd, SOL_SOCKET, IP_RECVIF, ifname,
+ strlen(ifname));
+#else
+ rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
+ strlen(ifname));
+#endif
+ if (rc < 0)
+ goto err;
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(3006);
+ sa.sin_addr.s_addr = INADDR_ANY;
+
+ rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
+ if (rc < 0)
+ goto err;
+
+ rc = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bc, sizeof(bc));
+ if (rc < 0)
+ goto err;
+
+#if 0
+ /* we cannot bind, since the response packets don't come from
+ * the broadcast address */
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(3006);
+ inet_aton("255.255.255.255", &sa.sin_addr);
+
+ rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
+ if (rc < 0)
+ goto err;
+#endif
+ return fd;
+
+err:
+ close(fd);
+ return rc;
+}
+
+const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00,
+ IPAC_MSGT_ID_GET,
+ 0x01, IPAC_IDTAG_MACADDR,
+ 0x01, IPAC_IDTAG_IPADDR,
+ 0x01, IPAC_IDTAG_UNIT,
+ 0x01, IPAC_IDTAG_LOCATION1,
+ 0x01, IPAC_IDTAG_LOCATION2,
+ 0x01, IPAC_IDTAG_EQUIPVERS,
+ 0x01, IPAC_IDTAG_SWVERSION,
+ 0x01, IPAC_IDTAG_UNITNAME,
+ 0x01, IPAC_IDTAG_SERNR,
+ };
+
+
+static int bcast_find(int fd)
+{
+ struct sockaddr_in sa;
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(3006);
+ inet_aton("255.255.255.255", &sa.sin_addr);
+
+ return sendto(fd, find_pkt, sizeof(find_pkt), 0, (struct sockaddr *) &sa, sizeof(sa));
+}
+
+static int parse_response(unsigned char *buf, int len)
+{
+ uint8_t t_len;
+ uint8_t t_tag;
+ uint8_t *cur = buf;
+
+ while (cur < buf + len) {
+ t_len = *cur++;
+ t_tag = *cur++;
+
+ printf("%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
+
+ cur += t_len;
+ }
+ printf("\n");
+ return 0;
+}
+
+static int read_response(int fd)
+{
+ unsigned char buf[255];
+ struct sockaddr_in sa;
+ int len;
+ socklen_t sa_len = sizeof(sa);
+
+ len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len);
+ if (len < 0)
+ return len;
+
+ /* 2 bytes length, 1 byte protocol */
+ if (buf[2] != IPAC_PROTO_IPACCESS)
+ return 0;
+
+ if (buf[4] != IPAC_MSGT_ID_RESP)
+ return 0;
+
+ return parse_response(buf+6, len-6);
+}
+
+static int bfd_cb(struct osmo_fd *bfd, unsigned int flags)
+{
+ if (flags & BSC_FD_READ)
+ return read_response(bfd->fd);
+ if (flags & BSC_FD_WRITE) {
+ bfd->when &= ~BSC_FD_WRITE;
+ return bcast_find(bfd->fd);
+ }
+ return 0;
+}
+
+static struct osmo_timer_list timer;
+
+static void timer_cb(void *_data)
+{
+ struct osmo_fd *bfd = _data;
+
+ bfd->when |= BSC_FD_WRITE;
+
+ osmo_timer_schedule(&timer, 5, 0);
+}
+
+int main(int argc, char **argv)
+{
+ struct osmo_fd bfd;
+ char *ifname = NULL;
+ int rc;
+
+ printf("abisip-find (C) 2009 by Harald Welte\n");
+ printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
+
+ if (argc < 2) {
+ fprintf(stdout, "you might need to specify the outgoing\n"
+ " network interface, e.g. ``%s eth0''\n", argv[0]);
+ } else {
+ ifname = argv[1];
+ }
+
+ bfd.cb = bfd_cb;
+ bfd.when = BSC_FD_READ | BSC_FD_WRITE;
+ bfd.fd = udp_sock(ifname);
+ if (bfd.fd < 0) {
+ perror("Cannot create local socket for broadcast udp");
+ exit(1);
+ }
+
+ rc = osmo_fd_register(&bfd);
+ if (rc < 0) {
+ fprintf(stderr, "Cannot register FD\n");
+ exit(1);
+ }
+
+ osmo_timer_setup(&timer, timer_cb, &bfd);
+ osmo_timer_schedule(&timer, 5, 0);
+
+ printf("Trying to find ip.access BTS by broadcast UDP...\n");
+
+ while (1) {
+ rc = osmo_select_main(0);
+ if (rc < 0)
+ exit(3);
+ }
+
+ exit(0);
+}
+
diff --git a/src/ipaccess/ipaccess-config.c b/src/ipaccess/ipaccess-config.c
new file mode 100644
index 000000000..6822c06a6
--- /dev/null
+++ b/src/ipaccess/ipaccess-config.c
@@ -0,0 +1,1019 @@
+/* ip.access nanoBTS configuration tool */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009-2011 by Holger Hans Peter Freyther
+ * (C) 2009-2010 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/timer.h>
+#include <openbsc/ipaccess.h>
+#include <openbsc/common_bsc.h>
+#include <osmocom/abis/e1_input.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/signal.h>
+#include <openbsc/debug.h>
+#include <openbsc/network_listen.h>
+#include <osmocom/abis/ipaccess.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/signal.h>
+#include <openbsc/debug.h>
+#include <openbsc/network_listen.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/abis/abis.h>
+#include <osmocom/gsm/protocol/gsm_12_21.h>
+
+struct gsm_network *bsc_gsmnet;
+
+static int net_listen_testnr;
+static int restart;
+static char *prim_oml_ip;
+static char *bts_ip_addr, *bts_ip_mask, *bts_ip_gw;
+static char *unit_id;
+static uint16_t nv_flags;
+static uint16_t nv_mask;
+static char *software = NULL;
+static int sw_load_state = 0;
+static int oml_state = 0;
+static int dump_files = 0;
+static char *firmware_analysis = NULL;
+static int found_trx = 0;
+static int loop_tests = 0;
+
+static void *tall_ctx_config = NULL;
+static struct abis_nm_sw_desc *sw_load1 = NULL;
+static struct abis_nm_sw_desc *sw_load2 = NULL;
+
+/*
+static uint8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 };
+static uint8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 };
+*/
+
+extern int ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what);
+extern struct e1inp_line_ops ipaccess_e1inp_line_ops;
+
+/* Actively connect to a BTS. Currently used by ipaccess-config.c */
+static int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa)
+{
+ struct e1inp_ts *e1i_ts = &line->ts[0];
+ struct osmo_fd *bfd = &e1i_ts->driver.ipaccess.fd;
+ int ret, on = 1;
+
+ bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ bfd->cb = ipaccess_fd_cb;
+ bfd->when = BSC_FD_READ | BSC_FD_WRITE;
+ bfd->data = line;
+ bfd->priv_nr = E1INP_SIGN_OML;
+
+ if (bfd->fd < 0) {
+ LOGP(DLINP, LOGL_ERROR, "could not create TCP socket.\n");
+ return -EIO;
+ }
+
+ ret = setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "could not set socket option\n");
+ close(bfd->fd);
+ return -EIO;
+ }
+
+ ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "could not connect socket\n");
+ close(bfd->fd);
+ return ret;
+ }
+
+ ret = osmo_fd_register(bfd);
+ if (ret < 0) {
+ close(bfd->fd);
+ return ret;
+ }
+ return ret;
+ //return e1inp_line_register(line);
+}
+
+/* configure pseudo E1 line in ip.access style and connect to BTS */
+static int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin)
+{
+ struct e1inp_line *line;
+ struct e1inp_ts *sign_ts, *rsl_ts;
+ struct e1inp_sign_link *oml_link, *rsl_link;
+
+ line = talloc_zero(tall_bsc_ctx, struct e1inp_line);
+ if (!line)
+ return -ENOMEM;
+
+ line->driver = e1inp_driver_find("ipa");
+ if (!line->driver) {
+ fprintf(stderr, "cannot `ipa' driver, giving up.\n");
+ return -EINVAL;
+ }
+ line->ops = &ipaccess_e1inp_line_ops;
+
+ /* create E1 timeslots for signalling and TRAU frames */
+ e1inp_ts_config_sign(&line->ts[1-1], line);
+ e1inp_ts_config_sign(&line->ts[2-1], line);
+
+ /* create signalling links for TS1 */
+ sign_ts = &line->ts[1-1];
+ rsl_ts = &line->ts[2-1];
+ oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML,
+ bts->c0, 0xff, 0);
+ rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL,
+ bts->c0, 0, 0);
+
+ /* create back-links from bts/trx */
+ bts->oml_link = oml_link;
+ bts->c0->rsl_link = rsl_link;
+
+ /* default port at BTS for incoming connections is 3006 */
+ if (sin->sin_port == 0)
+ sin->sin_port = htons(3006);
+
+ return ipaccess_connect(line, sin);
+}
+
+/*
+ * Callback function for NACK on the OML NM
+ *
+ * Currently we send the config requests but don't check the
+ * result. The nanoBTS will send us a NACK when we did something the
+ * BTS didn't like.
+ */
+static int ipacc_msg_nack(uint8_t mt)
+{
+ fprintf(stderr, "Failure to set attribute. This seems fatal\n");
+ exit(-1);
+ return 0;
+}
+
+static void check_restart_or_exit(struct gsm_bts_trx *trx)
+{
+ if (restart) {
+ abis_nm_ipaccess_restart(trx);
+ } else {
+ exit(0);
+ }
+}
+
+static int ipacc_msg_ack(uint8_t mt, struct gsm_bts_trx *trx)
+{
+ if (sw_load_state == 1) {
+ fprintf(stderr, "The new software is activaed.\n");
+ check_restart_or_exit(trx);
+ } else if (oml_state == 1) {
+ fprintf(stderr, "Set the NV Attributes.\n");
+ check_restart_or_exit(trx);
+ }
+
+ return 0;
+}
+
+static const uint8_t phys_conf_min[] = { 0x02 };
+
+static uint16_t build_physconf(uint8_t *physconf_buf, const struct rxlev_stats *st)
+{
+ uint16_t *whitelist = (uint16_t *) (physconf_buf + 4);
+ int num_arfcn;
+ unsigned int arfcnlist_size;
+
+ /* Create whitelist from rxlevels */
+ physconf_buf[0] = phys_conf_min[0];
+ physconf_buf[1] = NM_IPAC_EIE_ARFCN_WHITE;
+ num_arfcn = ipac_rxlevstat2whitelist(whitelist, st, 0, 100);
+ arfcnlist_size = num_arfcn * 2;
+ *((uint16_t *) (physconf_buf+2)) = htons(arfcnlist_size);
+ DEBUGP(DNM, "physconf_buf (%s)\n", osmo_hexdump(physconf_buf, arfcnlist_size+4));
+ return arfcnlist_size+4;
+}
+
+static int nwl_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct gsm_bts_trx *trx;
+ uint8_t physconf_buf[2*NUM_ARFCNS+16];
+ uint16_t physconf_len;
+
+ switch (signal) {
+ case S_IPAC_NWL_COMPLETE:
+ trx = signal_data;
+ DEBUGP(DNM, "received S_IPAC_NWL_COMPLETE signal\n");
+ switch (trx->ipaccess.test_nr) {
+ case NM_IPACC_TESTNO_CHAN_USAGE:
+ /* Dump RxLev results */
+ //rxlev_stat_dump(&trx->ipaccess.rxlev_stat);
+ /* Create whitelist from results */
+ physconf_len = build_physconf(physconf_buf,
+ &trx->ipaccess.rxlev_stat);
+ /* Start next test abbout BCCH channel usage */
+ ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_CHAN_USAGE,
+ physconf_buf, physconf_len);
+ break;
+ case NM_IPACC_TESTNO_BCCH_CHAN_USAGE:
+ /* Dump BCCH RxLev results */
+ //rxlev_stat_dump(&trx->ipaccess.rxlev_stat);
+ /* Create whitelist from results */
+ physconf_len = build_physconf(physconf_buf,
+ &trx->ipaccess.rxlev_stat);
+ /* Start next test about BCCH info */
+ ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_INFO,
+ physconf_buf, physconf_len);
+ break;
+ case NM_IPACC_TESTNO_BCCH_INFO:
+ /* re-start full process with CHAN_USAGE */
+ if (loop_tests) {
+ DEBUGP(DNM, "starting next test cycle\n");
+ ipac_nwl_test_start(trx, net_listen_testnr, phys_conf_min,
+ sizeof(phys_conf_min));
+ } else {
+ exit(0);
+ }
+ break;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int nm_state_event(int evt, uint8_t obj_class, void *obj,
+ struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
+ struct abis_om_obj_inst *obj_inst);
+
+static int nm_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ struct ipacc_ack_signal_data *ipacc_data;
+ struct nm_statechg_signal_data *nsd;
+
+ switch (signal) {
+ case S_NM_IPACC_NACK:
+ ipacc_data = signal_data;
+ return ipacc_msg_nack(ipacc_data->msg_type);
+ case S_NM_IPACC_ACK:
+ ipacc_data = signal_data;
+ return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->trx);
+ case S_NM_IPACC_RESTART_ACK:
+ printf("The BTS has acked the restart. Exiting.\n");
+ exit(0);
+ break;
+ case S_NM_IPACC_RESTART_NACK:
+ printf("The BTS has nacked the restart. Exiting.\n");
+ exit(0);
+ break;
+ case S_NM_STATECHG_OPER:
+ case S_NM_STATECHG_ADM:
+ nsd = signal_data;
+ nm_state_event(signal, nsd->obj_class, nsd->obj, nsd->old_state,
+ nsd->new_state, nsd->obj_inst);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* callback function passed to the ABIS OML code */
+static int percent;
+static int percent_old;
+static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg,
+ void *data, void *param)
+{
+ struct msgb *msg;
+ struct gsm_bts_trx *trx;
+
+ if (hook != GSM_HOOK_NM_SWLOAD)
+ return 0;
+
+ trx = (struct gsm_bts_trx *) data;
+
+ switch (event) {
+ case NM_MT_LOAD_INIT_ACK:
+ fprintf(stdout, "Software Load Initiate ACK\n");
+ break;
+ case NM_MT_LOAD_INIT_NACK:
+ fprintf(stderr, "ERROR: Software Load Initiate NACK\n");
+ exit(5);
+ break;
+ case NM_MT_LOAD_END_ACK:
+ fprintf(stderr, "LOAD END ACK...");
+ /* now make it the default */
+ sw_load_state = 1;
+
+ msg = msgb_alloc(1024, "sw: nvattr");
+ msg->l2h = msgb_put(msg, 3);
+ msg->l3h = &msg->l2h[3];
+
+ /* activate software */
+ if (sw_load1)
+ abis_nm_put_sw_desc(msg, sw_load1, true);
+
+ if (sw_load2)
+ abis_nm_put_sw_desc(msg, sw_load2, true);
+
+ /* fill in the data */
+ msg->l2h[0] = NM_ATT_IPACC_CUR_SW_CFG;
+ msg->l2h[1] = msgb_l3len(msg) >> 8;
+ msg->l2h[2] = msgb_l3len(msg) & 0xff;
+ printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg));
+ abis_nm_ipaccess_set_nvattr(trx, msg->l2h, msgb_l2len(msg));
+ msgb_free(msg);
+ break;
+ case NM_MT_LOAD_END_NACK:
+ fprintf(stderr, "ERROR: Software Load End NACK\n");
+ exit(3);
+ break;
+ case NM_MT_ACTIVATE_SW_NACK:
+ fprintf(stderr, "ERROR: Activate Software NACK\n");
+ exit(4);
+ break;
+ case NM_MT_ACTIVATE_SW_ACK:
+ break;
+ case NM_MT_LOAD_SEG_ACK:
+ percent = abis_nm_software_load_status(trx->bts);
+ if (percent > percent_old)
+ printf("Software Download Progress: %d%%\n", percent);
+ percent_old = percent;
+ break;
+ case NM_MT_LOAD_ABORT:
+ fprintf(stderr, "ERROR: Load aborted by the BTS.\n");
+ exit(6);
+ break;
+ }
+ return 0;
+}
+
+static void nv_put_ip_if_cfg(struct msgb *nmsg, uint32_t ip, uint32_t mask)
+{
+ msgb_put_u8(nmsg, NM_ATT_IPACC_IP_IF_CFG);
+
+ msgb_put_u32(nmsg, ip);
+ msgb_put_u32(nmsg, mask);
+}
+
+static void nv_put_gw_cfg(struct msgb *nmsg, uint32_t addr, uint32_t mask, uint32_t gw)
+{
+ msgb_put_u8(nmsg, NM_ATT_IPACC_IP_GW_CFG);
+ msgb_put_u32(nmsg, addr);
+ msgb_put_u32(nmsg, mask);
+ msgb_put_u32(nmsg, gw);
+}
+
+static void nv_put_unit_id(struct msgb *nmsg, const char *unit_id)
+{
+ msgb_tl16v_put(nmsg, NM_ATT_IPACC_UNIT_ID, strlen(unit_id)+1,
+ (const uint8_t *)unit_id);
+}
+
+static void nv_put_prim_oml(struct msgb *nmsg, uint32_t ip, uint16_t port)
+{
+ int len;
+
+ /* 0x88 + IP + port */
+ len = 1 + sizeof(ip) + sizeof(port);
+
+ msgb_put_u8(nmsg, NM_ATT_IPACC_PRIM_OML_CFG_LIST);
+ msgb_put_u16(nmsg, len);
+
+ msgb_put_u8(nmsg, 0x88);
+
+ /* IP address */
+ msgb_put_u32(nmsg, ip);
+
+ /* port number */
+ msgb_put_u16(nmsg, port);
+}
+
+static void nv_put_flags(struct msgb *nmsg, uint16_t nv_flags, uint16_t nv_mask)
+{
+ msgb_put_u8(nmsg, NM_ATT_IPACC_NV_FLAGS);
+ msgb_put_u16(nmsg, sizeof(nv_flags) + sizeof(nv_mask));
+ msgb_put_u8(nmsg, nv_flags & 0xff);
+ msgb_put_u8(nmsg, nv_mask & 0xff);
+ msgb_put_u8(nmsg, nv_flags >> 8);
+ msgb_put_u8(nmsg, nv_mask >> 8);
+}
+
+/* human-readable test names for the ip.access tests */
+static const struct value_string ipa_test_strs[] = {
+ { 64, "ccch-usage" },
+ { 65, "bcch-usage" },
+ { 66, "freq-sync" },
+ { 67, "rtp-usage" },
+ { 68, "rtp-perf" },
+ { 69, "gprs-ccch" },
+ { 70, "pccch-usage" },
+ { 71, "gprs-usage" },
+ { 72, "esta-mf" },
+ { 73, "uplink-mf" },
+ { 74, "dolink-mf" },
+ { 75, "tbf-details" },
+ { 76, "tbf-usage" },
+ { 77, "llc-data" },
+ { 78, "pdch-usage" },
+ { 79, "power-control" },
+ { 80, "link-adaption" },
+ { 81, "tch-usage" },
+ { 82, "amr-mf" },
+ { 83, "rtp-multiplex-perf" },
+ { 84, "rtp-multiplex-usage" },
+ { 85, "srtp-multiplex-usage" },
+ { 86, "abis-traffic" },
+ { 89, "gprs-multiplex-perf" },
+ { 90, "gprs-multiplex-usage" },
+ { 0, NULL },
+};
+
+/* human-readable names for the ip.access nanoBTS NVRAM Flags */
+static const struct value_string ipa_nvflag_strs[] = {
+ { 0x0001, "static-ip" },
+ { 0x0002, "static-gw" },
+ { 0x0004, "no-dhcp-vsi" },
+ { 0x0008, "dhcp-enabled" },
+ { 0x0040, "led-disabled" },
+ { 0x0100, "secondary-oml-enabled" },
+ { 0x0200, "diag-enabled" },
+ { 0x0400, "cli-enabled" },
+ { 0x0800, "http-enabled" },
+ { 0x1000, "post-enabled" },
+ { 0x2000, "snmp-enabled" },
+ { 0, NULL }
+};
+
+/* set the flags in flags/mask according to a string-identified flag and 'enable' */
+static int ipa_nvflag_set(uint16_t *flags, uint16_t *mask, const char *name, int en)
+{
+ int rc;
+ rc = get_string_value(ipa_nvflag_strs, name);
+ if (rc < 0)
+ return rc;
+
+ *mask |= rc;
+ if (en)
+ *flags |= rc;
+ else
+ *flags &= ~rc;
+
+ return 0;
+}
+
+static void bootstrap_om(struct gsm_bts_trx *trx)
+{
+ struct msgb *nmsg = msgb_alloc(1024, "nested msgb");
+ int need_to_set_attr = 0;
+ int len;
+
+ printf("OML link established using TRX %d\n", trx->nr);
+
+ if (unit_id) {
+ len = strlen(unit_id);
+ if (len > nmsg->data_len-10)
+ goto out_err;
+ printf("setting Unit ID to '%s'\n", unit_id);
+ nv_put_unit_id(nmsg, unit_id);
+ need_to_set_attr = 1;
+ }
+ if (prim_oml_ip) {
+ struct in_addr ia;
+
+ if (!inet_aton(prim_oml_ip, &ia)) {
+ fprintf(stderr, "invalid IP address: %s\n",
+ prim_oml_ip);
+ goto out_err;
+ }
+
+ printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia));
+ nv_put_prim_oml(nmsg, ntohl(ia.s_addr), 0);
+ need_to_set_attr = 1;
+ }
+ if (nv_mask) {
+ printf("setting NV Flags/Mask to 0x%04x/0x%04x\n",
+ nv_flags, nv_mask);
+ nv_put_flags(nmsg, nv_flags, nv_mask);
+ need_to_set_attr = 1;
+ }
+ if (bts_ip_addr && bts_ip_mask) {
+ struct in_addr ia_addr, ia_mask;
+
+ if (!inet_aton(bts_ip_addr, &ia_addr)) {
+ fprintf(stderr, "invalid IP address: %s\n",
+ bts_ip_addr);
+ goto out_err;
+ }
+
+ if (!inet_aton(bts_ip_mask, &ia_mask)) {
+ fprintf(stderr, "invalid IP address: %s\n",
+ bts_ip_mask);
+ goto out_err;
+ }
+
+ printf("setting static IP Address/Mask\n");
+ nv_put_ip_if_cfg(nmsg, ntohl(ia_addr.s_addr), ntohl(ia_mask.s_addr));
+ need_to_set_attr = 1;
+ }
+ if (bts_ip_gw) {
+ struct in_addr ia_gw;
+
+ if (!inet_aton(bts_ip_gw, &ia_gw)) {
+ fprintf(stderr, "invalid IP address: %s\n",
+ bts_ip_gw);
+ goto out_err;
+ }
+
+ printf("setting static IP Gateway\n");
+ /* we only set the default gateway with zero addr/mask */
+ nv_put_gw_cfg(nmsg, 0, 0, ntohl(ia_gw.s_addr));
+ need_to_set_attr = 1;
+ }
+
+ if (need_to_set_attr) {
+ abis_nm_ipaccess_set_nvattr(trx, nmsg->head, nmsg->len);
+ oml_state = 1;
+ }
+
+ if (restart && !prim_oml_ip && !software) {
+ printf("restarting BTS\n");
+ abis_nm_ipaccess_restart(trx);
+ }
+
+out_err:
+ msgb_free(nmsg);
+}
+
+static int nm_state_event(int evt, uint8_t obj_class, void *obj,
+ struct gsm_nm_state *old_state, struct gsm_nm_state *new_state,
+ struct abis_om_obj_inst *obj_inst)
+{
+ if (obj_class == NM_OC_BASEB_TRANSC) {
+ if (!found_trx && obj_inst->trx_nr != 0xff) {
+ struct gsm_bts_trx *trx = container_of(obj, struct gsm_bts_trx, bb_transc);
+ bootstrap_om(trx);
+ found_trx = 1;
+ }
+ } else if (evt == S_NM_STATECHG_OPER &&
+ obj_class == NM_OC_RADIO_CARRIER &&
+ new_state->availability == 3) {
+ struct gsm_bts_trx *trx = obj;
+
+ if (net_listen_testnr)
+ ipac_nwl_test_start(trx, net_listen_testnr,
+ phys_conf_min, sizeof(phys_conf_min));
+ else if (software) {
+ int rc;
+ printf("Attempting software upload with '%s'\n", software);
+ rc = abis_nm_software_load(trx->bts, trx->nr, software, 19, 0, swload_cbfn, trx);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to start software load\n");
+ exit(-3);
+ }
+ }
+ }
+ return 0;
+}
+
+static struct abis_nm_sw_desc *create_swload(struct sdp_header *header)
+{
+ struct abis_nm_sw_desc *load;
+
+ load = talloc_zero(tall_ctx_config, struct abis_nm_sw_desc);
+
+ osmo_strlcpy((char *)load->file_id, header->firmware_info.sw_part,
+ sizeof(load->file_id));
+ load->file_id_len = strlen((char*)load->file_id) + 1;
+
+ osmo_strlcpy((char *)load->file_version, header->firmware_info.version,
+ sizeof(load->file_version));
+ load->file_version_len = strlen((char*)load->file_version) + 1;
+
+ return load;
+}
+
+static int find_sw_load_params(const char *filename)
+{
+ struct stat stat;
+ struct sdp_header *header;
+ struct llist_head *entry;
+ int fd;
+ void *tall_firm_ctx = 0;
+
+ entry = talloc_zero(tall_firm_ctx, struct llist_head);
+ INIT_LLIST_HEAD(entry);
+
+ fd = open(filename, O_RDONLY);
+ if (!fd) {
+ perror("nada");
+ return -1;
+ }
+
+ /* verify the file */
+ if (fstat(fd, &stat) == -1) {
+ perror("Can not stat the file");
+ close(fd);
+ return -1;
+ }
+
+ ipaccess_analyze_file(fd, stat.st_size, 0, entry);
+ if (close(fd) != 0) {
+ perror("Close failed.\n");
+ return -1;
+ }
+
+ /* try to find what we are looking for */
+ llist_for_each_entry(header, entry, entry) {
+ if (ntohs(header->firmware_info.more_more_magic) == 0x1000) {
+ sw_load1 = create_swload(header);
+ } else if (ntohs(header->firmware_info.more_more_magic) == 0x2001) {
+ sw_load2 = create_swload(header);
+ }
+ }
+
+ if (!sw_load1 || !sw_load2) {
+ fprintf(stderr, "Did not find data.\n");
+ talloc_free(tall_firm_ctx);
+ return -1;
+ }
+
+ talloc_free(tall_firm_ctx);
+ return 0;
+}
+
+static void dump_entry(struct sdp_header_item *sub_entry, int part, int fd)
+{
+ int out_fd;
+ int copied;
+ char filename[4096];
+ off_t target;
+
+ if (!dump_files)
+ return;
+
+ if (sub_entry->header_entry.something1 == 0)
+ return;
+
+ snprintf(filename, sizeof(filename), "part.%d", part++);
+ out_fd = open(filename, O_WRONLY | O_CREAT, 0660);
+ if (out_fd < 0) {
+ perror("Can not dump firmware");
+ return;
+ }
+
+ target = sub_entry->absolute_offset + ntohl(sub_entry->header_entry.start) + 4;
+ if (lseek(fd, target, SEEK_SET) != target) {
+ perror("seek failed");
+ close(out_fd);
+ return;
+ }
+
+ for (copied = 0; copied < ntohl(sub_entry->header_entry.length); ++copied) {
+ char c;
+ if (read(fd, &c, sizeof(c)) != sizeof(c)) {
+ perror("copy failed");
+ break;
+ }
+
+ if (write(out_fd, &c, sizeof(c)) != sizeof(c)) {
+ perror("write failed");
+ break;
+ }
+ }
+
+ close(out_fd);
+}
+
+static void analyze_firmware(const char *filename)
+{
+ struct stat stat;
+ struct sdp_header *header;
+ struct sdp_header_item *sub_entry;
+ struct llist_head *entry;
+ int fd;
+ void *tall_firm_ctx = 0;
+ int part = 0;
+
+ entry = talloc_zero(tall_firm_ctx, struct llist_head);
+ INIT_LLIST_HEAD(entry);
+
+ printf("Opening possible firmware '%s'\n", filename);
+ fd = open(filename, O_RDONLY);
+ if (!fd) {
+ perror("nada");
+ return;
+ }
+
+ /* verify the file */
+ if (fstat(fd, &stat) == -1) {
+ perror("Can not stat the file");
+ close(fd);
+ return;
+ }
+
+ ipaccess_analyze_file(fd, stat.st_size, 0, entry);
+
+ llist_for_each_entry(header, entry, entry) {
+ printf("Printing header information:\n");
+ printf("more_more_magic: 0x%x\n", ntohs(header->firmware_info.more_more_magic));
+ printf("header_length: %u\n", ntohl(header->firmware_info.header_length));
+ printf("file_length: %u\n", ntohl(header->firmware_info.file_length));
+ printf("sw_part: %.20s\n", header->firmware_info.sw_part);
+ printf("text1: %.64s\n", header->firmware_info.text1);
+ printf("time: %.12s\n", header->firmware_info.time);
+ printf("date: %.14s\n", header->firmware_info.date);
+ printf("text2: %.10s\n", header->firmware_info.text2);
+ printf("version: %.20s\n", header->firmware_info.version);
+ printf("subitems...\n");
+
+ llist_for_each_entry(sub_entry, &header->header_list, entry) {
+ printf("\tsomething1: %u\n", sub_entry->header_entry.something1);
+ printf("\ttext1: %.64s\n", sub_entry->header_entry.text1);
+ printf("\ttime: %.12s\n", sub_entry->header_entry.time);
+ printf("\tdate: %.14s\n", sub_entry->header_entry.date);
+ printf("\ttext2: %.10s\n", sub_entry->header_entry.text2);
+ printf("\tversion: %.20s\n", sub_entry->header_entry.version);
+ printf("\tlength: %u\n", ntohl(sub_entry->header_entry.length));
+ printf("\taddr1: 0x%x\n", ntohl(sub_entry->header_entry.addr1));
+ printf("\taddr2: 0x%x\n", ntohl(sub_entry->header_entry.addr2));
+ printf("\tstart: 0x%x\n", ntohl(sub_entry->header_entry.start));
+ printf("\tabs. offset: 0x%lx\n", sub_entry->absolute_offset);
+ printf("\n\n");
+
+ dump_entry(sub_entry, part++, fd);
+ }
+ printf("\n\n");
+ }
+
+ if (close(fd) != 0) {
+ perror("Close failed.\n");
+ return;
+ }
+
+ talloc_free(tall_firm_ctx);
+}
+
+static void print_usage(void)
+{
+ printf("Usage: ipaccess-config IP_OF_BTS\n");
+}
+
+static void print_help(void)
+{
+#if 0
+ printf("Commands for reading from the BTS:\n");
+ printf(" -D --dump\t\t\tDump the BTS configuration\n");
+ printf("\n");
+#endif
+ printf("Commands for writing to the BTS:\n");
+ printf(" -u --unit-id UNIT_ID\t\tSet the Unit ID of the BTS\n");
+ printf(" -o --oml-ip IP\t\tSet primary OML IP (IP of your BSC)\n");
+ printf(" -i --ip-address IP/MASK\tSet static IP address + netmask of BTS\n");
+ printf(" -g --ip-gateway IP\t\tSet static IP gateway of BTS\n");
+ printf(" -r --restart\t\t\tRestart the BTS (after other operations)\n");
+ printf(" -n --nvram-flags FLAGS/MASK\tSet NVRAM attributes\n");
+ printf(" -S --nvattr-set FLAG\tSet one additional NVRAM attribute\n");
+ printf(" -U --nvattr-unset FLAG\tSet one additional NVRAM attribute\n");
+ printf(" -l --listen TESTNR\t\tPerform specified test number\n");
+ printf(" -L --Listen TEST_NAME\t\tPerform specified test\n");
+ printf(" -s --stream-id ID\t\tSet the IPA Stream Identifier for OML\n");
+ printf(" -d --software FIRMWARE\tDownload firmware into BTS\n");
+ printf("\n");
+ printf("Miscellaneous commands:\n");
+ printf(" -h --help\t\t\tthis text\n");
+ printf(" -H --HELP\t\t\tPrint parameter details.\n");
+ printf(" -f --firmware FIRMWARE\tProvide firmware information\n");
+ printf(" -w --write-firmware\t\tThis will dump the firmware parts to the filesystem. Use with -f.\n");
+ printf(" -p --loop\t\t\tLoop the tests executed with the --listen command.\n");
+}
+
+static void print_value_string(const struct value_string *val, int size)
+{
+ int i;
+
+ for (i = 0; i < size - 1; ++i) {
+ char sep = val[i + 1].str == NULL ? '.' : ',';
+ printf("%s%c ", val[i].str, sep);
+ }
+ printf("\n");
+}
+
+static void print_options(void)
+{
+
+ printf("Options for NVRAM (-S,-U):\n ");
+ print_value_string(&ipa_nvflag_strs[0], ARRAY_SIZE(ipa_nvflag_strs));
+
+ printf("Options for Tests (-L):\n ");
+ print_value_string(&ipa_test_strs[0], ARRAY_SIZE(ipa_test_strs));
+}
+
+extern void bts_model_nanobts_init();
+
+int main(int argc, char **argv)
+{
+ struct gsm_bts *bts;
+ struct sockaddr_in sin;
+ int rc, option_index = 0, stream_id = 0xff;
+
+ tall_ctx_config = talloc_named_const(NULL, 0, "ipaccess-config");
+ msgb_talloc_ctx_init(tall_ctx_config, 0);
+
+ osmo_init_logging(&log_info);
+ log_parse_category_mask(osmo_stderr_target, "DNM,0");
+ bts_model_nanobts_init();
+
+ printf("ipaccess-config (C) 2009-2010 by Harald Welte and others\n");
+ printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
+
+ while (1) {
+ int c;
+ unsigned long ul;
+ char *slash;
+ static struct option long_options[] = {
+ { "unit-id", 1, 0, 'u' },
+ { "oml-ip", 1, 0, 'o' },
+ { "ip-address", 1, 0, 'i' },
+ { "ip-gateway", 1, 0, 'g' },
+ { "restart", 0, 0, 'r' },
+ { "nvram-flags", 1, 0, 'n' },
+ { "nvattr-set", 1, 0, 'S' },
+ { "nvattr-unset", 1, 0, 'U' },
+ { "help", 0, 0, 'h' },
+ { "HELP", 0, 0, 'H' },
+ { "listen", 1, 0, 'l' },
+ { "Listen", 1, 0, 'L' },
+ { "stream-id", 1, 0, 's' },
+ { "software", 1, 0, 'd' },
+ { "firmware", 1, 0, 'f' },
+ { "write-firmware", 0, 0, 'w' },
+ { "disable-color", 0, 0, 'c'},
+ { "loop", 0, 0, 'p' },
+ { 0, 0, 0, 0 },
+ };
+
+ c = getopt_long(argc, argv, "u:o:i:g:rn:S:U:l:L:hs:d:f:wcpH", long_options,
+ &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'u':
+ unit_id = optarg;
+ break;
+ case 'o':
+ prim_oml_ip = optarg;
+ break;
+ case 'i':
+ slash = strchr(optarg, '/');
+ if (!slash)
+ exit(2);
+ bts_ip_addr = optarg;
+ *slash = 0;
+ bts_ip_mask = slash+1;
+ break;
+ case 'g':
+ bts_ip_gw = optarg;
+ break;
+ case 'r':
+ restart = 1;
+ break;
+ case 'n':
+ slash = strchr(optarg, '/');
+ if (!slash)
+ exit(2);
+ ul = strtoul(optarg, NULL, 16);
+ nv_flags = ul & 0xffff;
+ ul = strtoul(slash+1, NULL, 16);
+ nv_mask = ul & 0xffff;
+ break;
+ case 'S':
+ if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 1) < 0)
+ exit(2);
+ break;
+ case 'U':
+ if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 0) < 0)
+ exit(2);
+ break;
+ case 'l':
+ net_listen_testnr = atoi(optarg);
+ break;
+ case 'L':
+ net_listen_testnr = get_string_value(ipa_test_strs,
+ optarg);
+ if (net_listen_testnr < 0) {
+ fprintf(stderr,
+ "The test '%s' is not known. Use -H to"
+ " see available tests.\n", optarg);
+ exit(2);
+ }
+ break;
+ case 's':
+ stream_id = atoi(optarg);
+ break;
+ case 'd':
+ software = strdup(optarg);
+ if (find_sw_load_params(optarg) != 0)
+ exit(0);
+ break;
+ case 'f':
+ firmware_analysis = optarg;
+ break;
+ case 'w':
+ dump_files = 1;
+ break;
+ case 'c':
+ log_set_use_color(osmo_stderr_target, 0);
+ break;
+ case 'p':
+ loop_tests = 1;
+ break;
+ case 'h':
+ print_usage();
+ print_help();
+ exit(0);
+ case 'H':
+ print_options();
+ exit(0);
+ }
+ };
+
+ if (firmware_analysis)
+ analyze_firmware(firmware_analysis);
+
+ if (optind >= argc) {
+ /* only warn if we have not done anything else */
+ if (!firmware_analysis)
+ fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n");
+ exit(2);
+ }
+ libosmo_abis_init(tall_ctx_config);
+
+ bsc_gsmnet = bsc_network_init(tall_bsc_ctx, 1, 1, NULL);
+ if (!bsc_gsmnet)
+ exit(1);
+
+ bts = gsm_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_NANOBTS,
+ HARDCODED_BSIC);
+ /* ip.access supports up to 4 chained TRX */
+ gsm_bts_trx_alloc(bts);
+ gsm_bts_trx_alloc(bts);
+ gsm_bts_trx_alloc(bts);
+ bts->oml_tei = stream_id;
+
+ osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL);
+ osmo_signal_register_handler(SS_IPAC_NWL, nwl_sig_cb, NULL);
+
+ ipac_nwl_init();
+
+ printf("Trying to connect to ip.access BTS ...\n");
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ inet_aton(argv[optind], &sin.sin_addr);
+ rc = ia_config_connect(bts, &sin);
+ if (rc < 0) {
+ perror("Error connecting to the BTS");
+ exit(1);
+ }
+
+ bts->oml_link->ts->sign.delay = 10;
+ bts->c0->rsl_link->ts->sign.delay = 10;
+ while (1) {
+ rc = osmo_select_main(0);
+ if (rc < 0)
+ exit(3);
+ }
+
+ exit(0);
+}
+
diff --git a/src/ipaccess/ipaccess-firmware.c b/src/ipaccess/ipaccess-firmware.c
new file mode 100644
index 000000000..5f55bb526
--- /dev/null
+++ b/src/ipaccess/ipaccess-firmware.c
@@ -0,0 +1,135 @@
+/* Routines for parsing an ipacces SDP firmware file */
+
+/* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <openbsc/debug.h>
+#include <openbsc/ipaccess.h>
+#include <osmocom/core/talloc.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define PART_LENGTH 138
+
+osmo_static_assert(sizeof(struct sdp_header_entry) == 138, right_entry);
+osmo_static_assert(sizeof(struct sdp_firmware) == 158, _right_header_length);
+
+/* more magic, the second "int" in the header */
+static char more_magic[] = { 0x10, 0x02 };
+
+int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned int base_offset, struct llist_head *list)
+{
+ struct sdp_firmware *firmware_header = 0;
+ struct sdp_header *header;
+ char buf[4096];
+ int rc, i;
+ uint16_t table_size;
+ uint16_t table_offset;
+ off_t table_start;
+
+
+ rc = read(fd, buf, sizeof(*firmware_header));
+ if (rc < 0) {
+ perror("Can not read header start.");
+ return -1;
+ }
+
+ firmware_header = (struct sdp_firmware *) &buf[0];
+ if (strncmp(firmware_header->magic, " SDP", 4) != 0) {
+ fprintf(stderr, "Wrong magic.\n");
+ return -1;
+ }
+
+ if (memcmp(firmware_header->more_magic, more_magic, 2) != 0) {
+ fprintf(stderr, "Wrong more magic. Got: 0x%x 0x%x vs. 0x%x 0x%x\n",
+ firmware_header->more_magic[0] & 0xff, firmware_header->more_magic[1] & 0xff,
+ more_magic[0], more_magic[1]);
+ return -1;
+ }
+
+
+ if (ntohl(firmware_header->file_length) != st_size) {
+ fprintf(stderr, "The filesize and the header do not match.\n");
+ return -1;
+ }
+
+ /* add the firmware */
+ header = talloc_zero(list, struct sdp_header);
+ header->firmware_info = *firmware_header;
+ INIT_LLIST_HEAD(&header->header_list);
+ llist_add(&header->entry, list);
+
+ table_offset = ntohs(firmware_header->table_offset);
+ table_start = lseek(fd, table_offset, SEEK_CUR);
+ if (table_start == -1) {
+ fprintf(stderr, "Failed to seek to the rel position: 0x%x\n", table_offset);
+ return -1;
+ }
+
+ if (read(fd, &table_size, sizeof(table_size)) != sizeof(table_size)) {
+ fprintf(stderr, "The table size could not be read.\n");
+ return -1;
+ }
+
+ table_size = ntohs(table_size);
+
+ if (table_size % PART_LENGTH != 0) {
+ fprintf(stderr, "The part length seems to be wrong: 0x%x\n", table_size);
+ return -1;
+ }
+
+ /* look into each firmware now */
+ for (i = 0; i < table_size / PART_LENGTH; ++i) {
+ struct sdp_header_entry entry;
+ struct sdp_header_item *header_entry;
+ unsigned int offset = table_start + 2;
+ offset += i * 138;
+
+ if (lseek(fd, offset, SEEK_SET) != offset) {
+ fprintf(stderr, "Can not seek to the offset: %u.\n", offset);
+ return -1;
+ }
+
+ rc = read(fd, &entry, sizeof(entry));
+ if (rc != sizeof(entry)) {
+ fprintf(stderr, "Can not read the header entry.\n");
+ return -1;
+ }
+
+ header_entry = talloc_zero(header, struct sdp_header_item);
+ header_entry->header_entry = entry;
+ header_entry->absolute_offset = base_offset;
+ llist_add(&header_entry->entry, &header->header_list);
+
+ /* now we need to find the SDP file... */
+ offset = ntohl(entry.start) + 4 + base_offset;
+ if (lseek(fd, offset, SEEK_SET) != offset) {
+ perror("can't seek to sdp");
+ return -1;
+ }
+
+
+ ipaccess_analyze_file(fd, ntohl(entry.length), offset, list);
+ }
+
+ return 0;
+}
+
diff --git a/src/ipaccess/ipaccess-proxy.c b/src/ipaccess/ipaccess-proxy.c
new file mode 100644
index 000000000..d3674426c
--- /dev/null
+++ b/src/ipaccess/ipaccess-proxy.c
@@ -0,0 +1,1226 @@
+/* OpenBSC Abis/IP proxy ip.access nanoBTS */
+
+/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2010 by On-Waves
+ * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <openbsc/gsm_data.h>
+#include <osmocom/core/application.h>
+#include <osmocom/core/select.h>
+#include <osmocom/gsm/tlv.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gsm/ipa.h>
+#include <osmocom/abis/ipa.h>
+#include <osmocom/abis/ipaccess.h>
+#include <openbsc/debug.h>
+#include <openbsc/ipaccess.h>
+#include <openbsc/socket.h>
+#include <osmocom/core/talloc.h>
+
+/* one instance of an ip.access protocol proxy */
+struct ipa_proxy {
+ /* socket where we listen for incoming OML from BTS */
+ struct osmo_fd oml_listen_fd;
+ /* socket where we listen for incoming RSL from BTS */
+ struct osmo_fd rsl_listen_fd;
+ /* list of BTS's (struct ipa_bts_conn */
+ struct llist_head bts_list;
+ /* the BSC reconnect timer */
+ struct osmo_timer_list reconn_timer;
+ /* global GPRS NS data */
+ struct in_addr gprs_addr;
+ struct in_addr listen_addr;
+};
+
+/* global pointer to the proxy structure */
+static struct ipa_proxy *ipp;
+
+struct ipa_proxy_conn {
+ struct osmo_fd fd;
+ struct llist_head tx_queue;
+ struct ipa_bts_conn *bts_conn;
+};
+#define MAX_TRX 4
+
+/* represents a particular BTS in our proxy */
+struct ipa_bts_conn {
+ /* list of BTS's (ipa_proxy->bts_list) */
+ struct llist_head list;
+ /* back pointer to the proxy which we belong to */
+ struct ipa_proxy *ipp;
+ /* the unit ID as determined by CCM */
+ struct {
+ uint16_t site_id;
+ uint16_t bts_id;
+ } unit_id;
+
+ /* incoming connections from BTS */
+ struct ipa_proxy_conn *oml_conn;
+ struct ipa_proxy_conn *rsl_conn[MAX_TRX];
+
+ /* outgoing connections to BSC */
+ struct ipa_proxy_conn *bsc_oml_conn;
+ struct ipa_proxy_conn *bsc_rsl_conn[MAX_TRX];
+
+ /* UDP sockets for BTS and BSC injection */
+ struct osmo_fd udp_bts_fd;
+ struct osmo_fd udp_bsc_fd;
+
+ /* NS data */
+ struct in_addr bts_addr;
+ struct osmo_fd gprs_ns_fd;
+ int gprs_local_port;
+ uint16_t gprs_orig_port;
+ uint32_t gprs_orig_ip;
+
+ char *id_tags[256];
+ uint8_t *id_resp;
+ unsigned int id_resp_len;
+};
+
+enum ipp_fd_type {
+ OML_FROM_BTS = 1,
+ RSL_FROM_BTS = 2,
+ OML_TO_BSC = 3,
+ RSL_TO_BSC = 4,
+ UDP_TO_BTS = 5,
+ UDP_TO_BSC = 6,
+};
+
+/* some of the code against we link from OpenBSC needs this */
+void *tall_bsc_ctx;
+
+static char *listen_ipaddr;
+static char *bsc_ipaddr;
+static char *gprs_ns_ipaddr;
+
+static int gprs_ns_cb(struct osmo_fd *bfd, unsigned int what);
+
+#define PROXY_ALLOC_SIZE 1200
+
+static struct ipa_bts_conn *find_bts_by_unitid(struct ipa_proxy *ipp,
+ uint16_t site_id,
+ uint16_t bts_id)
+{
+ struct ipa_bts_conn *ipbc;
+
+ llist_for_each_entry(ipbc, &ipp->bts_list, list) {
+ if (ipbc->unit_id.site_id == site_id &&
+ ipbc->unit_id.bts_id == bts_id)
+ return ipbc;
+ }
+
+ return NULL;
+}
+
+struct ipa_proxy_conn *alloc_conn(void)
+{
+ struct ipa_proxy_conn *ipc;
+
+ ipc = talloc_zero(tall_bsc_ctx, struct ipa_proxy_conn);
+ if (!ipc)
+ return NULL;
+
+ INIT_LLIST_HEAD(&ipc->tx_queue);
+
+ return ipc;
+}
+
+static int store_idtags(struct ipa_bts_conn *ipbc, struct tlv_parsed *tlvp)
+{
+ unsigned int i, len;
+
+ for (i = 0; i <= 0xff; i++) {
+ if (!TLVP_PRESENT(tlvp, i))
+ continue;
+
+ len = TLVP_LEN(tlvp, i);
+#if 0
+ if (!ipbc->id_tags[i])
+ ipbc->id_tags[i] = talloc_size(tall_bsc_ctx, len);
+ else
+#endif
+ ipbc->id_tags[i] = talloc_realloc_size(ipbc,
+ ipbc->id_tags[i], len);
+ if (!ipbc->id_tags[i])
+ return -ENOMEM;
+
+ memset(ipbc->id_tags[i], 0, len);
+ //memcpy(ipbc->id_tags[i], TLVP_VAL(tlvp, i), len);
+ }
+ return 0;
+}
+
+
+static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data);
+
+#define logp_ipbc_uid(ss, lvl, ipbc, trx_id) _logp_ipbc_uid(ss, lvl, __FILE__, __LINE__, ipbc, trx_id)
+
+static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int line,
+ struct ipa_bts_conn *ipbc, uint8_t trx_id)
+{
+ if (ipbc)
+ logp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id,
+ ipbc->unit_id.bts_id, trx_id);
+ else
+ logp2(ss, lvl, file, line, 0, "unknown ");
+}
+
+static int handle_udp_read(struct osmo_fd *bfd)
+{
+ struct ipa_bts_conn *ipbc = bfd->data;
+ struct ipa_proxy_conn *other_conn = NULL;
+ struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP");
+ struct ipaccess_head *hh;
+ int ret;
+
+ /* with UDP sockets, we cannot read partial packets but have to read
+ * all of it in one go */
+ hh = (struct ipaccess_head *) msg->data;
+ ret = recv(bfd->fd, msg->data, msg->data_len, 0);
+ if (ret < 0) {
+ if (errno != EAGAIN)
+ LOGP(DLINP, LOGL_ERROR, "recv error %s\n", strerror(errno));
+ msgb_free(msg);
+ return ret;
+ }
+ if (ret == 0) {
+ DEBUGP(DLINP, "UDP peer disappeared, dead socket\n");
+ osmo_fd_unregister(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+ msgb_free(msg);
+ return -EIO;
+ }
+ if (ret < sizeof(*hh)) {
+ DEBUGP(DLINP, "could not even read header!?!\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+ msgb_put(msg, ret);
+ msg->l2h = msg->data + sizeof(*hh);
+ DEBUGP(DLMI, "UDP RX: %s\n", osmo_hexdump(msg->data, msg->len));
+
+ if (hh->len != msg->len - sizeof(*hh)) {
+ DEBUGP(DLINP, "length (%u/%u) disagrees with header(%u)\n",
+ msg->len, msg->len - 3, hh->len);
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ switch (bfd->priv_nr & 0xff) {
+ case UDP_TO_BTS:
+ /* injection towards BTS */
+ switch (hh->proto) {
+ case IPAC_PROTO_RSL:
+ /* FIXME: what to do about TRX > 0 */
+ other_conn = ipbc->rsl_conn[0];
+ break;
+ default:
+ DEBUGP(DLINP, "Unknown protocol 0x%02x, sending to "
+ "OML FD\n", hh->proto);
+ /* fall through */
+ case IPAC_PROTO_IPACCESS:
+ case IPAC_PROTO_OML:
+ other_conn = ipbc->oml_conn;
+ break;
+ }
+ break;
+ case UDP_TO_BSC:
+ /* injection towards BSC */
+ switch (hh->proto) {
+ case IPAC_PROTO_RSL:
+ /* FIXME: what to do about TRX > 0 */
+ other_conn = ipbc->bsc_rsl_conn[0];
+ break;
+ default:
+ DEBUGP(DLINP, "Unknown protocol 0x%02x, sending to "
+ "OML FD\n", hh->proto);
+ /* fall through */
+ case IPAC_PROTO_IPACCESS:
+ case IPAC_PROTO_OML:
+ other_conn = ipbc->bsc_oml_conn;
+ break;
+ }
+ break;
+ default:
+ DEBUGP(DLINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr);
+ break;
+ }
+
+ if (other_conn) {
+ /* enqueue the message for TX on the respective FD */
+ msgb_enqueue(&other_conn->tx_queue, msg);
+ other_conn->fd.when |= BSC_FD_WRITE;
+ } else
+ msgb_free(msg);
+
+ return 0;
+}
+
+static int handle_udp_write(struct osmo_fd *bfd)
+{
+ /* not implemented yet */
+ bfd->when &= ~BSC_FD_WRITE;
+
+ return -EIO;
+}
+
+/* callback from select.c in case one of the fd's can be read/written */
+static int udp_fd_cb(struct osmo_fd *bfd, unsigned int what)
+{
+ int rc = 0;
+
+ if (what & BSC_FD_READ)
+ rc = handle_udp_read(bfd);
+ if (what & BSC_FD_WRITE)
+ rc = handle_udp_write(bfd);
+
+ return rc;
+}
+
+
+static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct osmo_fd *bfd,
+ uint16_t site_id, uint16_t bts_id,
+ uint16_t trx_id, struct tlv_parsed *tlvp,
+ struct msgb *msg)
+{
+ struct ipa_bts_conn *ipbc;
+ uint16_t udp_port;
+ int ret = 0;
+ struct sockaddr_in sin;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ inet_aton(bsc_ipaddr, &sin.sin_addr);
+
+ DEBUGP(DLINP, "(%u/%u/%u) New BTS connection: ",
+ site_id, bts_id, trx_id);
+
+ /* OML needs to be established before RSL */
+ if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) {
+ DEBUGPC(DLINP, "Not a OML connection ?!?\n");
+ return -EIO;
+ }
+
+ /* allocate new BTS connection data structure */
+ ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn);
+ if (!ipbc) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ DEBUGPC(DLINP, "Created BTS Conn data structure\n");
+ ipbc->ipp = ipp;
+ ipbc->unit_id.site_id = site_id;
+ ipbc->unit_id.bts_id = bts_id;
+ ipbc->oml_conn = ipc;
+ ipc->bts_conn = ipbc;
+
+ /* store the content of the ID TAGS for later reference */
+ store_idtags(ipbc, tlvp);
+ ipbc->id_resp_len = msg->len;
+ ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len);
+ memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len);
+
+ /* Create OML TCP connection towards BSC */
+ sin.sin_port = htons(IPA_TCP_PORT_OML);
+ ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
+ if (!ipbc->bsc_oml_conn) {
+ ret = -EIO;
+ goto err_bsc_conn;
+ }
+
+ DEBUGP(DLINP, "(%u/%u/%u) OML Connected to BSC\n",
+ site_id, bts_id, trx_id);
+
+ /* Create UDP socket for BTS packet injection */
+ udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100);
+ ret = make_sock(&ipbc->udp_bts_fd, IPPROTO_UDP, INADDR_ANY, udp_port,
+ UDP_TO_BTS, udp_fd_cb, ipbc);
+ if (ret < 0)
+ goto err_udp_bts;
+ DEBUGP(DLINP, "(%u/%u/%u) Created UDP socket for injection "
+ "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port);
+
+ /* Create UDP socket for BSC packet injection */
+ udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100);
+ ret = make_sock(&ipbc->udp_bsc_fd, IPPROTO_UDP, INADDR_ANY, udp_port,
+ UDP_TO_BSC, udp_fd_cb, ipbc);
+ if (ret < 0)
+ goto err_udp_bsc;
+ DEBUGP(DLINP, "(%u/%u/%u) Created UDP socket for injection "
+ "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port);
+
+
+ /* GPRS NS related code */
+ if (gprs_ns_ipaddr) {
+ struct sockaddr_in sock;
+ socklen_t len = sizeof(sock);
+ struct in_addr addr;
+ uint32_t ip;
+
+ inet_aton(listen_ipaddr, &addr);
+ ip = ntohl(addr.s_addr); /* make_sock() needs host byte order */
+ ret = make_sock(&ipbc->gprs_ns_fd, IPPROTO_UDP, ip, 0, 0,
+ gprs_ns_cb, ipbc);
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "Creating the GPRS socket failed.\n");
+ goto err_udp_bsc;
+ }
+
+ ret = getsockname(ipbc->gprs_ns_fd.fd, (struct sockaddr* ) &sock, &len);
+ ipbc->gprs_local_port = ntohs(sock.sin_port);
+ LOGP(DLINP, LOGL_NOTICE,
+ "Created GPRS NS Socket. Listening on: %s:%d\n",
+ inet_ntoa(sock.sin_addr), ipbc->gprs_local_port);
+
+ ret = getpeername(bfd->fd, (struct sockaddr* ) &sock, &len);
+ ipbc->bts_addr = sock.sin_addr;
+ }
+
+ llist_add(&ipbc->list, &ipp->bts_list);
+
+ return 0;
+
+err_udp_bsc:
+ osmo_fd_unregister(&ipbc->udp_bts_fd);
+err_udp_bts:
+ osmo_fd_unregister(&ipbc->bsc_oml_conn->fd);
+ close(ipbc->bsc_oml_conn->fd.fd);
+ talloc_free(ipbc->bsc_oml_conn);
+ ipbc->bsc_oml_conn = NULL;
+err_bsc_conn:
+ talloc_free(ipbc->id_resp);
+ talloc_free(ipbc);
+#if 0
+ osmo_fd_unregister(bfd);
+ close(bfd->fd);
+ talloc_free(bfd);
+#endif
+err_out:
+ return ret;
+}
+
+static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg,
+ struct osmo_fd *bfd)
+{
+ struct tlv_parsed tlvp;
+ uint8_t msg_type = *(msg->l2h);
+ struct ipaccess_unit unit_data;
+ struct ipa_bts_conn *ipbc;
+ int ret = 0;
+
+ switch (msg_type) {
+ case IPAC_MSGT_PING:
+ ret = ipa_ccm_send_pong(bfd->fd);
+ break;
+ case IPAC_MSGT_PONG:
+ DEBUGP(DLMI, "PONG!\n");
+ break;
+ case IPAC_MSGT_ID_RESP:
+ DEBUGP(DLMI, "ID_RESP ");
+ /* parse tags, search for Unit ID */
+ ipa_ccm_idtag_parse(&tlvp, (uint8_t *)msg->l2h + 2,
+ msgb_l2len(msg)-2);
+ DEBUGP(DLMI, "\n");
+
+ if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) {
+ LOGP(DLINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n");
+ return -EIO;
+ }
+
+ /* lookup BTS, create sign_link, ... */
+ memset(&unit_data, 0, sizeof(unit_data));
+ ipa_parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT),
+ &unit_data);
+ ipbc = find_bts_by_unitid(ipp, unit_data.site_id, unit_data.bts_id);
+ if (!ipbc) {
+ /* We have not found an ipbc (per-bts proxy instance)
+ * for this BTS yet. The first connection of a new BTS must
+ * be a OML connection. We allocate the associated data structures,
+ * and try to connect to the remote end */
+
+ return ipbc_alloc_connect(ipc, bfd, unit_data.site_id,
+ unit_data.bts_id,
+ unit_data.trx_id, &tlvp, msg);
+ /* if this fails, the caller will clean up bfd */
+ } else {
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ inet_aton(bsc_ipaddr, &sin.sin_addr);
+
+ DEBUGP(DLINP, "Identified BTS %u/%u/%u\n",
+ unit_data.site_id, unit_data.bts_id, unit_data.trx_id);
+
+ if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) {
+ LOGP(DLINP, LOGL_ERROR, "Second OML connection from "
+ "same BTS ?!?\n");
+ return 0;
+ }
+
+ if (unit_data.trx_id >= MAX_TRX) {
+ LOGP(DLINP, LOGL_ERROR, "We don't support more "
+ "than %u TRX\n", MAX_TRX);
+ return -EINVAL;
+ }
+
+ ipc->bts_conn = ipbc;
+ /* store TRX number in higher 8 bit of the bfd private number */
+ bfd->priv_nr |= unit_data.trx_id << 8;
+ ipbc->rsl_conn[unit_data.trx_id] = ipc;
+
+ /* Create RSL TCP connection towards BSC */
+ sin.sin_port = htons(IPA_TCP_PORT_RSL);
+ ipbc->bsc_rsl_conn[unit_data.trx_id] =
+ connect_bsc(&sin, RSL_TO_BSC | (unit_data.trx_id << 8), ipbc);
+ if (!ipbc->bsc_oml_conn)
+ return -EIO;
+ DEBUGP(DLINP, "(%u/%u/%u) Connected RSL to BSC\n",
+ unit_data.site_id, unit_data.bts_id, unit_data.trx_id);
+ }
+ break;
+ case IPAC_MSGT_ID_GET:
+ DEBUGP(DLMI, "ID_GET\n");
+ if ((bfd->priv_nr & 0xff) != OML_TO_BSC &&
+ (bfd->priv_nr & 0xff) != RSL_TO_BSC) {
+ DEBUGP(DLINP, "IDentity REQuest from BTS ?!?\n");
+ return -EIO;
+ }
+ ipbc = ipc->bts_conn;
+ if (!ipbc) {
+ DEBUGP(DLINP, "ID_GET from BSC before we have ID_RESP from BTS\n");
+ return -EIO;
+ }
+ ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len);
+ if (ret != ipbc->id_resp_len) {
+ LOGP(DLINP, LOGL_ERROR, "Partial write: %d of %d\n",
+ ret, ipbc->id_resp_len);
+ return -EIO;
+ }
+ ret = 0;
+ break;
+ case IPAC_MSGT_ID_ACK:
+ DEBUGP(DLMI, "ID_ACK? -> ACK!\n");
+ ret = ipa_ccm_send_id_ack(bfd->fd);
+ break;
+ default:
+ LOGP(DLMI, LOGL_ERROR, "Unhandled IPA type; %d\n", msg_type);
+ return 1;
+ break;
+ }
+ return ret;
+}
+
+struct msgb *ipaccess_proxy_read_msg(struct osmo_fd *bfd, int *error)
+{
+ struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP");
+ struct ipaccess_head *hh;
+ int len, ret = 0;
+
+ if (!msg) {
+ *error = -ENOMEM;
+ return NULL;
+ }
+
+ /* first read our 3-byte header */
+ hh = (struct ipaccess_head *) msg->data;
+ ret = recv(bfd->fd, msg->data, 3, 0);
+ if (ret < 0) {
+ if (errno != EAGAIN)
+ LOGP(DLINP, LOGL_ERROR, "recv error: %s\n", strerror(errno));
+ msgb_free(msg);
+ *error = ret;
+ return NULL;
+ } else if (ret == 0) {
+ msgb_free(msg);
+ *error = ret;
+ return NULL;
+ }
+
+ msgb_put(msg, ret);
+
+ /* then read te length as specified in header */
+ msg->l2h = msg->data + sizeof(*hh);
+ len = ntohs(hh->len);
+ ret = recv(bfd->fd, msg->l2h, len, 0);
+ if (ret < len) {
+ LOGP(DLINP, LOGL_ERROR, "short read!\n");
+ msgb_free(msg);
+ *error = -EIO;
+ return NULL;
+ }
+ msgb_put(msg, ret);
+
+ return msg;
+}
+
+static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc,
+ unsigned int priv_nr)
+{
+ struct ipa_proxy_conn *bsc_conn;
+ unsigned int trx_id = priv_nr >> 8;
+
+ switch (priv_nr & 0xff) {
+ case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
+ bsc_conn = ipbc->bsc_oml_conn;
+ break;
+ case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
+ bsc_conn = ipbc->bsc_rsl_conn[trx_id];
+ break;
+ case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
+ bsc_conn = ipbc->oml_conn;
+ break;
+ case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
+ bsc_conn = ipbc->rsl_conn[trx_id];
+ break;
+ default:
+ bsc_conn = NULL;
+ break;
+ }
+ return bsc_conn;
+}
+
+static void reconn_tmr_cb(void *data)
+{
+ struct ipa_proxy *ipp = data;
+ struct ipa_bts_conn *ipbc;
+ struct sockaddr_in sin;
+ int i;
+
+ DEBUGP(DLINP, "Running reconnect timer\n");
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ inet_aton(bsc_ipaddr, &sin.sin_addr);
+
+ llist_for_each_entry(ipbc, &ipp->bts_list, list) {
+ /* if OML to BSC is dead, try to restore it */
+ if (ipbc->oml_conn && !ipbc->bsc_oml_conn) {
+ sin.sin_port = htons(IPA_TCP_PORT_OML);
+ logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0);
+ LOGPC(DLINP, LOGL_NOTICE, "OML Trying to reconnect\n");
+ ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc);
+ if (!ipbc->bsc_oml_conn)
+ goto reschedule;
+ logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0);
+ LOGPC(DLINP, LOGL_NOTICE, "OML Reconnected\n");
+ }
+ /* if we (still) don't have a OML connection, skip RSL */
+ if (!ipbc->oml_conn || !ipbc->bsc_oml_conn)
+ continue;
+
+ for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) {
+ unsigned int priv_nr;
+ /* don't establish RSL links which we don't have */
+ if (!ipbc->rsl_conn[i])
+ continue;
+ if (ipbc->bsc_rsl_conn[i])
+ continue;
+ priv_nr = ipbc->rsl_conn[i]->fd.priv_nr;
+ priv_nr &= ~0xff;
+ priv_nr |= RSL_TO_BSC;
+ sin.sin_port = htons(IPA_TCP_PORT_RSL);
+ logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
+ LOGPC(DLINP, LOGL_NOTICE, "RSL Trying to reconnect\n");
+ ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc);
+ if (!ipbc->bsc_rsl_conn[i])
+ goto reschedule;
+ logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8);
+ LOGPC(DLINP, LOGL_NOTICE, "RSL Reconnected\n");
+ }
+ }
+ return;
+
+reschedule:
+ osmo_timer_schedule(&ipp->reconn_timer, 5, 0);
+}
+
+static void handle_dead_socket(struct osmo_fd *bfd)
+{
+ struct ipa_proxy_conn *ipc = bfd->data; /* local conn */
+ struct ipa_proxy_conn *bsc_conn; /* remote conn */
+ struct ipa_bts_conn *ipbc = ipc->bts_conn;
+ unsigned int trx_id = bfd->priv_nr >> 8;
+ struct msgb *msg, *msg2;
+
+ osmo_fd_unregister(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+
+ /* FIXME: clear tx_queue, remove all references, etc. */
+ llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list)
+ msgb_free(msg);
+
+ switch (bfd->priv_nr & 0xff) {
+ case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */
+ /* The BTS started a connection with us but we got no
+ * IPAC_MSGT_ID_RESP message yet, in that scenario we did not
+ * allocate the ipa_bts_conn structure. */
+ if (ipbc == NULL)
+ break;
+ ipbc->oml_conn = NULL;
+ bsc_conn = ipbc->bsc_oml_conn;
+ /* close the connection to the BSC */
+ osmo_fd_unregister(&bsc_conn->fd);
+ close(bsc_conn->fd.fd);
+ llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
+ msgb_free(msg);
+ talloc_free(bsc_conn);
+ ipbc->bsc_oml_conn = NULL;
+ /* FIXME: do we need to delete the entire ipbc ? */
+ break;
+ case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */
+ ipbc->rsl_conn[trx_id] = NULL;
+ bsc_conn = ipbc->bsc_rsl_conn[trx_id];
+ /* close the connection to the BSC */
+ osmo_fd_unregister(&bsc_conn->fd);
+ close(bsc_conn->fd.fd);
+ llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list)
+ msgb_free(msg);
+ talloc_free(bsc_conn);
+ ipbc->bsc_rsl_conn[trx_id] = NULL;
+ break;
+ case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */
+ ipbc->bsc_oml_conn = NULL;
+ bsc_conn = ipbc->oml_conn;
+ /* start reconnect timer */
+ osmo_timer_schedule(&ipp->reconn_timer, 5, 0);
+ break;
+ case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */
+ ipbc->bsc_rsl_conn[trx_id] = NULL;
+ bsc_conn = ipbc->rsl_conn[trx_id];
+ /* start reconnect timer */
+ osmo_timer_schedule(&ipp->reconn_timer, 5, 0);
+ break;
+ default:
+ bsc_conn = NULL;
+ break;
+ }
+
+ talloc_free(ipc);
+}
+
+static void patch_gprs_msg(struct ipa_bts_conn *ipbc, int priv_nr, struct msgb *msg)
+{
+ uint8_t *nsvci;
+
+ if ((priv_nr & 0xff) != OML_FROM_BTS && (priv_nr & 0xff) != OML_TO_BSC)
+ return;
+
+ if (msgb_l2len(msg) != 39)
+ return;
+
+ /*
+ * Check if this is a IPA Set Attribute or IPA Set Attribute ACK
+ * and if the FOM Class is GPRS NSVC0 and then we will patch it.
+ *
+ * The patch assumes the message looks like the one from the trace
+ * but we only match messages with a specific size anyway... So
+ * this hack should work just fine.
+ */
+
+ if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 &&
+ msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 &&
+ msg->l2h[18] == 0xf5 && msg->l2h[19] == 0xf2) {
+ nsvci = &msg->l2h[23];
+ ipbc->gprs_orig_port = *(uint16_t *)(nsvci+8);
+ ipbc->gprs_orig_ip = *(uint32_t *)(nsvci+10);
+ *(uint16_t *)(nsvci+8) = htons(ipbc->gprs_local_port);
+ *(uint32_t *)(nsvci+10) = ipbc->ipp->listen_addr.s_addr;
+ } else if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 &&
+ msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 &&
+ msg->l2h[18] == 0xf6 && msg->l2h[19] == 0xf2) {
+ nsvci = &msg->l2h[23];
+ *(uint16_t *)(nsvci+8) = ipbc->gprs_orig_port;
+ *(uint32_t *)(nsvci+10) = ipbc->gprs_orig_ip;
+ }
+}
+
+static int handle_tcp_read(struct osmo_fd *bfd)
+{
+ struct ipa_proxy_conn *ipc = bfd->data;
+ struct ipa_bts_conn *ipbc = ipc->bts_conn;
+ struct ipa_proxy_conn *bsc_conn;
+ struct msgb *msg;
+ struct ipaccess_head *hh;
+ int ret = 0;
+ char *btsbsc;
+
+ if ((bfd->priv_nr & 0xff) <= 2)
+ btsbsc = "BTS";
+ else
+ btsbsc = "BSC";
+
+ msg = ipaccess_proxy_read_msg(bfd, &ret);
+ if (!msg) {
+ if (ret == 0) {
+ logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
+ LOGPC(DLINP, LOGL_NOTICE, "%s disappeared, "
+ "dead socket\n", btsbsc);
+ handle_dead_socket(bfd);
+ }
+ return ret;
+ }
+
+ msgb_put(msg, ret);
+ logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
+ DEBUGPC(DLMI, "RX<-%s: %s\n", btsbsc, osmo_hexdump(msg->data, msg->len));
+
+ hh = (struct ipaccess_head *) msg->data;
+ if (hh->proto == IPAC_PROTO_IPACCESS) {
+ ret = ipaccess_rcvmsg(ipc, msg, bfd);
+ if (ret < 0) {
+ osmo_fd_unregister(bfd);
+ close(bfd->fd);
+ bfd->fd = -1;
+ talloc_free(bfd);
+ msgb_free(msg);
+ return ret;
+ } else if (ret == 0) {
+ /* we do not forward parts of the CCM protocol
+ * through the proxy but rather terminate it ourselves. */
+ msgb_free(msg);
+ return ret;
+ }
+ }
+
+ if (!ipbc) {
+ LOGP(DLINP, LOGL_ERROR,
+ "received %s packet but no ipc->bts_conn?!?\n", btsbsc);
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr);
+ if (bsc_conn) {
+ if (gprs_ns_ipaddr)
+ patch_gprs_msg(ipbc, bfd->priv_nr, msg);
+ /* enqueue packet towards BSC */
+ msgb_enqueue(&bsc_conn->tx_queue, msg);
+ /* mark respective filedescriptor as 'we want to write' */
+ bsc_conn->fd.when |= BSC_FD_WRITE;
+ } else {
+ logp_ipbc_uid(DLINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8);
+ LOGPC(DLINP, LOGL_INFO, "Dropping packet from %s, "
+ "since remote connection is dead\n", btsbsc);
+ msgb_free(msg);
+ }
+
+ return ret;
+}
+
+/* a TCP socket is ready to be written to */
+static int handle_tcp_write(struct osmo_fd *bfd)
+{
+ struct ipa_proxy_conn *ipc = bfd->data;
+ struct ipa_bts_conn *ipbc = ipc->bts_conn;
+ struct llist_head *lh;
+ struct msgb *msg;
+ char *btsbsc;
+ int ret;
+
+ if ((bfd->priv_nr & 0xff) <= 2)
+ btsbsc = "BTS";
+ else
+ btsbsc = "BSC";
+
+
+ /* get the next msg for this timeslot */
+ if (llist_empty(&ipc->tx_queue)) {
+ bfd->when &= ~BSC_FD_WRITE;
+ return 0;
+ }
+ lh = ipc->tx_queue.next;
+ llist_del(lh);
+ msg = llist_entry(lh, struct msgb, list);
+
+ logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8);
+ DEBUGPC(DLMI, "TX %04x: %s\n", bfd->priv_nr,
+ osmo_hexdump(msg->data, msg->len));
+
+ ret = send(bfd->fd, msg->data, msg->len, 0);
+ msgb_free(msg);
+
+ if (ret == 0) {
+ logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8);
+ LOGP(DLINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc);
+ handle_dead_socket(bfd);
+ }
+
+ return ret;
+}
+
+/* callback from select.c in case one of the fd's can be read/written */
+static int proxy_ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what)
+{
+ int rc = 0;
+
+ if (what & BSC_FD_READ) {
+ rc = handle_tcp_read(bfd);
+ if (rc < 0)
+ return rc;
+ }
+ if (what & BSC_FD_WRITE)
+ rc = handle_tcp_write(bfd);
+
+ return rc;
+}
+
+/* callback of the listening filedescriptor */
+static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what)
+{
+ int ret;
+ struct ipa_proxy_conn *ipc;
+ struct osmo_fd *bfd;
+ struct sockaddr_in sa;
+ socklen_t sa_len = sizeof(sa);
+
+ if (!(what & BSC_FD_READ))
+ return 0;
+
+ ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
+ if (ret < 0) {
+ perror("accept");
+ return ret;
+ }
+ DEBUGP(DLINP, "accept()ed new %s link from %s\n",
+ (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL",
+ inet_ntoa(sa.sin_addr));
+
+ ipc = alloc_conn();
+ if (!ipc) {
+ close(ret);
+ return -ENOMEM;
+ }
+
+ bfd = &ipc->fd;
+ bfd->fd = ret;
+ bfd->data = ipc;
+ bfd->priv_nr = listen_bfd->priv_nr;
+ bfd->cb = proxy_ipaccess_fd_cb;
+ bfd->when = BSC_FD_READ;
+ ret = osmo_fd_register(bfd);
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "could not register FD\n");
+ close(bfd->fd);
+ talloc_free(ipc);
+ return ret;
+ }
+
+ /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
+ ret = ipa_ccm_send_id_req(bfd->fd);
+
+ return 0;
+}
+
+static void send_ns(int fd, const char *buf, int size, struct in_addr ip, int port)
+{
+ int ret;
+ struct sockaddr_in addr;
+ socklen_t len = sizeof(addr);
+ memset(&addr, 0, sizeof(addr));
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr = ip;
+
+ ret = sendto(fd, buf, size, 0, (struct sockaddr *) &addr, len);
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "Failed to forward GPRS message.\n");
+ }
+}
+
+static int gprs_ns_cb(struct osmo_fd *bfd, unsigned int what)
+{
+ struct ipa_bts_conn *bts;
+ char buf[4096];
+ int ret;
+ struct sockaddr_in sock;
+ socklen_t len = sizeof(sock);
+
+ /* 1. get the data... */
+ ret = recvfrom(bfd->fd, buf, sizeof(buf), 0, (struct sockaddr *) &sock, &len);
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "Failed to recv GPRS NS msg: %s.\n", strerror(errno));
+ return -1;
+ }
+
+ bts = bfd->data;
+
+ /* 2. figure out where to send it to */
+ if (memcmp(&sock.sin_addr, &ipp->gprs_addr, sizeof(sock.sin_addr)) == 0) {
+ LOGP(DLINP, LOGL_DEBUG, "GPRS NS msg from network.\n");
+ send_ns(bfd->fd, buf, ret, bts->bts_addr, 23000);
+ } else if (memcmp(&sock.sin_addr, &bts->bts_addr, sizeof(sock.sin_addr)) == 0) {
+ LOGP(DLINP, LOGL_DEBUG, "GPRS NS msg from BTS.\n");
+ send_ns(bfd->fd, buf, ret, ipp->gprs_addr, 23000);
+ } else {
+ LOGP(DLINP, LOGL_ERROR, "Unknown GPRS source: %s\n", inet_ntoa(sock.sin_addr));
+ }
+
+ return 0;
+}
+
+/* Actively connect to a BSC. */
+static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data)
+{
+ struct ipa_proxy_conn *ipc;
+ struct osmo_fd *bfd;
+ int ret, on = 1;
+
+ ipc = alloc_conn();
+ if (!ipc)
+ return NULL;
+
+ ipc->bts_conn = data;
+
+ bfd = &ipc->fd;
+ bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ bfd->cb = ipaccess_fd_cb;
+ bfd->when = BSC_FD_READ | BSC_FD_WRITE;
+ bfd->data = ipc;
+ bfd->priv_nr = priv_nr;
+
+ if (bfd->fd < 0) {
+ LOGP(DLINP, LOGL_ERROR, "Could not create socket: %s\n",
+ strerror(errno));
+ talloc_free(ipc);
+ return NULL;
+ }
+
+ ret = setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "Could not set socket option\n");
+ close(bfd->fd);
+ talloc_free(ipc);
+ return NULL;
+ }
+
+ ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "Could not connect socket: %s\n",
+ inet_ntoa(sa->sin_addr));
+ close(bfd->fd);
+ talloc_free(ipc);
+ return NULL;
+ }
+
+ /* pre-fill tx_queue with identity request */
+ ret = osmo_fd_register(bfd);
+ if (ret < 0) {
+ close(bfd->fd);
+ talloc_free(ipc);
+ return NULL;
+ }
+
+ return ipc;
+}
+
+static int ipaccess_proxy_setup(void)
+{
+ int ret;
+
+ ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy);
+ if (!ipp)
+ return -ENOMEM;
+ INIT_LLIST_HEAD(&ipp->bts_list);
+ osmo_timer_setup(&ipp->reconn_timer, reconn_tmr_cb, ipp);
+
+ /* Listen for OML connections */
+ ret = make_sock(&ipp->oml_listen_fd, IPPROTO_TCP, INADDR_ANY,
+ IPA_TCP_PORT_OML, OML_FROM_BTS, listen_fd_cb, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* Listen for RSL connections */
+ ret = make_sock(&ipp->rsl_listen_fd, IPPROTO_TCP, INADDR_ANY,
+ IPA_TCP_PORT_RSL, RSL_FROM_BTS, listen_fd_cb, NULL);
+
+ if (ret < 0)
+ return ret;
+
+ /* Connect the GPRS NS Socket */
+ if (gprs_ns_ipaddr) {
+ inet_aton(gprs_ns_ipaddr, &ipp->gprs_addr);
+ inet_aton(listen_ipaddr, &ipp->listen_addr);
+ }
+
+ return ret;
+}
+
+static void signal_handler(int signal)
+{
+ fprintf(stdout, "signal %u received\n", signal);
+
+ switch (signal) {
+ case SIGABRT:
+ /* in case of abort, we want to obtain a talloc report
+ * and then return to the caller, who will abort the process */
+ case SIGUSR1:
+ talloc_report_full(tall_bsc_ctx, stderr);
+ break;
+ default:
+ break;
+ }
+}
+
+static void print_help(void)
+{
+ printf(" ipaccess-proxy is a proxy BTS.\n");
+ printf(" -h --help. This help text.\n");
+ printf(" -l --listen IP. The ip to listen to.\n");
+ printf(" -b --bsc IP. The BSC IP address.\n");
+ printf(" -g --gprs IP. Take GPRS NS from that IP.\n");
+ printf("\n");
+ printf(" -s --disable-color. Disable the color inside the logging message.\n");
+ printf(" -e --log-level number. Set the global loglevel.\n");
+ printf(" -T --timestamp. Prefix every log message with a timestamp.\n");
+ printf(" -V --version. Print the version of OpenBSC.\n");
+}
+
+static void print_usage(void)
+{
+ printf("Usage: ipaccess-proxy [options]\n");
+}
+
+enum {
+ IPA_PROXY_OPT_LISTEN_NONE = 0,
+ IPA_PROXY_OPT_LISTEN_IP = (1 << 0),
+ IPA_PROXY_OPT_BSC_IP = (1 << 1),
+};
+
+static void handle_options(int argc, char** argv)
+{
+ int options_mask = 0;
+
+ /* disable explicit missing arguments error output from getopt_long */
+ opterr = 0;
+
+ while (1) {
+ int option_index = 0, c;
+ static struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"disable-color", 0, 0, 's'},
+ {"timestamp", 0, 0, 'T'},
+ {"log-level", 1, 0, 'e'},
+ {"listen", 1, 0, 'l'},
+ {"bsc", 1, 0, 'b'},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "hsTe:l:b:g:",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_usage();
+ print_help();
+ exit(0);
+ case 'l':
+ listen_ipaddr = optarg;
+ options_mask |= IPA_PROXY_OPT_LISTEN_IP;
+ break;
+ case 'b':
+ bsc_ipaddr = optarg;
+ options_mask |= IPA_PROXY_OPT_BSC_IP;
+ break;
+ case 'g':
+ gprs_ns_ipaddr = optarg;
+ break;
+ case 's':
+ log_set_use_color(osmo_stderr_target, 0);
+ break;
+ case 'T':
+ log_set_print_timestamp(osmo_stderr_target, 1);
+ break;
+ case 'e':
+ log_set_log_level(osmo_stderr_target, atoi(optarg));
+ break;
+ case '?':
+ if (optopt) {
+ printf("ERROR: missing mandatory argument "
+ "for `%s' option\n", argv[optind-1]);
+ } else {
+ printf("ERROR: unknown option `%s'\n",
+ argv[optind-1]);
+ }
+ print_usage();
+ print_help();
+ exit(EXIT_FAILURE);
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+ }
+ if ((options_mask & (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP))
+ != (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP)) {
+ printf("ERROR: You have to specify `--listen' and `--bsc' "
+ "options at least.\n");
+ print_usage();
+ print_help();
+ exit(EXIT_FAILURE);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int rc;
+
+ tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy");
+ msgb_talloc_ctx_init(tall_bsc_ctx, 0);
+
+ osmo_init_logging(&log_info);
+ log_parse_category_mask(osmo_stderr_target, "DLINP:DLMI");
+
+ handle_options(argc, argv);
+
+ rc = ipaccess_proxy_setup();
+ if (rc < 0)
+ exit(1);
+
+ signal(SIGUSR1, &signal_handler);
+ signal(SIGABRT, &signal_handler);
+ osmo_init_ignore_signals();
+
+ while (1) {
+ osmo_select_main(0);
+ }
+}
diff --git a/src/ipaccess/network_listen.c b/src/ipaccess/network_listen.c
new file mode 100644
index 000000000..3b44ceb74
--- /dev/null
+++ b/src/ipaccess/network_listen.c
@@ -0,0 +1,257 @@
+/* ip.access nanoBTS network listen mode */
+
+/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <arpa/inet.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/gsm/rxlev_stat.h>
+#include <osmocom/gsm/gsm48_ie.h>
+
+#include <openbsc/gsm_data.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/signal.h>
+#include <openbsc/debug.h>
+#include <osmocom/abis/e1_input.h>
+
+#define WHITELIST_MAX_SIZE ((NUM_ARFCNS*2)+2+1)
+
+int ipac_rxlevstat2whitelist(uint16_t *buf, const struct rxlev_stats *st, uint8_t min_rxlev,
+ uint16_t max_num_arfcns)
+{
+ int i;
+ unsigned int num_arfcn = 0;
+
+ for (i = NUM_RXLEVS-1; i >= min_rxlev; i--) {
+ int16_t arfcn = -1;
+
+ while ((arfcn = rxlev_stat_get_next(st, i, arfcn)) >= 0) {
+ *buf++ = htons(arfcn);
+ num_arfcn++;
+
+ }
+
+ if (num_arfcn > max_num_arfcns)
+ break;
+ }
+
+ return num_arfcn;
+}
+
+enum ipac_test_state {
+ IPAC_TEST_S_IDLE,
+ IPAC_TEST_S_RQD,
+ IPAC_TEST_S_EXEC,
+ IPAC_TEST_S_PARTIAL,
+};
+
+int ipac_nwl_test_start(struct gsm_bts_trx *trx, uint8_t testnr,
+ const uint8_t *phys_conf, unsigned int phys_conf_len)
+{
+ struct msgb *msg;
+
+ if (trx->ipaccess.test_state != IPAC_TEST_S_IDLE) {
+ fprintf(stderr, "Cannot start test in state %u\n", trx->ipaccess.test_state);
+ return -EINVAL;
+ }
+
+ switch (testnr) {
+ case NM_IPACC_TESTNO_CHAN_USAGE:
+ case NM_IPACC_TESTNO_BCCH_CHAN_USAGE:
+ rxlev_stat_reset(&trx->ipaccess.rxlev_stat);
+ break;
+ }
+
+ msg = msgb_alloc_headroom(phys_conf_len+256, 128, "OML");
+
+ if (phys_conf && phys_conf_len) {
+ uint8_t *payload;
+ /* first put the phys conf header */
+ msgb_tv16_put(msg, NM_ATT_PHYS_CONF, phys_conf_len);
+ payload = msgb_put(msg, phys_conf_len);
+ memcpy(payload, phys_conf, phys_conf_len);
+ }
+
+ abis_nm_perform_test(trx->bts, NM_OC_RADIO_CARRIER, 0, trx->nr, 0xff,
+ testnr, 1, msg);
+ trx->ipaccess.test_nr = testnr;
+
+ /* FIXME: start safety timer until when test is supposed to complete */
+
+ return 0;
+}
+
+static uint16_t last_arfcn;
+static struct gsm_sysinfo_freq nwl_si_freq[1024];
+#define FREQ_TYPE_NCELL_2 0x04 /* sub channel of SI 2 */
+#define FREQ_TYPE_NCELL_2bis 0x08 /* sub channel of SI 2bis */
+#define FREQ_TYPE_NCELL_2ter 0x10 /* sub channel of SI 2ter */
+
+struct ipacc_ferr_elem {
+ int16_t freq_err;
+ uint8_t freq_qual;
+ uint8_t arfcn;
+} __attribute__((packed));
+
+struct ipacc_cusage_elem {
+ uint16_t arfcn:10,
+ rxlev:6;
+} __attribute__ ((packed));
+
+static int test_rep(void *_msg)
+{
+ struct msgb *msg = _msg;
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ uint16_t test_rep_len, ferr_list_len;
+ struct ipacc_ferr_elem *ife;
+ struct ipac_bcch_info binfo;
+ struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst;
+ int i, rc;
+
+ DEBUGP(DNM, "TEST REPORT: ");
+
+ if (foh->data[0] != NM_ATT_TEST_NO ||
+ foh->data[2] != NM_ATT_TEST_REPORT)
+ return -EINVAL;
+
+ DEBUGPC(DNM, "test_no=0x%02x ", foh->data[1]);
+ /* data[2] == NM_ATT_TEST_REPORT */
+ /* data[3..4]: test_rep_len */
+ memcpy(&test_rep_len, &foh->data[3], sizeof(uint16_t));
+ test_rep_len = ntohs(test_rep_len);
+ /* data[5]: ip.access test result */
+ DEBUGPC(DNM, "tst_res=%s\n", ipacc_testres_name(foh->data[5]));
+
+ /* data[6]: ip.access nested IE. 3 == freq_err_list */
+ switch (foh->data[6]) {
+ case NM_IPAC_EIE_FREQ_ERR_LIST:
+ /* data[7..8]: length of ferr_list */
+ memcpy(&ferr_list_len, &foh->data[7], sizeof(uint16_t));
+ ferr_list_len = ntohs(ferr_list_len);
+
+ /* data[9...]: frequency error list elements */
+ for (i = 0; i < ferr_list_len; i+= sizeof(*ife)) {
+ ife = (struct ipacc_ferr_elem *) (foh->data + 9 + i);
+ DEBUGP(DNM, "==> ARFCN %4u, Frequency Error %6hd\n",
+ ife->arfcn, ntohs(ife->freq_err));
+ }
+ break;
+ case NM_IPAC_EIE_CHAN_USE_LIST:
+ /* data[7..8]: length of ferr_list */
+ memcpy(&ferr_list_len, &foh->data[7], sizeof(uint16_t));
+ ferr_list_len = ntohs(ferr_list_len);
+
+ /* data[9...]: channel usage list elements */
+ for (i = 0; i < ferr_list_len; i+= 2) {
+ uint16_t *cu_ptr = (uint16_t *)(foh->data + 9 + i);
+ uint16_t cu = ntohs(*cu_ptr);
+ uint16_t arfcn = cu & 0x3ff;
+ uint8_t rxlev = cu >> 10;
+ DEBUGP(DNM, "==> ARFCN %4u, RxLev %2u\n", arfcn, rxlev);
+ rxlev_stat_input(&sign_link->trx->ipaccess.rxlev_stat,
+ arfcn, rxlev);
+ }
+ break;
+ case NM_IPAC_EIE_BCCH_INFO_TYPE:
+ break;
+ case NM_IPAC_EIE_BCCH_INFO:
+ rc = ipac_parse_bcch_info(&binfo, foh->data+6);
+ if (rc < 0) {
+ DEBUGP(DNM, "BCCH Info parsing failed\n");
+ break;
+ }
+ DEBUGP(DNM, "==> ARFCN %u, RxLev %2u, RxQual %2u: %3d-%d, LAC %d CI %d BSIC %u\n",
+ binfo.arfcn, binfo.rx_lev, binfo.rx_qual,
+ binfo.cgi.mcc, binfo.cgi.mnc,
+ binfo.cgi.lac, binfo.cgi.ci, binfo.bsic);
+
+ if (binfo.arfcn != last_arfcn) {
+ /* report is on a new arfcn, need to clear channel list */
+ memset(nwl_si_freq, 0, sizeof(nwl_si_freq));
+ last_arfcn = binfo.arfcn;
+ }
+ if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2) {
+ DEBUGP(DNM, "BA SI2: %s\n", osmo_hexdump(binfo.ba_list_si2, sizeof(binfo.ba_list_si2)));
+ gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2, sizeof(binfo.ba_list_si2),
+ 0x8c, FREQ_TYPE_NCELL_2);
+ }
+ if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2bis) {
+ DEBUGP(DNM, "BA SI2bis: %s\n", osmo_hexdump(binfo.ba_list_si2bis, sizeof(binfo.ba_list_si2bis)));
+ gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2bis, sizeof(binfo.ba_list_si2bis),
+ 0x8e, FREQ_TYPE_NCELL_2bis);
+ }
+ if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2ter) {
+ DEBUGP(DNM, "BA SI2ter: %s\n", osmo_hexdump(binfo.ba_list_si2ter, sizeof(binfo.ba_list_si2ter)));
+ gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2ter, sizeof(binfo.ba_list_si2ter),
+ 0x8e, FREQ_TYPE_NCELL_2ter);
+ }
+ for (i = 0; i < ARRAY_SIZE(nwl_si_freq); i++) {
+ if (nwl_si_freq[i].mask)
+ DEBUGP(DNM, "Neighbor Cell on ARFCN %u\n", i);
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch (foh->data[5]) {
+ case NM_IPACC_TESTRES_SUCCESS:
+ case NM_IPACC_TESTRES_STOPPED:
+ case NM_IPACC_TESTRES_TIMEOUT:
+ case NM_IPACC_TESTRES_NO_CHANS:
+ sign_link->trx->ipaccess.test_state = IPAC_TEST_S_IDLE;
+ /* Send signal to notify higher layers of test completion */
+ DEBUGP(DNM, "dispatching S_IPAC_NWL_COMPLETE signal\n");
+ osmo_signal_dispatch(SS_IPAC_NWL, S_IPAC_NWL_COMPLETE,
+ sign_link->trx);
+ break;
+ case NM_IPACC_TESTRES_PARTIAL:
+ sign_link->trx->ipaccess.test_state = IPAC_TEST_S_PARTIAL;
+ break;
+ }
+
+ return 0;
+}
+
+static int nwl_sig_cb(unsigned int subsys, unsigned int signal,
+ void *handler_data, void *signal_data)
+{
+ switch (signal) {
+ case S_NM_TEST_REP:
+ return test_rep(signal_data);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void ipac_nwl_init(void)
+{
+ osmo_signal_register_handler(SS_NM, nwl_sig_cb, NULL);
+}