diff options
Diffstat (limited to 'src/utils')
-rw-r--r-- | src/utils/Makefile.am | 147 | ||||
-rw-r--r-- | src/utils/bs11_config.c | 953 | ||||
-rw-r--r-- | src/utils/isdnsync.c | 189 | ||||
-rw-r--r-- | src/utils/meas_db.c | 330 | ||||
-rw-r--r-- | src/utils/meas_db.h | 17 | ||||
-rw-r--r-- | src/utils/meas_json.c | 190 | ||||
-rw-r--r-- | src/utils/meas_pcap2db.c | 138 | ||||
-rw-r--r-- | src/utils/meas_udp2db.c | 126 | ||||
-rw-r--r-- | src/utils/meas_vis.c | 310 | ||||
-rw-r--r-- | src/utils/smpp_mirror.c | 329 |
10 files changed, 2729 insertions, 0 deletions
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 <laforge@gnumonks.org> + * All Rights Reserved + * + * This software is based on ideas (but not code) of BS11Config + * (C) 2009 by Dieter Spaar <spaar@mirider.augusta.de> + * + * 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 <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <getopt.h> +#include <fcntl.h> +#include <signal.h> + +#include <sys/stat.h> + +#include <openbsc/common_bsc.h> +#include <openbsc/abis_nm.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/tlv.h> +#include <openbsc/debug.h> +#include <osmocom/core/select.h> +#include <openbsc/rs232.h> +#include <osmocom/core/application.h> +#include <osmocom/core/talloc.h> +#include <osmocom/abis/abis.h> +#include <osmocom/abis/e1_input.h> + +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 </dev/ttyXXX>\t\tSpecify serial port\n"); + printf("\t-s --software <file>\t\tSpecify Software file\n"); + printf("\t-S --safety <file>\t\tSpecify Safety Load file\n"); + printf("\t-d --delay <ms>\t\t\tSpecify delay in milliseconds\n"); + printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n"); + printf("\t-w --win-size <num>\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 <value>\tSet the PLL set value\n"); + printf("\tpll-workvalue <value>\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 <jolly@eversberg.eu> + * + * 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 <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/socket.h> +#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 <card>\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 <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 <stdint.h> +#include <errno.h> +#include <string.h> + +#include <sqlite3.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/gsm_utils.h> +#include <openbsc/meas_rep.h> + +#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 <Alexander.Chemeris@fairwaves.co> + * 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 <http://www.gnu.org/licenses/>. + * + */ +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +#include <netinet/in.h> + +#include <osmocom/core/socket.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/select.h> + +#include <osmocom/gsm/gsm_utils.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_data_shared.h> +#include <openbsc/meas_feed.h> + +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 <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 <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/udp.h> + +#include <osmocom/core/socket.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/select.h> +#include <osmocom/core/talloc.h> + +#include <osmocom/gsm/gsm_utils.h> + +#include <openbsc/meas_feed.h> + +#include <pcap/pcap.h> + +#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 <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 <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> + +#include <netinet/in.h> + +#include <osmocom/core/socket.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/select.h> +#include <osmocom/core/talloc.h> + +#include <osmocom/gsm/gsm_utils.h> + +#include <openbsc/meas_feed.h> + +#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 <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include <netinet/in.h> + +#include <cdk/cdk.h> + +#include <osmocom/core/socket.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/select.h> +#include <osmocom/core/talloc.h> + +#include <osmocom/gsm/gsm_utils.h> + +#include <openbsc/meas_feed.h> + +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), "</%d>%1d<!%d> %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 <stdio.h> +#include <unistd.h> +#include <stdint.h> +#include <errno.h> +#include <string.h> + +#include <netinet/in.h> + +#include <smpp34.h> +#include <smpp34_structs.h> +#include <smpp34_params.h> + +#include <osmocom/core/application.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/select.h> +#include <osmocom/core/socket.h> +#include <osmocom/core/write_queue.h> + +#include <openbsc/debug.h> + +/* 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); +} |