From 218e4b4aa0fc6de842ff820dec8e97d1f083268a Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Tue, 4 Jul 2017 23:08:44 +0200 Subject: 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 --- src/utils/Makefile.am | 147 ++++++++ src/utils/bs11_config.c | 953 +++++++++++++++++++++++++++++++++++++++++++++++ src/utils/isdnsync.c | 189 ++++++++++ src/utils/meas_db.c | 330 ++++++++++++++++ src/utils/meas_db.h | 17 + src/utils/meas_json.c | 190 ++++++++++ src/utils/meas_pcap2db.c | 138 +++++++ src/utils/meas_udp2db.c | 126 +++++++ src/utils/meas_vis.c | 310 +++++++++++++++ src/utils/smpp_mirror.c | 329 ++++++++++++++++ 10 files changed, 2729 insertions(+) create mode 100644 src/utils/Makefile.am create mode 100644 src/utils/bs11_config.c create mode 100644 src/utils/isdnsync.c create mode 100644 src/utils/meas_db.c create mode 100644 src/utils/meas_db.h create mode 100644 src/utils/meas_json.c create mode 100644 src/utils/meas_pcap2db.c create mode 100644 src/utils/meas_udp2db.c create mode 100644 src/utils/meas_vis.c create mode 100644 src/utils/smpp_mirror.c (limited to 'src/utils') diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am new file mode 100644 index 000000000..26494e13d --- /dev/null +++ b/src/utils/Makefile.am @@ -0,0 +1,147 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + -I$(top_builddir) \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(COVERAGE_CFLAGS) \ + $(SQLITE3_CFLAGS) \ + $(LIBSMPP34_CFLAGS) \ + $(NULL) + +AM_LDFLAGS = \ + $(COVERAGE_LDFLAGS) \ + $(NULL) + +noinst_HEADERS = \ + meas_db.h \ + $(NULL) + +bin_PROGRAMS = \ + bs11_config \ + isdnsync \ + meas_json \ + $(NULL) +if HAVE_SQLITE3 +bin_PROGRAMS += \ + osmo-meas-udp2db \ + $(NULL) +if HAVE_PCAP +bin_PROGRAMS += \ + osmo-meas-pcap2db \ + $(NULL) +endif +endif +if HAVE_LIBCDK +bin_PROGRAMS += \ + meas_vis \ + $(NULL) +endif + +if BUILD_SMPP +noinst_PROGRAMS = \ + smpp_mirror \ + $(NULL) +endif + +bs11_config_SOURCES = \ + bs11_config.c \ + $(NULL) + +bs11_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 \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + $(NULL) + +isdnsync_SOURCES = \ + isdnsync.c \ + $(NULL) + +smpp_mirror_SOURCES = \ + smpp_mirror.c \ + $(NULL) + +smpp_mirror_LDADD = \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBSMPP34_LIBS) \ + $(NULL) + +meas_vis_SOURCES = \ + meas_vis.c \ + $(NULL) + +meas_vis_LDADD = \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + -lcdk \ + -lncurses \ + $(NULL) + +meas_vis_CFLAGS = \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(NULL) + +osmo_meas_pcap2db_SOURCES = \ + meas_pcap2db.c \ + meas_db.c \ + $(NULL) + +osmo_meas_pcap2db_LDADD = \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(SQLITE3_LIBS) \ + -lpcap \ + $(NULL) + +osmo_meas_pcap2db_CFLAGS = \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(NULL) + +osmo_meas_udp2db_SOURCES = \ + meas_udp2db.c \ + meas_db.c \ + $(NULL) + +osmo_meas_udp2db_LDADD = \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(SQLITE3_LIBS) \ + $(NULL) + +osmo_meas_udp2db_CFLAGS = \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(NULL) + +meas_json_SOURCES = \ + meas_json.c \ + $(NULL) + +meas_json_LDADD = \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(NULL) + +meas_json_CFLAGS = \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(NULL) + diff --git a/src/utils/bs11_config.c b/src/utils/bs11_config.c new file mode 100644 index 000000000..a0f3cb757 --- /dev/null +++ b/src/utils/bs11_config.c @@ -0,0 +1,953 @@ +/* Siemens BS-11 microBTS configuration tool */ + +/* (C) 2009-2010 by Harald Welte + * All Rights Reserved + * + * This software is based on ideas (but not code) of BS11Config + * (C) 2009 by Dieter Spaar + * + * 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 . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void *tall_bs11cfg_ctx; +static struct e1inp_sign_link *oml_link; + +/* state of our bs11_config application */ +enum bs11cfg_state { + STATE_NONE, + STATE_LOGON_WAIT, + STATE_LOGON_ACK, + STATE_SWLOAD, + STATE_QUERY, +}; +static enum bs11cfg_state bs11cfg_state = STATE_NONE; +static char *command, *value; +struct osmo_timer_list status_timer; + +static const uint8_t obj_li_attr[] = { + NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00, + NM_ATT_BS11_L1_PROT_TYPE, 0x00, + NM_ATT_BS11_LINE_CFG, 0x00, +}; +static const uint8_t obj_bbsig0_attr[] = { + NM_ATT_BS11_RSSI_OFFS, 0x02, 0x00, 0x00, + NM_ATT_BS11_DIVERSITY, 0x01, 0x00, +}; +static const uint8_t obj_pa0_attr[] = { + NM_ATT_BS11_TXPWR, 0x01, BS11_TRX_POWER_GSM_30mW, +}; +static const char *trx1_password = "1111111111"; +#define TEI_OML 25 + +/* dummy function to keep gsm_data.c happy */ +struct osmo_counter *osmo_counter_alloc(const char *name) +{ + return NULL; +} + +int handle_serial_msg(struct msgb *rx_msg); + +/* create all objects for an initial configuration */ +static int create_objects(struct gsm_bts *bts) +{ + fprintf(stdout, "Crating Objects for minimal config\n"); + abis_nm_bs11_create_object(bts, BS11_OBJ_LI, 0, sizeof(obj_li_attr), + obj_li_attr); + abis_nm_bs11_create_object(bts, BS11_OBJ_GPSU, 0, 0, NULL); + abis_nm_bs11_create_object(bts, BS11_OBJ_ALCO, 0, 0, NULL); + abis_nm_bs11_create_object(bts, BS11_OBJ_CCLK, 0, 0, NULL); + abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 0, + sizeof(obj_bbsig0_attr), obj_bbsig0_attr); + abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 0, + sizeof(obj_pa0_attr), obj_pa0_attr); + abis_nm_bs11_create_envaBTSE(bts, 0); + abis_nm_bs11_create_envaBTSE(bts, 1); + abis_nm_bs11_create_envaBTSE(bts, 2); + abis_nm_bs11_create_envaBTSE(bts, 3); + + abis_nm_bs11_conn_oml_tei(bts, 0, 1, 0xff, TEI_OML); + + abis_nm_bs11_set_trx_power(bts->c0, BS11_TRX_POWER_GSM_30mW); + + sleep(1); + + abis_nm_bs11_set_trx1_pw(bts, trx1_password); + + sleep(1); + + return 0; +} + +static int create_trx1(struct gsm_bts *bts) +{ + uint8_t bbsig1_attr[sizeof(obj_bbsig0_attr)+12]; + uint8_t *cur = bbsig1_attr; + struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, 1); + + if (!trx) + trx = gsm_bts_trx_alloc(bts); + + fprintf(stdout, "Crating Objects for TRX1\n"); + + abis_nm_bs11_set_trx1_pw(bts, trx1_password); + + sleep(1); + + cur = tlv_put(cur, NM_ATT_BS11_PASSWORD, 10, + (uint8_t *)trx1_password); + memcpy(cur, obj_bbsig0_attr, sizeof(obj_bbsig0_attr)); + abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 1, + sizeof(bbsig1_attr), bbsig1_attr); + abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 1, + sizeof(obj_pa0_attr), obj_pa0_attr); + abis_nm_bs11_set_trx_power(trx, BS11_TRX_POWER_GSM_30mW); + + return 0; +} + +static char *serial_port = "/dev/ttyUSB0"; +static char *fname_safety = "BTSBMC76.SWI"; +static char *fname_software = "HS011106.SWL"; +static int delay_ms = 0; +static int win_size = 8; +static int param_disconnect = 0; +static int param_restart = 0; +static int param_forced = 0; +static struct gsm_bts *g_bts; + +static int file_is_readable(const char *fname) +{ + int rc; + struct stat st; + + rc = stat(fname, &st); + if (rc < 0) + return 0; + + if (S_ISREG(st.st_mode) && (st.st_mode & S_IRUSR)) + return 1; + + return 0; +} + +static int percent; +static int percent_old; + +/* callback function passed to the ABIS OML code */ +static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg, + void *data, void *param) +{ + if (hook != GSM_HOOK_NM_SWLOAD) + return 0; + + 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: + if (data) { + /* we did a safety load and must activate it */ + abis_nm_software_activate(g_bts, fname_safety, + swload_cbfn, g_bts); + sleep(5); + } + 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: + bs11cfg_state = STATE_NONE; + + break; + case NM_MT_LOAD_SEG_ACK: + percent = abis_nm_software_load_status(g_bts); + if (percent > percent_old) + printf("Software Download Progress: %d%%\n", percent); + percent_old = percent; + break; + } + return 0; +} + +static const struct value_string bs11_linkst_names[] = { + { 0, "Down" }, + { 1, "Up" }, + { 2, "Restoring" }, + { 0, NULL } +}; + +static const char *linkstate_name(uint8_t linkstate) +{ + return get_value_string(bs11_linkst_names, linkstate); +} + +static const struct value_string mbccu_load_names[] = { + { 0, "No Load" }, + { 1, "Load BTSCAC" }, + { 2, "Load BTSDRX" }, + { 3, "Load BTSBBX" }, + { 4, "Load BTSARC" }, + { 5, "Load" }, + { 0, NULL } +}; + +static const char *mbccu_load_name(uint8_t linkstate) +{ + return get_value_string(mbccu_load_names, linkstate); +} + +static const char *bts_phase_name(uint8_t phase) +{ + switch (phase) { + case BS11_STATE_WARM_UP: + case BS11_STATE_WARM_UP_2: + return "Warm Up"; + break; + case BS11_STATE_LOAD_SMU_SAFETY: + return "Load SMU Safety"; + break; + case BS11_STATE_LOAD_SMU_INTENDED: + return "Load SMU Intended"; + break; + case BS11_STATE_LOAD_MBCCU: + return "Load MBCCU"; + break; + case BS11_STATE_SOFTWARE_RQD: + return "Software required"; + break; + case BS11_STATE_WAIT_MIN_CFG: + case BS11_STATE_WAIT_MIN_CFG_2: + return "Wait minimal config"; + break; + case BS11_STATE_MAINTENANCE: + return "Maintenance"; + break; + case BS11_STATE_NORMAL: + return "Normal"; + break; + case BS11_STATE_ABIS_LOAD: + return "Abis load"; + break; + default: + return "Unknown"; + break; + } +} + +static const char *trx_power_name(uint8_t pwr) +{ + switch (pwr) { + case BS11_TRX_POWER_GSM_2W: + return "2W (GSM)"; + case BS11_TRX_POWER_GSM_250mW: + return "250mW (GSM)"; + case BS11_TRX_POWER_GSM_80mW: + return "80mW (GSM)"; + case BS11_TRX_POWER_GSM_30mW: + return "30mW (GSM)"; + case BS11_TRX_POWER_DCS_3W: + return "3W (DCS)"; + case BS11_TRX_POWER_DCS_1W6: + return "1.6W (DCS)"; + case BS11_TRX_POWER_DCS_500mW: + return "500mW (DCS)"; + case BS11_TRX_POWER_DCS_160mW: + return "160mW (DCS)"; + default: + return "unknown value"; + } +} + +static const char *pll_mode_name(uint8_t mode) +{ + switch (mode) { + case BS11_LI_PLL_LOCKED: + return "E1 Locked"; + case BS11_LI_PLL_STANDALONE: + return "Standalone"; + default: + return "unknown"; + } +} + +static const char *cclk_acc_name(uint8_t acc) +{ + switch (acc) { + case 0: + /* Out of the demanded +/- 0.05ppm */ + return "Medium"; + case 1: + /* Synchronized with Abis, within demanded tolerance +/- 0.05ppm */ + return "High"; + default: + return "unknown"; + } +} + +static const char *bport_lcfg_name(uint8_t lcfg) +{ + switch (lcfg) { + case BS11_LINE_CFG_STAR: + return "Star"; + case BS11_LINE_CFG_MULTIDROP: + return "Multi-Drop"; + default: + return "unknown"; + } +} + +static const char *obj_name(struct abis_om_fom_hdr *foh) +{ + static char retbuf[256]; + + retbuf[0] = 0; + + switch (foh->obj_class) { + case NM_OC_BS11: + strcat(retbuf, "BS11 "); + switch (foh->obj_inst.bts_nr) { + case BS11_OBJ_PA: + sprintf(retbuf+strlen(retbuf), "Power Amplifier %d ", + foh->obj_inst.ts_nr); + break; + case BS11_OBJ_LI: + sprintf(retbuf+strlen(retbuf), "Line Interface "); + break; + case BS11_OBJ_CCLK: + sprintf(retbuf+strlen(retbuf), "CCLK "); + break; + } + break; + case NM_OC_SITE_MANAGER: + strcat(retbuf, "SITE MANAGER "); + break; + case NM_OC_BS11_BPORT: + sprintf(retbuf+strlen(retbuf), "BPORT%u ", + foh->obj_inst.bts_nr); + break; + } + return retbuf; +} + +static void print_state(struct tlv_parsed *tp) +{ + if (TLVP_PRESENT(tp, NM_ATT_BS11_BTS_STATE)) { + uint8_t phase, mbccu; + if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 1) { + phase = *TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE); + printf("PHASE: %u %-20s ", phase & 0xf, + bts_phase_name(phase)); + } + if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 2) { + mbccu = *(TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE)+1); + printf("MBCCU0: %-11s MBCCU1: %-11s ", + mbccu_load_name(mbccu & 0xf), mbccu_load_name(mbccu >> 4)); + } + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_E1_STATE) && + TLVP_LEN(tp, NM_ATT_BS11_E1_STATE) >= 1) { + uint8_t e1_state = *TLVP_VAL(tp, NM_ATT_BS11_E1_STATE); + printf("Abis-link: %-9s ", linkstate_name(e1_state & 0xf)); + } + printf("\n"); +} + +static int print_attr(struct tlv_parsed *tp) +{ + if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_PCB_SERIAL)) { + printf("\tBS-11 ESN PCB Serial Number: %s\n", + TLVP_VAL(tp, NM_ATT_BS11_ESN_PCB_SERIAL)); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_HW_CODE_NO)) { + printf("\tBS-11 ESN Hardware Code Number: %s\n", + TLVP_VAL(tp, NM_ATT_BS11_ESN_HW_CODE_NO)+6); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_FW_CODE_NO)) { + printf("\tBS-11 ESN Firmware Code Number: %s\n", + TLVP_VAL(tp, NM_ATT_BS11_ESN_FW_CODE_NO)+6); + } +#if 0 + if (TLVP_PRESENT(tp, NM_ATT_BS11_BOOT_SW_VERS)) { + printf("BS-11 Boot Software Version: %s\n", + TLVP_VAL(tp, NM_ATT_BS11_BOOT_SW_VERS)+6); + } +#endif + if (TLVP_PRESENT(tp, NM_ATT_ABIS_CHANNEL) && + TLVP_LEN(tp, NM_ATT_ABIS_CHANNEL) >= 3) { + const uint8_t *chan = TLVP_VAL(tp, NM_ATT_ABIS_CHANNEL); + printf("\tE1 Channel: Port=%u Timeslot=%u ", + chan[0], chan[1]); + if (chan[2] == 0xff) + printf("(Full Slot)\n"); + else + printf("Subslot=%u\n", chan[2]); + } + if (TLVP_PRESENT(tp, NM_ATT_TEI)) + printf("\tTEI: %d\n", *TLVP_VAL(tp, NM_ATT_TEI)); + if (TLVP_PRESENT(tp, NM_ATT_BS11_TXPWR) && + TLVP_LEN(tp, NM_ATT_BS11_TXPWR) >= 1) { + printf("\tTRX Power: %s\n", + trx_power_name(*TLVP_VAL(tp, NM_ATT_BS11_TXPWR))); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL_MODE) && + TLVP_LEN(tp, NM_ATT_BS11_PLL_MODE) >= 1) { + printf("\tPLL Mode: %s\n", + pll_mode_name(*TLVP_VAL(tp, NM_ATT_BS11_PLL_MODE))); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL) && + TLVP_LEN(tp, NM_ATT_BS11_PLL) >= 4) { + const uint8_t *vp = TLVP_VAL(tp, NM_ATT_BS11_PLL); + printf("\tPLL Set Value=%d, Work Value=%d\n", + vp[0] << 8 | vp[1], vp[2] << 8 | vp[3]); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_ACCURACY) && + TLVP_LEN(tp, NM_ATT_BS11_CCLK_ACCURACY) >= 1) { + const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_ACCURACY); + printf("\tCCLK Accuracy: %s (%d)\n", cclk_acc_name(*acc), *acc); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_TYPE) && + TLVP_LEN(tp, NM_ATT_BS11_CCLK_TYPE) >= 1) { + const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_TYPE); + printf("\tCCLK Type=%d\n", *acc); + } + if (TLVP_PRESENT(tp, NM_ATT_BS11_LINE_CFG) && + TLVP_LEN(tp, NM_ATT_BS11_LINE_CFG) >= 1) { + const uint8_t *lcfg = TLVP_VAL(tp, NM_ATT_BS11_LINE_CFG); + printf("\tLine Configuration: %s (%d)\n", + bport_lcfg_name(*lcfg), *lcfg); + } + + + + return 0; +} + +static void cmd_query(void) +{ + struct gsm_bts_trx *trx = g_bts->c0; + + bs11cfg_state = STATE_QUERY; + abis_nm_bs11_get_serno(g_bts); + abis_nm_bs11_get_oml_tei_ts(g_bts); + abis_nm_bs11_get_pll_mode(g_bts); + abis_nm_bs11_get_cclk(g_bts); + abis_nm_bs11_get_trx_power(trx); + trx = gsm_bts_trx_num(g_bts, 1); + if (trx) + abis_nm_bs11_get_trx_power(trx); + abis_nm_bs11_get_bport_line_cfg(g_bts, 0); + abis_nm_bs11_get_bport_line_cfg(g_bts, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; +} + +/* handle a response from the BTS to a GET STATE command */ +static int handle_state_resp(enum abis_bs11_phase state) +{ + int rc = 0; + + switch (state) { + case BS11_STATE_WARM_UP: + case BS11_STATE_LOAD_SMU_SAFETY: + case BS11_STATE_LOAD_SMU_INTENDED: + case BS11_STATE_LOAD_MBCCU: + break; + case BS11_STATE_SOFTWARE_RQD: + bs11cfg_state = STATE_SWLOAD; + /* send safety load. Use g_bts as private 'param' + * argument, so our swload_cbfn can distinguish + * a safety load from a regular software */ + if (file_is_readable(fname_safety)) + rc = abis_nm_software_load(g_bts, 0xff, fname_safety, + win_size, param_forced, + swload_cbfn, g_bts); + else + fprintf(stderr, "No valid Safety Load file \"%s\"\n", + fname_safety); + break; + case BS11_STATE_WAIT_MIN_CFG: + case BS11_STATE_WAIT_MIN_CFG_2: + bs11cfg_state = STATE_SWLOAD; + rc = create_objects(g_bts); + break; + case BS11_STATE_MAINTENANCE: + if (command) { + if (!strcmp(command, "disconnect")) + abis_nm_bs11_factory_logon(g_bts, 0); + else if (!strcmp(command, "reconnect")) + rc = abis_nm_bs11_bsc_disconnect(g_bts, 1); + else if (!strcmp(command, "software") + && bs11cfg_state != STATE_SWLOAD) { + bs11cfg_state = STATE_SWLOAD; + /* send software (FIXME: over A-bis?) */ + if (file_is_readable(fname_software)) + rc = abis_nm_bs11_load_swl(g_bts, fname_software, + win_size, param_forced, + swload_cbfn); + else + fprintf(stderr, "No valid Software file \"%s\"\n", + fname_software); + } else if (!strcmp(command, "delete-trx1")) { + printf("Locing BBSIG and PA objects of TRX1\n"); + abis_nm_chg_adm_state(g_bts, NM_OC_BS11, + BS11_OBJ_BBSIG, 0, 1, + NM_STATE_LOCKED); + abis_nm_chg_adm_state(g_bts, NM_OC_BS11, + BS11_OBJ_PA, 0, 1, + NM_STATE_LOCKED); + sleep(1); + printf("Deleting BBSIG and PA objects of TRX1\n"); + abis_nm_bs11_delete_object(g_bts, BS11_OBJ_BBSIG, 1); + abis_nm_bs11_delete_object(g_bts, BS11_OBJ_PA, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "create-trx1")) { + create_trx1(g_bts); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "pll-e1-locked")) { + abis_nm_bs11_set_pll_locked(g_bts, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "pll-standalone")) { + abis_nm_bs11_set_pll_locked(g_bts, 0); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "pll-setvalue")) { + abis_nm_bs11_set_pll(g_bts, atoi(value)); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "pll-workvalue")) { + /* To set the work value we need to login as FIELD */ + abis_nm_bs11_factory_logon(g_bts, 0); + sleep(1); + abis_nm_bs11_infield_logon(g_bts, 1); + sleep(1); + abis_nm_bs11_set_pll(g_bts, atoi(value)); + sleep(1); + abis_nm_bs11_infield_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "oml-tei")) { + abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML); + command = NULL; + } else if (!strcmp(command, "restart")) { + abis_nm_bs11_restart(g_bts); + command = NULL; + } else if (!strcmp(command, "query")) { + cmd_query(); + } else if (!strcmp(command, "create-bport1")) { + abis_nm_bs11_create_bport(g_bts, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "delete-bport1")) { + abis_nm_chg_adm_state(g_bts, NM_OC_BS11_BPORT, 1, 0xff, 0xff, NM_STATE_LOCKED); + sleep(1); + abis_nm_bs11_delete_bport(g_bts, 1); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "bport0-star")) { + abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_STAR); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "bport0-multidrop")) { + abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_MULTIDROP); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } else if (!strcmp(command, "bport1-multidrop")) { + abis_nm_bs11_set_bport_line_cfg(g_bts, 1, BS11_LINE_CFG_MULTIDROP); + sleep(1); + abis_nm_bs11_factory_logon(g_bts, 0); + command = NULL; + } + + } + break; + case BS11_STATE_NORMAL: + if (command) { + if (!strcmp(command, "reconnect")) + abis_nm_bs11_factory_logon(g_bts, 0); + else if (!strcmp(command, "disconnect")) + abis_nm_bs11_bsc_disconnect(g_bts, 0); + else if (!strcmp(command, "query")) { + cmd_query(); + } + } else if (param_disconnect) { + param_disconnect = 0; + abis_nm_bs11_bsc_disconnect(g_bts, 0); + if (param_restart) { + param_restart = 0; + abis_nm_bs11_restart(g_bts); + } + } + break; + default: + break; + } + return rc; +} + +/* handle a fully-received message/packet from the RS232 port */ +static int abis_nm_bs11cfg_rcvmsg(struct msgb *rx_msg) +{ + struct e1inp_sign_link *link = rx_msg->dst; + struct abis_om_hdr *oh; + struct abis_om_fom_hdr *foh; + struct tlv_parsed tp; + int rc = -1; + +#if 0 + const uint8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 }; + + if (rx_msg->len < LAPD_HDR_LEN + + sizeof(struct abis_om_fom_hdr) + + sizeof(struct abis_om_hdr)) { + if (!memcmp(rx_msg->data + 2, too_fast, + sizeof(too_fast))) { + fprintf(stderr, "BS11 tells us we're too " + "fast, try --delay bigger than %u\n", + delay_ms); + return -E2BIG; + } else + fprintf(stderr, "unknown BS11 message\n"); + } +#endif + + oh = (struct abis_om_hdr *) msgb_l2(rx_msg); + foh = (struct abis_om_fom_hdr *) oh->data; + switch (foh->msg_type) { + case NM_MT_BS11_LMT_LOGON_ACK: + printf("LMT LOGON: ACK\n\n"); + if (bs11cfg_state == STATE_NONE) + bs11cfg_state = STATE_LOGON_ACK; + rc = abis_nm_bs11_get_state(g_bts); + break; + case NM_MT_BS11_LMT_LOGOFF_ACK: + printf("LMT LOGOFF: ACK\n"); + exit(0); + break; + case NM_MT_BS11_GET_STATE_ACK: + rc = abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); + print_state(&tp); + if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) && + TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1) + rc = handle_state_resp(*TLVP_VAL(&tp, NM_ATT_BS11_BTS_STATE)); + break; + case NM_MT_GET_ATTR_RESP: + printf("\n%sATTRIBUTES:\n", obj_name(foh)); + abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); + rc = print_attr(&tp); + //osmo_hexdump(foh->data, oh->length-sizeof(*foh)); + break; + case NM_MT_BS11_SET_ATTR_ACK: + printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) ACK\n", + foh->obj_class, foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); + rc = 0; + break; + case NM_MT_BS11_SET_ATTR_NACK: + printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) NACK\n", + foh->obj_class, foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); + break; + case NM_MT_GET_ATTR_NACK: + printf("\n%sGET ATTR NACK\n", obj_name(foh)); + break; + case NM_MT_BS11_CREATE_OBJ_ACK: + printf("\n%sCREATE OBJECT ACK\n", obj_name(foh)); + break; + case NM_MT_BS11_CREATE_OBJ_NACK: + printf("\n%sCREATE OBJECT NACK\n", obj_name(foh)); + break; + case NM_MT_BS11_DELETE_OBJ_ACK: + printf("\n%sDELETE OBJECT ACK\n", obj_name(foh)); + break; + case NM_MT_BS11_DELETE_OBJ_NACK: + printf("\n%sDELETE OBJECT NACK\n", obj_name(foh)); + break; + default: + rc = abis_nm_rcvmsg(rx_msg); + } + if (rc < 0) { + perror("ERROR in main loop"); + //break; + } + /* flush the queue of pending messages to be sent. */ + abis_nm_queue_send_next(link->trx->bts); + if (rc == 1) + return rc; + + switch (bs11cfg_state) { + case STATE_NONE: + abis_nm_bs11_factory_logon(g_bts, 1); + break; + case STATE_LOGON_ACK: + osmo_timer_schedule(&status_timer, 5, 0); + break; + default: + break; + } + + return rc; +} + +void status_timer_cb(void *data) +{ + abis_nm_bs11_get_state(g_bts); +} + +static void print_banner(void) +{ + printf("bs11_config (C) 2009-2010 by Harald Welte and Dieter Spaar\n"); + printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); +} + +static void print_help(void) +{ + printf("bs11_config [options] [command]\n"); + printf("\nSupported options:\n"); + printf("\t-h --help\t\t\tPrint this help text\n"); + printf("\t-p --port \t\tSpecify serial port\n"); + printf("\t-s --software \t\tSpecify Software file\n"); + printf("\t-S --safety \t\tSpecify Safety Load file\n"); + printf("\t-d --delay \t\t\tSpecify delay in milliseconds\n"); + printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n"); + printf("\t-w --win-size \t\tSpecify Window Size\n"); + printf("\t-f --forced\t\t\tForce Software Load\n"); + printf("\nSupported commands:\n"); + printf("\tquery\t\t\tQuery the BS-11 about serial number and configuration\n"); + printf("\tdisconnect\t\tDisconnect A-bis link (go into administrative state)\n"); + printf("\tresconnect\t\tReconnect A-bis link (go into normal state)\n"); + printf("\trestart\t\t\tRestart the BTS\n"); + printf("\tsoftware\t\tDownload Software (only in administrative state)\n"); + printf("\tcreate-trx1\t\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n"); + printf("\tdelete-trx1\t\tDelete objects for TRX1\n"); + printf("\tpll-e1-locked\t\tSet the PLL to be locked to E1 clock\n"); + printf("\tpll-standalone\t\tSet the PLL to be in standalone mode\n"); + printf("\tpll-setvalue \tSet the PLL set value\n"); + printf("\tpll-workvalue \tSet the PLL work value\n"); + printf("\toml-tei\t\t\tSet OML E1 TS and TEI\n"); + printf("\tbport0-star\t\tSet BPORT0 line config to star\n"); + printf("\tbport0-multidrop\tSet BPORT0 line config to multidrop\n"); + printf("\tbport1-multidrop\tSet BPORT1 line config to multidrop\n"); + printf("\tcreate-bport1\t\tCreate BPORT1 object\n"); + printf("\tdelete-bport1\t\tDelete BPORT1 object\n"); +} + +static void handle_options(int argc, char **argv) +{ + int option_index = 0; + print_banner(); + + while (1) { + int c; + static struct option long_options[] = { + { "help", 0, 0, 'h' }, + { "port", 1, 0, 'p' }, + { "software", 1, 0, 's' }, + { "safety", 1, 0, 'S' }, + { "delay", 1, 0, 'd' }, + { "disconnect", 0, 0, 'D' }, + { "win-size", 1, 0, 'w' }, + { "forced", 0, 0, 'f' }, + { "restart", 0, 0, 'r' }, + { "debug", 1, 0, 'b'}, + }; + + c = getopt_long(argc, argv, "hp:s:S:td:Dw:fra:", + long_options, &option_index); + + if (c == -1) + break; + + switch (c) { + case 'h': + print_help(); + exit(0); + case 'p': + serial_port = optarg; + break; + case 'b': + log_parse_category_mask(osmo_stderr_target, optarg); + break; + case 's': + fname_software = optarg; + break; + case 'S': + fname_safety = optarg; + break; + case 'd': + delay_ms = atoi(optarg); + break; + case 'w': + win_size = atoi(optarg); + break; + case 'D': + param_disconnect = 1; + break; + case 'f': + param_forced = 1; + break; + case 'r': + param_disconnect = 1; + param_restart = 1; + break; + default: + break; + } + } + if (optind < argc) { + command = argv[optind]; + if (optind+1 < argc) + value = argv[optind+1]; + } + +} + +static int num_sigint; + +static void signal_handler(int signal) +{ + fprintf(stdout, "\nsignal %u received\n", signal); + + switch (signal) { + case SIGINT: + num_sigint++; + abis_nm_bs11_factory_logon(g_bts, 0); + if (num_sigint >= 3) + exit(0); + break; + } +} + +static int bs11cfg_sign_link(struct msgb *msg) +{ + msg->dst = oml_link; + return abis_nm_bs11cfg_rcvmsg(msg); +} + +struct e1inp_line_ops bs11cfg_e1inp_line_ops = { + .sign_link = bs11cfg_sign_link, +}; + +extern int bts_model_bs11_init(void); +int main(int argc, char **argv) +{ + struct gsm_network *gsmnet; + struct e1inp_line *line; + + tall_bs11cfg_ctx = talloc_named_const(NULL, 0, "bs11-config"); + msgb_talloc_ctx_init(tall_bs11cfg_ctx, 0); + + osmo_init_logging(&log_info); + handle_options(argc, argv); + bts_model_bs11_init(); + + gsmnet = bsc_network_init(tall_bs11cfg_ctx, 1, 1, NULL); + if (!gsmnet) { + fprintf(stderr, "Unable to allocate gsm network\n"); + exit(1); + } + g_bts = gsm_bts_alloc_register(gsmnet, GSM_BTS_TYPE_BS11, + HARDCODED_BSIC); + + /* Override existing OML callback handler to set our own. */ + g_bts->model->oml_rcvmsg = abis_nm_bs11cfg_rcvmsg; + + libosmo_abis_init(tall_bs11cfg_ctx); + + /* Initialize virtual E1 line over rs232. */ + line = talloc_zero(tall_bs11cfg_ctx, struct e1inp_line); + if (!line) { + fprintf(stderr, "Unable to allocate memory for virtual E1 line\n"); + exit(1); + } + /* set the serial port. */ + bs11cfg_e1inp_line_ops.cfg.rs232.port = serial_port; + bs11cfg_e1inp_line_ops.cfg.rs232.delay = delay_ms; + + line->driver = e1inp_driver_find("rs232"); + if (!line->driver) { + fprintf(stderr, "cannot find `rs232' driver, giving up.\n"); + exit(1); + } + e1inp_line_bind_ops(line, &bs11cfg_e1inp_line_ops); + + /* configure and create signalling link for OML. */ + e1inp_ts_config_sign(&line->ts[0], line); + g_bts->oml_link = oml_link = + e1inp_sign_link_create(&line->ts[0], E1INP_SIGN_OML, + g_bts->c0, TEI_OML, 0); + + e1inp_line_update(line); + + signal(SIGINT, &signal_handler); + + abis_nm_bs11_factory_logon(g_bts, 1); + //abis_nm_bs11_get_serno(g_bts); + + osmo_timer_setup(&status_timer, status_timer_cb, NULL); + + while (1) { + if (osmo_select_main(0) < 0) + break; + } + + abis_nm_bs11_factory_logon(g_bts, 0); + + exit(0); +} diff --git a/src/utils/isdnsync.c b/src/utils/isdnsync.c new file mode 100644 index 000000000..cc8ff6723 --- /dev/null +++ b/src/utils/isdnsync.c @@ -0,0 +1,189 @@ +/* isdnsync.c + * + * Author Andreas Eversberg + * + * 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 . + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mISDNif.h" +#define MISDN_OLD_AF_COMPATIBILITY +#define AF_COMPATIBILITY_FUNC +#include "compat_af_isdn.h" + +int card = 0; +int sock = -1; + +int mISDN_open(void) +{ + int fd, ret; + struct mISDN_devinfo devinfo; + struct sockaddr_mISDN l2addr; + + fd = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); + if (fd < 0) { + fprintf(stderr, "could not open socket (%s)\n", strerror(errno)); + return fd; + } + devinfo.id = card; + ret = ioctl(fd, IMGETDEVINFO, &devinfo); + if (ret < 0) { + fprintf(stderr,"could not send IOCTL IMGETCOUNT (%s)\n", strerror(errno)); + close(fd); + return ret; + } + close(fd); + if (!(devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) + && !(devinfo.Dprotocols & (1 << ISDN_P_TE_E1))) { + fprintf(stderr,"Interface does not support TE mode (%s)\n", strerror(errno)); + return ret; + } + fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_TE); + if (fd < 0) { + fprintf(stderr,"could not open ISDN_P_LAPD_TE socket (%s)\n", strerror(errno)); + return fd; + } + l2addr.family = AF_ISDN; + l2addr.dev = card; + l2addr.channel = 0; + l2addr.sapi = 0; + l2addr.tei = 0; + ret = bind(fd, (struct sockaddr *)&l2addr, sizeof(l2addr)); + if (ret < 0) { + fprintf(stderr,"could not bind socket for card %d (%s)\n", card, strerror(errno)); + close(fd); + return ret; + } + sock = fd; + + return sock; +} + + +void mISDN_handle(void) +{ + int ret; + fd_set rfd; + struct timeval tv; + struct sockaddr_mISDN addr; + socklen_t alen; + unsigned char buffer[2048]; + struct mISDNhead *hh = (struct mISDNhead *)buffer; + int l1 = 0, l2 = 0, tei = 0; + + while(1) { +again: + FD_ZERO(&rfd); + FD_SET(sock, &rfd); + tv.tv_sec = 2; + tv.tv_usec = 0; + ret = select(sock+1, &rfd, NULL, NULL, &tv); + if (ret < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, "%s aborted: %s\n", __FUNCTION__, strerror(errno)); + break; + } + if (FD_ISSET(sock, &rfd)) { + alen = sizeof(addr); + ret = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &alen); + if (ret < 0) { + fprintf(stderr, "%s read socket error %s\n", __FUNCTION__, strerror(errno)); + } else if (ret < MISDN_HEADER_LEN) { + fprintf(stderr, "%s read socket shor frame\n", __FUNCTION__); + } else { + switch(hh->prim) { + case MPH_ACTIVATE_IND: + case PH_ACTIVATE_IND: + if (!l1) { + printf("PH_ACTIVATE\n"); + printf("*** Sync available from interface :-)\n"); + l1 = 1; + } + goto again; + break; + case MPH_DEACTIVATE_IND: + case PH_DEACTIVATE_IND: + if (l1) { + printf("PH_DEACTIVATE\n"); + printf("*** Lost sync on interface :-(\n"); + l1 = 0; + } + goto again; + break; + case DL_ESTABLISH_IND: + case DL_ESTABLISH_CNF: + printf("DL_ESTABLISH\n"); + l2 = 1; + goto again; + break; + case DL_RELEASE_IND: + case DL_RELEASE_CNF: + printf("DL_RELEASE\n"); + l2 = 0; + goto again; + break; + case DL_INFORMATION_IND: + printf("DL_INFORMATION (tei %d sapi %d)\n", addr.tei, addr.sapi); + tei = 1; + break; + default: +// printf("prim %x\n", hh->prim); + goto again; + } + } + } + if (tei && !l2) { + hh->prim = DL_ESTABLISH_REQ; + printf("-> activating layer 2\n"); + sendto(sock, buffer, MISDN_HEADER_LEN, 0, (struct sockaddr *) &addr, alen); + } + } +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc <= 1) + { + printf("Usage: %s \n\n", argv[0]); + printf("Opens given card number in TE-mode PTP and tries to keep layer 2 established.\n"); + printf("This keeps layer 1 activated to retrieve a steady sync signal from network.\n"); + return(0); + } + + card = atoi(argv[1]); + + init_af_isdn(); + + if ((ret = mISDN_open() < 0)) + return(ret); + + mISDN_handle(); + + close(sock); + + return 0; +} diff --git a/src/utils/meas_db.c b/src/utils/meas_db.c new file mode 100644 index 000000000..d81efcade --- /dev/null +++ b/src/utils/meas_db.c @@ -0,0 +1,330 @@ +/* Routines for storing measurement reports in SQLite3 database */ + +/* (C) 2012 by Harald Welte + * + * 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 . + * + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "meas_db.h" + +#define INS_MR "INSERT INTO meas_rep (time, imsi, name, scenario, nr, bs_power, ms_timing_offset, fpc, ms_l1_pwr, ms_l1_ta) VALUES (?,?,?,?,?,?,?,?,?,?)" +#define INS_UD "INSERT INTO meas_rep_unidir (meas_id, rx_lev_full, rx_lev_sub, rx_qual_full, rx_qual_sub, dtx, uplink) VALUES (?,?,?,?,?,?,?)" +#define UPD_MR "UPDATE meas_rep SET ul_unidir=?, dl_unidir=? WHERE id=?" + +struct meas_db_state { + sqlite3 *db; + sqlite3_stmt *stmt_ins_ud; + sqlite3_stmt *stmt_ins_mr; + sqlite3_stmt *stmt_upd_mr; +}; + +/* macros to check for SQLite3 result codes */ +#define _SCK_OK(db, call, exp) \ + do { \ + int rc = call; \ + if (rc != exp) { \ + fprintf(stderr,"SQL Error in line %u: %s\n", \ + __LINE__, sqlite3_errmsg(db)); \ + goto err_io; \ + } \ + } while (0) +#define SCK_OK(db, call) _SCK_OK(db, call, SQLITE_OK) +#define SCK_DONE(db, call) _SCK_OK(db, call, SQLITE_DONE) + +static int _insert_ud(struct meas_db_state *st, unsigned long meas_id, int dtx, + int uplink, const struct gsm_meas_rep_unidir *ud) +{ + unsigned long rowid; + + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 1, meas_id)); + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 2, + rxlev2dbm(ud->full.rx_lev))); + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 3, + rxlev2dbm(ud->sub.rx_lev))); + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 4, ud->full.rx_qual)); + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 5, ud->sub.rx_qual)); + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 6, dtx)); + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 7, uplink)); + + SCK_DONE(st->db, sqlite3_step(st->stmt_ins_ud)); + + SCK_OK(st->db, sqlite3_reset(st->stmt_ins_ud)); + + return sqlite3_last_insert_rowid(st->db); +err_io: + exit(1); +} + +/* insert a measurement report into the database */ +int meas_db_insert(struct meas_db_state *st, const char *imsi, + const char *name, unsigned long timestamp, + const char *scenario, + const struct gsm_meas_rep *mr) +{ + int rc; + sqlite3_int64 rowid, ul_rowid, dl_rowid; + + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 1, timestamp)); + + if (imsi) + SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 2, + imsi, -1, SQLITE_STATIC)); + else + SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 2)); + + if (name) + SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 3, + name, -1, SQLITE_STATIC)); + else + SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 3)); + + if (scenario) + SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 4, + scenario, -1, SQLITE_STATIC)); + else + SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 4)); + + + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 5, mr->nr)); + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 6, mr->bs_power)); + + if (mr->flags & MEAS_REP_F_MS_TO) + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 7, mr->ms_timing_offset)); + else + SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 7)); + + if (mr->flags & MEAS_REP_F_FPC) + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 8, 1)); + else + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 8, 0)); + + if (mr->flags & MEAS_REP_F_MS_L1) { + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 9, + mr->ms_l1.pwr)); + SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 10, + mr->ms_l1.ta)); + } + + SCK_DONE(st->db, sqlite3_step(st->stmt_ins_mr)); + SCK_OK(st->db, sqlite3_reset(st->stmt_ins_mr)); + + rowid = sqlite3_last_insert_rowid(st->db); + + /* insert uplink measurement */ + ul_rowid = _insert_ud(st, rowid, mr->flags & MEAS_REP_F_UL_DTX, + 1, &mr->ul); + SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 1, ul_rowid)); + + /* insert downlink measurement, if present */ + if (mr->flags & MEAS_REP_F_DL_VALID) { + dl_rowid = _insert_ud(st, rowid, mr->flags & MEAS_REP_F_DL_DTX, + 0, &mr->dl); + SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 2, dl_rowid)); + } else + SCK_OK(st->db, sqlite3_bind_null(st->stmt_upd_mr, 2)); + + /* update meas_rep with the id's of the unidirectional + * measurements */ + SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 3, rowid)); + SCK_DONE(st->db, sqlite3_step(st->stmt_upd_mr)); + SCK_OK(st->db, sqlite3_reset(st->stmt_upd_mr)); + + return 0; + +err_io: + return -EIO; +} + +int meas_db_begin(struct meas_db_state *st) +{ + SCK_OK(st->db, sqlite3_exec(st->db, "BEGIN", NULL, NULL, NULL)); + + return 0; + +err_io: + return -EIO; +} + +int meas_db_commit(struct meas_db_state *st) +{ + SCK_OK(st->db, sqlite3_exec(st->db, "COMMIT", NULL, NULL, NULL)); + + return 0; + +err_io: + return -EIO; +} + +static const char *create_stmts[] = { + "CREATE TABLE IF NOT EXISTS meas_rep (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "time TIMESTAMP," + "imsi TEXT," + "name TEXT," + "scenario TEXT," + "nr INTEGER," + "bs_power INTEGER NOT NULL," + "ms_timing_offset INTEGER," + "fpc INTEGER NOT NULL DEFAULT 0," + "ul_unidir INTEGER REFERENCES meas_rep_unidir(id)," + "dl_unidir INTEGER REFERENCES meas_rep_unidir(id)," + "ms_l1_pwr INTEGER," + "ms_l1_ta INTEGER" + ")", + "CREATE TABLE IF NOT EXISTS meas_rep_unidir (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "meas_id INTEGER NOT NULL REFERENCES meas_rep(id)," + "rx_lev_full INTEGER NOT NULL," + "rx_lev_sub INTEGER NOT NULL," + "rx_qual_full INTEGER NOT NULL," + "rx_qual_sub INTEGER NOT NULL," + "dtx BOOLEAN NOT NULL DEFAULT 0," + "uplink BOOLEAN NOT NULL" + ")", + "CREATE VIEW IF NOT EXISTS path_loss AS " + "SELECT " + "meas_rep.id, " + "datetime(time,'unixepoch') AS timestamp, " + "imsi, " + "name, " + "scenario, " + "ms_timing_offset, " + "ms_l1_ta, " + "fpc, " + "ms_l1_pwr, " + "ud_ul.rx_lev_full AS ul_rx_lev_full, " + "ms_l1_pwr-ud_ul.rx_lev_full AS ul_path_loss_full, " + "ud_ul.rx_lev_sub ul_rx_lev_sub, " + "ms_l1_pwr-ud_ul.rx_lev_sub AS ul_path_loss_sub, " + "ud_ul.rx_qual_full AS ul_rx_qual_full, " + "ud_ul.rx_qual_sub AS ul_rx_qual_sub, " + "bs_power, " + "ud_dl.rx_lev_full AS dl_rx_lev_full, " + "bs_power-ud_dl.rx_lev_full AS dl_path_loss_full, " + "ud_dl.rx_lev_sub AS dl_rx_lev_sub, " + "bs_power-ud_dl.rx_lev_sub AS dl_path_loss_sub, " + "ud_dl.rx_qual_full AS dl_rx_qual_full, " + "ud_dl.rx_qual_sub AS dl_rx_qual_sub " + "FROM " + "meas_rep, " + "meas_rep_unidir AS ud_dl, " + "meas_rep_unidir AS ud_ul " + "WHERE " + "ud_ul.id = meas_rep.ul_unidir AND " + "ud_dl.id = meas_rep.dl_unidir", + "CREATE VIEW IF NOT EXISTS overview AS " + "SELECT " + "id," + "timestamp," + "imsi," + "name," + "scenario," + "ms_l1_pwr," + "ul_rx_lev_full," + "ul_path_loss_full," + "ul_rx_qual_full," + "bs_power," + "dl_rx_lev_full," + "dl_path_loss_full," + "dl_rx_qual_full " + "FROM path_loss", +}; + +static int check_create_tbl(struct meas_db_state *st) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(create_stmts); i++) { + SCK_OK(st->db, sqlite3_exec(st->db, create_stmts[i], + NULL, NULL, NULL)); + } + + return 0; +err_io: + return -EIO; +} + + +#define PREP_CHK(db, stmt, ptr) \ + do { \ + int rc; \ + rc = sqlite3_prepare_v2(db, stmt, strlen(stmt)+1, \ + ptr, NULL); \ + if (rc != SQLITE_OK) { \ + fprintf(stderr, "Error during prepare of '%s': %s\n", \ + stmt, sqlite3_errmsg(db)); \ + goto err_io; \ + } \ + } while (0) + +struct meas_db_state *meas_db_open(void *ctx, const char *fname) +{ + int rc; + struct meas_db_state *st = talloc_zero(ctx, struct meas_db_state); + + if (!st) + return NULL; + + rc = sqlite3_open_v2(fname, &st->db, + SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, + NULL); + if (rc != SQLITE_OK) { + fprintf(stderr, "Unable to open DB: %s\n", + sqlite3_errmsg(st->db)); + goto err_io; + } + + rc = check_create_tbl(st); + + PREP_CHK(st->db, INS_MR, &st->stmt_ins_mr); + PREP_CHK(st->db, INS_UD, &st->stmt_ins_ud); + PREP_CHK(st->db, UPD_MR, &st->stmt_upd_mr); + + return st; +err_io: + talloc_free(st); + return NULL; +} + +void meas_db_close(struct meas_db_state *st) +{ + if (sqlite3_finalize(st->stmt_ins_mr) != SQLITE_OK) + fprintf(stderr, "DB insert measurement report finalize error: %s\n", + sqlite3_errmsg(st->db)); + if (sqlite3_finalize(st->stmt_ins_ud) != SQLITE_OK) + fprintf(stderr, "DB insert unidir finalize error: %s\n", + sqlite3_errmsg(st->db)); + if (sqlite3_finalize(st->stmt_upd_mr) != SQLITE_OK) + fprintf(stderr, "DB update measurement report finalize error: %s\n", + sqlite3_errmsg(st->db)); + if (sqlite3_close(st->db) != SQLITE_OK) + fprintf(stderr, "Unable to close DB, abandoning.\n"); + + talloc_free(st); + +} diff --git a/src/utils/meas_db.h b/src/utils/meas_db.h new file mode 100644 index 000000000..889e9022f --- /dev/null +++ b/src/utils/meas_db.h @@ -0,0 +1,17 @@ +#ifndef OPENBSC_MEAS_DB_H +#define OPENBSC_MEAS_DB_H + +struct meas_db_state; + +struct meas_db_state *meas_db_open(void *ctx, const char *fname); +void meas_db_close(struct meas_db_state *st); + +int meas_db_begin(struct meas_db_state *st); +int meas_db_commit(struct meas_db_state *st); + +int meas_db_insert(struct meas_db_state *st, const char *imsi, + const char *name, unsigned long timestamp, + const char *scenario, + const struct gsm_meas_rep *mr); + +#endif diff --git a/src/utils/meas_json.c b/src/utils/meas_json.c new file mode 100644 index 000000000..51eb6c74e --- /dev/null +++ b/src/utils/meas_json.c @@ -0,0 +1,190 @@ +/* Convert measurement report feed into JSON feed printed to stdout. + * Each measurement report is printed as a separae JSON root entry. + * All measurement reports are separated by a new line. + */ + +/* (C) 2015 by Alexander Chemeris + * With parts of code adopted from different places in OpenBSC. + * + * 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 . + * + */ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include + +static void print_meas_rep_uni_json(struct gsm_meas_rep_unidir *mru) +{ + printf("\"RXL-FULL\":%d, \"RXL-SUB\":%d, ", + rxlev2dbm(mru->full.rx_lev), + rxlev2dbm(mru->sub.rx_lev)); + printf("\"RXQ-FULL\":%d, \"RXQ-SUB\":%d", + mru->full.rx_qual, mru->sub.rx_qual); +} + +static void print_meas_rep_json(struct gsm_meas_rep *mr) +{ + int i; + + printf("\"NR\":%d", mr->nr); + + if (mr->flags & MEAS_REP_F_DL_DTX) + printf(", \"DTXd\":true"); + + printf(", \"UL_MEAS\":{"); + print_meas_rep_uni_json(&mr->ul); + printf("}"); + printf(", \"BS_POWER\":%d", mr->bs_power); + if (mr->flags & MEAS_REP_F_MS_TO) + printf(", \"MS_TO\":%d", mr->ms_timing_offset); + + if (mr->flags & MEAS_REP_F_MS_L1) { + printf(", \"L1_MS_PWR\":%d", mr->ms_l1.pwr); + printf(", \"L1_FPC\":%s", + mr->flags & MEAS_REP_F_FPC ? "true" : "false"); + printf(", \"L1_TA\":%u", mr->ms_l1.ta); + } + + if (mr->flags & MEAS_REP_F_UL_DTX) + printf(", \"DTXu\":true"); + if (mr->flags & MEAS_REP_F_BA1) + printf(", \"BA1\":true"); + if (mr->flags & MEAS_REP_F_DL_VALID) { + printf(", \"DL_MEAS\":{"); + print_meas_rep_uni_json(&mr->dl); + printf("}"); + } + + if (mr->num_cell == 7) + return; + printf(", \"NUM_NEIGH\":%u, \"NEIGH\":[", mr->num_cell); + for (i = 0; i < mr->num_cell; i++) { + struct gsm_meas_rep_cell *mrc = &mr->cell[i]; + if (i!=0) printf(", "); + printf("{\"IDX\":%u, \"ARFCN\":%u, \"BSIC\":%u, \"POWER\":%d}", + mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev)); + } + printf("]"); +} + +static void print_chan_info_json(struct meas_feed_meas *mfm) +{ + printf("\"lchan_type\":\"%s\", \"pchan_type\":\"%s\", " + "\"bts_nr\":%d, \"trx_nr\":%d, \"ts_nr\":%d, \"ss_nr\":%d", + gsm_lchant_name(mfm->lchan_type), gsm_pchan_name(mfm->pchan_type), + mfm->bts_nr, mfm->trx_nr, mfm->ts_nr, mfm->ss_nr); +} + +static void print_meas_feed_json(struct meas_feed_meas *mfm) +{ + time_t now = time(NULL); + + printf("{"); + printf("\"time\":%ld, \"imsi\":\"%s\", \"name\":\"%s\", \"scenario\":\"%s\", ", + now, mfm->imsi, mfm->name, mfm->scenario); + + switch (mfm->hdr.version) { + case 1: + printf("\"chan_info\":{"); + print_chan_info_json(mfm); + printf("}, "); + /* no break, fall to version 0 */ + case 0: + printf("\"meas_rep\":{"); + print_meas_rep_json(&mfm->mr); + printf("}"); + break; + } + + printf("}\n"); + +} + +static int handle_meas(struct msgb *msg) +{ + struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg); + + print_meas_feed_json(mfm); + + return 0; +} + +static int handle_msg(struct msgb *msg) +{ + struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg); + + if (mfh->version != MEAS_FEED_VERSION) + return -EINVAL; + + switch (mfh->msg_type) { + case MEAS_FEED_MEAS: + handle_meas(msg); + break; + default: + break; + } + return 0; +} + +static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what) +{ + int rc; + + if (what & BSC_FD_READ) { + struct msgb *msg = msgb_alloc(1024, "UDP Rx"); + + rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg)); + if (rc < 0) + return rc; + msgb_put(msg, rc); + handle_msg(msg); + msgb_free(msg); + } + + return 0; +} + +int main(int argc, char **argv) +{ + int rc; + struct osmo_fd udp_ofd; + + udp_ofd.cb = udp_fd_cb; + rc = osmo_sock_init_ofd(&udp_ofd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND); + if (rc < 0) + exit(1); + + while (1) { + osmo_select_main(0); + }; + + exit(0); +} diff --git a/src/utils/meas_pcap2db.c b/src/utils/meas_pcap2db.c new file mode 100644 index 000000000..b874ac403 --- /dev/null +++ b/src/utils/meas_pcap2db.c @@ -0,0 +1,138 @@ +/* read PCAP file with meas_feed data and write it to sqlite3 database */ + +/* (C) 2012 by Harald Welte + * + * 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 . + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +#include "meas_db.h" + +static struct meas_db_state *db; + +static void handle_mfm(const struct pcap_pkthdr *h, + const struct meas_feed_meas *mfm) +{ + const char *scenario; + + if (strlen(mfm->scenario)) + scenario = mfm->scenario; + else + scenario = NULL; + + meas_db_insert(db, mfm->imsi, mfm->name, h->ts.tv_sec, + scenario, &mfm->mr); +} + +static void pcap_cb(u_char *user, const struct pcap_pkthdr *h, + const u_char *bytes) +{ + const char *cur = bytes; + const struct iphdr *ip; + const struct udphdr *udp; + const struct meas_feed_meas *mfm; + uint16_t udplen; + + if (h->caplen < 14+20+8) + return; + + /* Check if there is IPv4 in the Ethernet */ + if (cur[12] != 0x08 || cur[13] != 0x00) + return; + + cur += 14; /* ethernet header */ + ip = (struct iphdr *) cur; + + if (ip->version != 4) + return; + cur += ip->ihl * 4; + + if (ip->protocol != IPPROTO_UDP) + return; + + udp = (struct udphdr *) cur; + + if (udp->dest != htons(8888)) + return; + + udplen = ntohs(udp->len); + if (udplen != sizeof(*udp) + sizeof(*mfm)) + return; + cur += sizeof(*udp); + + mfm = (const struct meas_feed_meas *) cur; + + handle_mfm(h, mfm); +} + +int main(int argc, char **argv) +{ + char errbuf[PCAP_ERRBUF_SIZE+1]; + char *pcap_fname, *db_fname; + pcap_t *pc; + int rc; + + if (argc < 3) { + fprintf(stderr, "You need to specify PCAP and database file\n"); + exit(2); + } + + pcap_fname = argv[1]; + db_fname = argv[2]; + + pc = pcap_open_offline(pcap_fname, errbuf); + if (!pc) { + fprintf(stderr, "Cannot open %s: %s\n", pcap_fname, errbuf); + exit(1); + } + + db = meas_db_open(NULL, db_fname); + if (!db) + exit(0); + + rc = meas_db_begin(db); + if (rc < 0) { + fprintf(stderr, "Error during BEGIN\n"); + exit(1); + } + + pcap_loop(pc, 0 , pcap_cb, NULL); + + meas_db_commit(db); + + exit(0); +} diff --git a/src/utils/meas_udp2db.c b/src/utils/meas_udp2db.c new file mode 100644 index 000000000..5032d0c3e --- /dev/null +++ b/src/utils/meas_udp2db.c @@ -0,0 +1,126 @@ +/* liesten to meas_feed on UDP and write it to sqlite3 database */ + +/* (C) 2012 by Harald Welte + * + * 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 . + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include "meas_db.h" + +static struct osmo_fd udp_ofd; +static struct meas_db_state *db; + +static int handle_msg(struct msgb *msg) +{ + struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg); + struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg); + const char *scenario; + time_t now = time(NULL); + + if (mfh->version != MEAS_FEED_VERSION) + return -EINVAL; + + if (mfh->msg_type != MEAS_FEED_MEAS) + return -EINVAL; + + if (strlen(mfm->scenario)) + scenario = mfm->scenario; + else + scenario = NULL; + + meas_db_insert(db, mfm->imsi, mfm->name, now, + scenario, &mfm->mr); + + return 0; +} + +static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what) +{ + int rc; + + if (what & BSC_FD_READ) { + struct msgb *msg = msgb_alloc(1024, "UDP Rx"); + + rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg)); + if (rc < 0) + return rc; + msgb_put(msg, rc); + handle_msg(msg); + msgb_free(msg); + } + + return 0; +} + +int main(int argc, char **argv) +{ + char *db_fname; + int rc; + + msgb_talloc_ctx_init(NULL, 0); + + if (argc < 2) { + fprintf(stderr, "You have to specify the database file name\n"); + exit(2); + } + + db_fname = argv[1]; + + udp_ofd.cb = udp_fd_cb; + rc = osmo_sock_init_ofd(&udp_ofd, AF_INET, SOCK_DGRAM, + IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND); + if (rc < 0) { + fprintf(stderr, "Unable to create UDP listen socket\n"); + exit(1); + } + + db = meas_db_open(NULL, db_fname); + if (!db) { + fprintf(stderr, "Unable to open database\n"); + exit(1); + } + + /* FIXME: timer-based BEGIN/COMMIT */ + + while (1) { + osmo_select_main(0); + }; + + meas_db_close(db); + + exit(0); +} + diff --git a/src/utils/meas_vis.c b/src/utils/meas_vis.c new file mode 100644 index 000000000..77194ded4 --- /dev/null +++ b/src/utils/meas_vis.c @@ -0,0 +1,310 @@ +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +struct ms_state_uni { + CDKSLIDER *cdk; + CDKLABEL *cdk_label; + + time_t last_update; + char label[32]; + char *_lbl[1]; +}; + + +struct ms_state { + struct llist_head list; + + char name[31+1]; + char imsi[15+1]; + struct gsm_meas_rep mr; + + struct ms_state_uni ul; + struct ms_state_uni dl; +}; + +struct state { + struct osmo_fd udp_ofd; + struct llist_head ms_list; + + CDKSCREEN *cdkscreen; + WINDOW *curses_win; + + CDKLABEL *cdk_title; + char *title; + + CDKLABEL *cdk_header; + char header[256]; +}; + +static struct state g_st; + +struct ms_state *find_ms(const char *imsi) +{ + struct ms_state *ms; + + llist_for_each_entry(ms, &g_st.ms_list, list) { + if (!strcmp(ms->imsi, imsi)) + return ms; + } + return NULL; +} + +static struct ms_state *find_alloc_ms(const char *imsi) +{ + struct ms_state *ms; + + ms = find_ms(imsi); + if (!ms) { + ms = talloc_zero(NULL, struct ms_state); + osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi)); + ms->ul._lbl[0] = ms->ul.label; + ms->dl._lbl[0] = ms->dl.label; + llist_add_tail(&ms->list, &g_st.ms_list); + } + + return ms; +} + +static int handle_meas(struct msgb *msg) +{ + struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg); + struct ms_state *ms = find_alloc_ms(mfm->imsi); + time_t now = time(NULL); + + osmo_strlcpy(ms->name, mfm->name, sizeof(ms->name)); + memcpy(&ms->mr, &mfm->mr, sizeof(ms->mr)); + ms->ul.last_update = now; + if (ms->mr.flags & MEAS_REP_F_DL_VALID) + ms->dl.last_update = now; + + /* move to head of list */ + llist_del(&ms->list); + llist_add(&ms->list, &g_st.ms_list); + + return 0; +} + +static int handle_msg(struct msgb *msg) +{ + struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg); + + if (mfh->version != MEAS_FEED_VERSION) + return -EINVAL; + + switch (mfh->msg_type) { + case MEAS_FEED_MEAS: + handle_meas(msg); + break; + default: + break; + } + + return 0; +} + +static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what) +{ + int rc; + + if (what & BSC_FD_READ) { + struct msgb *msg = msgb_alloc(1024, "UDP Rx"); + + rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg)); + if (rc < 0) + return rc; + msgb_put(msg, rc); + handle_msg(msg); + msgb_free(msg); + } + + return 0; +} + + +static void destroy_dir(struct ms_state_uni *uni) +{ + if (uni->cdk) { + destroyCDKSlider(uni->cdk); + uni->cdk = NULL; + } + if (uni->cdk_label) { + destroyCDKLabel(uni->cdk_label); + uni->cdk_label = NULL; + } +} + +#define DIR_UL 0 +#define DIR_DL 1 +static const char *dir_str[2] = { + [DIR_UL] = "UL", + [DIR_DL] = "DL", +}; + +static int colpair_by_qual(uint8_t rx_qual) +{ + if (rx_qual == 0) + return 24; + else if (rx_qual <= 4) + return 32; + else + return 16; +} + +static int colpair_by_lev(int rx_lev) +{ + if (rx_lev < -95) + return 16; + else if (rx_lev < -80) + return 32; + else + return 24; +} + + +void write_uni(struct ms_state *ms, struct ms_state_uni *msu, + struct gsm_rx_lev_qual *lq, int dir, int row) +{ + + char label[128]; + time_t now = time(NULL); + int qual_col = colpair_by_qual(lq->rx_qual); + int lev_col = colpair_by_lev(rxlev2dbm(lq->rx_lev)); + int color, pwr; + + if (dir == DIR_UL) { + pwr = ms->mr.ms_l1.pwr; + } else { + pwr = ms->mr.bs_power; + } + + color = A_REVERSE | COLOR_PAIR(lev_col) | ' '; + snprintf(label, sizeof(label), "%s %s ", ms->imsi, dir_str[dir]); + msu->cdk = newCDKSlider(g_st.cdkscreen, 0, row, NULL, label, color, + COLS-40, rxlev2dbm(lq->rx_lev), -110, -47, + 1, 2, FALSE, FALSE); + //IsVisibleObj(ms->ul.cdk) = FALSE; + snprintf(msu->label, sizeof(msu->label), "%1d %3d %2u %2d %4u", + qual_col, lq->rx_qual, qual_col, pwr, + ms->mr.ms_l1.ta, ms->mr.ms_timing_offset, + now - msu->last_update); + msu->cdk_label = newCDKLabel(g_st.cdkscreen, RIGHT, row, + msu->_lbl, 1, FALSE, FALSE); +} + +static void update_sliders(void) +{ + int num_vis_sliders = 0; + struct ms_state *ms; +#define HEADER_LINES 2 + + /* remove all sliders */ + llist_for_each_entry(ms, &g_st.ms_list, list) { + destroy_dir(&ms->ul); + destroy_dir(&ms->dl); + + } + + llist_for_each_entry(ms, &g_st.ms_list, list) { + struct gsm_rx_lev_qual *lq; + unsigned int row = HEADER_LINES + num_vis_sliders*3; + + if (ms->mr.flags & MEAS_REP_F_UL_DTX) + lq = &ms->mr.ul.sub; + else + lq = &ms->mr.ul.full; + write_uni(ms, &ms->ul, lq, DIR_UL, row); + + if (ms->mr.flags & MEAS_REP_F_DL_DTX) + lq = &ms->mr.dl.sub; + else + lq = &ms->mr.dl.full; + write_uni(ms, &ms->dl, lq, DIR_DL, row+1); + + num_vis_sliders++; + if (num_vis_sliders >= LINES/3) + break; + } + + refreshCDKScreen(g_st.cdkscreen); + +} + +const struct value_string col_strs[] = { + { COLOR_WHITE, "white" }, + { COLOR_RED, "red" }, + { COLOR_GREEN, "green" }, + { COLOR_YELLOW, "yellow" }, + { COLOR_BLUE, "blue" }, + { COLOR_MAGENTA,"magenta" }, + { COLOR_CYAN, "cyan" }, + { COLOR_BLACK, "black" }, + { 0, NULL } +}; + +int main(int argc, char **argv) +{ + int rc; + char *header[1]; + char *title[1]; + + msgb_talloc_ctx_init(NULL, 0); + + printf("sizeof(gsm_meas_rep)=%u\n", sizeof(struct gsm_meas_rep)); + printf("sizeof(meas_feed_meas)=%u\n", sizeof(struct meas_feed_meas)); + + INIT_LLIST_HEAD(&g_st.ms_list); + g_st.curses_win = initscr(); + g_st.cdkscreen = initCDKScreen(g_st.curses_win); + initCDKColor(); + + g_st.title = "OpenBSC link quality monitor"; + title[0] = g_st.title; + g_st.cdk_title = newCDKLabel(g_st.cdkscreen, CENTER, 0, title, 1, FALSE, FALSE); + + snprintf(g_st.header, sizeof(g_st.header), "Q Pwr TA TO Time"); + header[0] = g_st.header; + g_st.cdk_header = newCDKLabel(g_st.cdkscreen, RIGHT, 1, header, 1, FALSE, FALSE); + +#if 0 + int i; + for (i = 0; i < 64; i++) { + short f, b; + pair_content(i, &f, &b); + attron(COLOR_PAIR(i)); + printw("%u: %u (%s) ", i, f, get_value_string(col_strs, f)); + printw("%u (%s)\n\r", b, get_value_string(col_strs, b)); + } + refresh(); + getch(); + exit(0); +#endif + + g_st.udp_ofd.cb = udp_fd_cb; + rc = osmo_sock_init_ofd(&g_st.udp_ofd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND); + if (rc < 0) + exit(1); + + while (1) { + osmo_select_main(0); + update_sliders(); + }; + + exit(0); +} diff --git a/src/utils/smpp_mirror.c b/src/utils/smpp_mirror.c new file mode 100644 index 000000000..95df5f2a6 --- /dev/null +++ b/src/utils/smpp_mirror.c @@ -0,0 +1,329 @@ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* FIXME: merge with smpp_smsc.c */ +#define SMPP_SYS_ID_LEN 16 +enum esme_read_state { + READ_ST_IN_LEN = 0, + READ_ST_IN_MSG = 1, +}; +/* FIXME: merge with smpp_smsc.c */ + +struct esme { + struct osmo_fd ofd; + + uint32_t own_seq_nr; + + struct osmo_wqueue wqueue; + enum esme_read_state read_state; + uint32_t read_len; + uint32_t read_idx; + struct msgb *read_msg; + + uint8_t smpp_version; + char system_id[SMPP_SYS_ID_LEN+1]; + char password[SMPP_SYS_ID_LEN+1]; +}; + +/* FIXME: merge with smpp_smsc.c */ +#define SMPP34_UNPACK(rc, type, str, data, len) \ + memset(str, 0, sizeof(*str)); \ + rc = smpp34_unpack(type, str, data, len) +#define INIT_RESP(type, resp, req) { \ + memset((resp), 0, sizeof(*(resp))); \ + (resp)->command_length = 0; \ + (resp)->command_id = type; \ + (resp)->command_status = ESME_ROK; \ + (resp)->sequence_number = (req)->sequence_number; \ +} +#define PACK_AND_SEND(esme, ptr) pack_and_send(esme, (ptr)->command_id, ptr) +static inline uint32_t smpp_msgb_cmdid(struct msgb *msg) +{ + uint8_t *tmp = msgb_data(msg) + 4; + return ntohl(*(uint32_t *)tmp); +} +static uint32_t esme_inc_seq_nr(struct esme *esme) +{ + esme->own_seq_nr++; + if (esme->own_seq_nr > 0x7fffffff) + esme->own_seq_nr = 1; + + return esme->own_seq_nr; +} +static int pack_and_send(struct esme *esme, uint32_t type, void *ptr) +{ + struct msgb *msg = msgb_alloc(4096, "SMPP_Tx"); + int rc, rlen; + if (!msg) + return -ENOMEM; + + rc = smpp34_pack(type, msg->tail, msgb_tailroom(msg), &rlen, ptr); + if (rc != 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Error during smpp34_pack(): %s\n", + esme->system_id, smpp34_strerror); + msgb_free(msg); + return -EINVAL; + } + msgb_put(msg, rlen); + + if (osmo_wqueue_enqueue(&esme->wqueue, msg) != 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Write queue full. Dropping message\n", + esme->system_id); + msgb_free(msg); + return -EAGAIN; + } + return 0; +} +/* FIXME: merge with smpp_smsc.c */ + + +static int smpp_handle_deliver(struct esme *esme, struct msgb *msg) +{ + struct deliver_sm_t deliver; + struct deliver_sm_resp_t deliver_r; + struct submit_sm_t submit; + int rc; + + memset(&deliver, 0, sizeof(deliver)); + SMPP34_UNPACK(rc, DELIVER_SM, &deliver, msgb_data(msg), msgb_length(msg)); + if (rc < 0) + return rc; + + INIT_RESP(DELIVER_SM_RESP, &deliver_r, &deliver); + + PACK_AND_SEND(esme, &deliver_r); + + memset(&submit, 0, sizeof(submit)); + submit.command_id = SUBMIT_SM; + submit.command_status = ESME_ROK; + submit.sequence_number = esme_inc_seq_nr(esme); + + submit.dest_addr_ton = deliver.source_addr_ton; + submit.dest_addr_npi = deliver.source_addr_npi; + memcpy(submit.destination_addr, deliver.source_addr, + OSMO_MIN(sizeof(submit.destination_addr), + sizeof(deliver.source_addr))); + + submit.source_addr_ton = deliver.dest_addr_ton; + submit.source_addr_npi = deliver.dest_addr_npi; + memcpy(submit.source_addr, deliver.destination_addr, + OSMO_MIN(sizeof(submit.source_addr), + sizeof(deliver.destination_addr))); + + submit.esm_class = deliver.esm_class; + submit.protocol_id = deliver.protocol_id; + submit.priority_flag = deliver.priority_flag; + memcpy(submit.schedule_delivery_time, deliver.schedule_delivery_time, + OSMO_MIN(sizeof(submit.schedule_delivery_time), + sizeof(deliver.schedule_delivery_time))); + memcpy(submit.validity_period, deliver.validity_period, + OSMO_MIN(sizeof(submit.validity_period), + sizeof(deliver.validity_period))); + submit.registered_delivery = deliver.registered_delivery; + submit.replace_if_present_flag = deliver.replace_if_present_flag; + submit.data_coding = deliver.data_coding; + submit.sm_default_msg_id = deliver.sm_default_msg_id; + submit.sm_length = deliver.sm_length; + memcpy(submit.short_message, deliver.short_message, + OSMO_MIN(sizeof(submit.short_message), + sizeof(deliver.short_message))); + /* FIXME: TLV? */ + + return PACK_AND_SEND(esme, &submit); +} + +static int bind_transceiver(struct esme *esme) +{ + struct bind_transceiver_t bind; + + memset(&bind, 0, sizeof(bind)); + bind.command_id = BIND_TRANSCEIVER; + bind.sequence_number = esme_inc_seq_nr(esme); + snprintf((char *)bind.system_id, sizeof(bind.system_id), "%s", esme->system_id); + snprintf((char *)bind.password, sizeof(bind.password), "%s", esme->password); + snprintf((char *)bind.system_type, sizeof(bind.system_type), "mirror"); + bind.interface_version = esme->smpp_version; + + return PACK_AND_SEND(esme, &bind); +} + +static int smpp_pdu_rx(struct esme *esme, struct msgb *msg) +{ + uint32_t cmd_id = smpp_msgb_cmdid(msg); + int rc; + + switch (cmd_id) { + case DELIVER_SM: + rc = smpp_handle_deliver(esme, msg); + break; + default: + LOGP(DSMPP, LOGL_NOTICE, "unhandled case %d\n", cmd_id); + rc = 0; + break; + } + + return rc; +} + +/* FIXME: merge with smpp_smsc.c */ +static int esme_read_cb(struct osmo_fd *ofd) +{ + struct esme *esme = ofd->data; + uint32_t len; + uint8_t *lenptr = (uint8_t *) &len; + uint8_t *cur; + struct msgb *msg; + int rdlen; + int rc; + + switch (esme->read_state) { + case READ_ST_IN_LEN: + rdlen = sizeof(uint32_t) - esme->read_idx; + rc = read(ofd->fd, lenptr + esme->read_idx, rdlen); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %d\n", + esme->system_id, rc); + } else if (rc == 0) { + goto dead_socket; + } else + esme->read_idx += rc; + if (esme->read_idx >= sizeof(uint32_t)) { + esme->read_len = ntohl(len); + msg = msgb_alloc(esme->read_len, "SMPP Rx"); + if (!msg) + return -ENOMEM; + esme->read_msg = msg; + cur = msgb_put(msg, sizeof(uint32_t)); + memcpy(cur, lenptr, sizeof(uint32_t)); + esme->read_state = READ_ST_IN_MSG; + esme->read_idx = sizeof(uint32_t); + } + break; + case READ_ST_IN_MSG: + msg = esme->read_msg; + rdlen = esme->read_len - esme->read_idx; + rc = read(ofd->fd, msg->tail, OSMO_MIN(rdlen, msgb_tailroom(msg))); + if (rc < 0) { + LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %d\n", + esme->system_id, rc); + } else if (rc == 0) { + goto dead_socket; + } else { + esme->read_idx += rc; + msgb_put(msg, rc); + } + + if (esme->read_idx >= esme->read_len) { + rc = smpp_pdu_rx(esme, esme->read_msg); + esme->read_msg = NULL; + esme->read_idx = 0; + esme->read_len = 0; + esme->read_state = READ_ST_IN_LEN; + } + break; + } + + return 0; +dead_socket: + msgb_free(esme->read_msg); + osmo_fd_unregister(&esme->wqueue.bfd); + close(esme->wqueue.bfd.fd); + esme->wqueue.bfd.fd = -1; + exit(2342); + + return 0; +} + +static int esme_write_cb(struct osmo_fd *ofd, struct msgb *msg) +{ + struct esme *esme = ofd->data; + int rc; + + rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); + if (rc == 0) { + osmo_fd_unregister(&esme->wqueue.bfd); + close(esme->wqueue.bfd.fd); + esme->wqueue.bfd.fd = -1; + exit(99); + } else if (rc < msgb_length(msg)) { + LOGP(DSMPP, LOGL_ERROR, "[%s] Short write\n", esme->system_id); + return 0; + } + + return 0; +} + +static int smpp_esme_init(struct esme *esme, const char *host, uint16_t port) +{ + int rc; + + if (port == 0) + port = 2775; + + esme->own_seq_nr = rand(); + esme_inc_seq_nr(esme); + osmo_wqueue_init(&esme->wqueue, 10); + esme->wqueue.bfd.data = esme; + esme->wqueue.read_cb = esme_read_cb; + esme->wqueue.write_cb = esme_write_cb; + + rc = osmo_sock_init_ofd(&esme->wqueue.bfd, AF_UNSPEC, SOCK_STREAM, + IPPROTO_TCP, host, port, OSMO_SOCK_F_CONNECT); + if (rc < 0) + return rc; + + return bind_transceiver(esme); +} + + +int main(int argc, char **argv) +{ + struct esme esme; + char *host = "localhost"; + int port = 0; + int rc; + + msgb_talloc_ctx_init(NULL, 0); + + memset(&esme, 0, sizeof(esme)); + + osmo_init_logging(&log_info); + + snprintf((char *) esme.system_id, sizeof(esme.system_id), "mirror"); + snprintf((char *) esme.password, sizeof(esme.password), "mirror"); + esme.smpp_version = 0x34; + + if (argc >= 2) + host = argv[1]; + if (argc >= 3) + port = atoi(argv[2]); + + rc = smpp_esme_init(&esme, host, port); + if (rc < 0) + exit(1); + + while (1) { + osmo_select_main(0); + } + + exit(0); +} -- cgit v1.2.3