diff options
Diffstat (limited to 'src/libcommon')
-rw-r--r-- | src/libcommon/Makefile.am | 47 | ||||
-rw-r--r-- | src/libcommon/bsc_version.c | 30 | ||||
-rw-r--r-- | src/libcommon/common_vty.c | 145 | ||||
-rw-r--r-- | src/libcommon/debug.c | 235 | ||||
-rw-r--r-- | src/libcommon/gsm_data.c | 473 | ||||
-rw-r--r-- | src/libcommon/gsm_data_shared.c | 849 | ||||
-rw-r--r-- | src/libcommon/gsm_subscriber_base.c | 163 | ||||
-rw-r--r-- | src/libcommon/gsup_client.c | 341 | ||||
-rw-r--r-- | src/libcommon/gsup_test_client.c | 298 | ||||
-rw-r--r-- | src/libcommon/oap_client.c | 280 | ||||
-rw-r--r-- | src/libcommon/socket.c | 111 | ||||
-rw-r--r-- | src/libcommon/talloc_ctx.c | 56 |
12 files changed, 3028 insertions, 0 deletions
diff --git a/src/libcommon/Makefile.am b/src/libcommon/Makefile.am new file mode 100644 index 000000000..0b258c08a --- /dev/null +++ b/src/libcommon/Makefile.am @@ -0,0 +1,47 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + -I$(top_builddir) \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOVTY_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(COVERAGE_CFLAGS) \ + $(NULL) + +noinst_LIBRARIES = \ + libcommon.a \ + $(NULL) + +libcommon_a_SOURCES = \ + bsc_version.c \ + common_vty.c \ + debug.c \ + gsm_data.c \ + gsm_data_shared.c \ + gsup_client.c \ + oap_client.c \ + socket.c \ + talloc_ctx.c \ + gsm_subscriber_base.c \ + $(NULL) + +noinst_PROGRAMS = \ + gsup_test_client \ + $(NULL) + +gsup_test_client_SOURCES = \ + gsup_test_client.c \ + $(NULL) +gsup_test_client_LDADD = \ + libcommon.a \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + -lrt \ + $(NULL) diff --git a/src/libcommon/bsc_version.c b/src/libcommon/bsc_version.c new file mode 100644 index 000000000..f0369bffe --- /dev/null +++ b/src/libcommon/bsc_version.c @@ -0,0 +1,30 @@ +/* Hold the copyright and version string */ +/* (C) 2010-2016 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 "../../bscconfig.h" + +const char *openbsc_copyright = + "Copyright (C) 2008-2016 Harald Welte, Holger Freyther\r\n" + "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" + "Dieter Spaar, Andreas Eversberg, Sylvain Munaut, Neels Hofmeyr\r\n\r\n" + "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n" + "This is free software: you are free to change and redistribute it.\r\n" + "There is NO WARRANTY, to the extent permitted by law.\r\n"; + + diff --git a/src/libcommon/common_vty.c b/src/libcommon/common_vty.c new file mode 100644 index 000000000..6e1c10b00 --- /dev/null +++ b/src/libcommon/common_vty.c @@ -0,0 +1,145 @@ +/* OpenBSC VTY common helpers */ +/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2009-2010 by Holger Hans Peter Freyther + * 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 <stdlib.h> +#include <string.h> + +#include <osmocom/core/talloc.h> + +#include <openbsc/vty.h> +#include <openbsc/gsm_data.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/bsc_nat.h> +#include <openbsc/abis_om2000.h> + +#include <osmocom/vty/telnet_interface.h> +#include <osmocom/vty/command.h> +#include <osmocom/vty/buffer.h> +#include <osmocom/vty/vty.h> + + +int bsc_vty_go_parent(struct vty *vty) +{ + switch (vty->node) { + case GSMNET_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + case BTS_NODE: + vty->node = GSMNET_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts *bts = vty->index; + vty->index = bts->network; + vty->index_sub = NULL; + } + break; + case TRX_NODE: + vty->node = BTS_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts_trx *trx = vty->index; + vty->index = trx->bts; + vty->index_sub = &trx->bts->description; + } + break; + case TS_NODE: + vty->node = TRX_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts_trx_ts *ts = vty->index; + vty->index = ts->trx; + vty->index_sub = &ts->trx->description; + } + break; + case OML_NODE: + case OM2K_NODE: + vty->node = ENABLE_NODE; + /* NOTE: this only works because it's not part of the config + * tree, where outer commands are searched via vty_go_parent() + * and only (!) executed when a matching one is found. + */ + talloc_free(vty->index); + vty->index = NULL; + break; + case OM2K_CON_GROUP_NODE: + vty->node = BTS_NODE; + { + struct con_group *cg = vty->index; + struct gsm_bts *bts = cg->bts; + vty->index = bts; + vty->index_sub = &bts->description; + } + break; + case NAT_BSC_NODE: + vty->node = NAT_NODE; + { + struct bsc_config *bsc_config = vty->index; + vty->index = bsc_config->nat; + } + break; + case PGROUP_NODE: + vty->node = NAT_NODE; + vty->index = NULL; + break; + case TRUNK_NODE: + vty->node = MGCP_NODE; + vty->index = NULL; + break; + case SMPP_ESME_NODE: + vty->node = SMPP_NODE; + vty->index = NULL; + break; + case SMPP_NODE: + case MGCP_NODE: + case GBPROXY_NODE: + case SGSN_NODE: + case NAT_NODE: + case BSC_NODE: + case MSC_NODE: + case MNCC_INT_NODE: + case NITB_NODE: + default: + if (bsc_vty_is_config_node(vty, vty->node)) + vty->node = CONFIG_NODE; + else + vty->node = ENABLE_NODE; + + vty->index = NULL; + } + + return vty->node; +} + +int bsc_vty_is_config_node(struct vty *vty, int node) +{ + switch (node) { + /* add items that are not config */ + case OML_NODE: + case OM2K_NODE: + case SUBSCR_NODE: + case CONFIG_NODE: + return 0; + + default: + return 1; + } +} diff --git a/src/libcommon/debug.c b/src/libcommon/debug.c new file mode 100644 index 000000000..f29f1683d --- /dev/null +++ b/src/libcommon/debug.c @@ -0,0 +1,235 @@ +/* OpenBSC Debugging/Logging support code */ + +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org> + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <time.h> +#include <errno.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> +#include <osmocom/gprs/gprs_msgb.h> +#include <openbsc/gsm_data.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/debug.h> + +/* default categories */ +static const struct log_info_cat default_categories[] = { + [DRLL] = { + .name = "DRLL", + .description = "A-bis Radio Link Layer (RLL)", + .color = "\033[1;31m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DCC] = { + .name = "DCC", + .description = "Layer3 Call Control (CC)", + .color = "\033[1;32m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DMM] = { + .name = "DMM", + .description = "Layer3 Mobility Management (MM)", + .color = "\033[1;33m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DRR] = { + .name = "DRR", + .description = "Layer3 Radio Resource (RR)", + .color = "\033[1;34m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DRSL] = { + .name = "DRSL", + .description = "A-bis Radio Siganlling Link (RSL)", + .color = "\033[1;35m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DNM] = { + .name = "DNM", + .description = "A-bis Network Management / O&M (NM/OML)", + .color = "\033[1;36m", + .enabled = 1, .loglevel = LOGL_INFO, + }, + [DMNCC] = { + .name = "DMNCC", + .description = "MNCC API for Call Control application", + .color = "\033[1;39m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DPAG] = { + .name = "DPAG", + .description = "Paging Subsystem", + .color = "\033[1;38m", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DMEAS] = { + .name = "DMEAS", + .description = "Radio Measurement Processing", + .enabled = 0, .loglevel = LOGL_NOTICE, + }, + [DSCCP] = { + .name = "DSCCP", + .description = "SCCP Protocol", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DMSC] = { + .name = "DMSC", + .description = "Mobile Switching Center", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DMGCP] = { + .name = "DMGCP", + .description = "Media Gateway Control Protocol", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DHO] = { + .name = "DHO", + .description = "Hand-Over", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DDB] = { + .name = "DDB", + .description = "Database Layer", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DREF] = { + .name = "DREF", + .description = "Reference Counting", + .enabled = 0, .loglevel = LOGL_NOTICE, + }, + [DGPRS] = { + .name = "DGPRS", + .description = "GPRS Packet Service", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DNS] = { + .name = "DNS", + .description = "GPRS Network Service (NS)", + .enabled = 1, .loglevel = LOGL_INFO, + }, + [DBSSGP] = { + .name = "DBSSGP", + .description = "GPRS BSS Gateway Protocol (BSSGP)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DLLC] = { + .name = "DLLC", + .description = "GPRS Logical Link Control Protocol (LLC)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DSNDCP] = { + .name = "DSNDCP", + .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DNAT] = { + .name = "DNAT", + .description = "GSM 08.08 NAT/Multiplexer", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DCTRL] = { + .name = "DCTRL", + .description = "Control interface", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DSMPP] = { + .name = "DSMPP", + .description = "SMPP interface for external SMS apps", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DFILTER] = { + .name = "DFILTER", + .description = "BSC/NAT IMSI based filtering", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DRANAP] = { + .name = "DRANAP", + .description = "Radio Access Network Application Part Protocol", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DSUA] = { + .name = "DSUA", + .description = "SCCP User Adaptation Protocol", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, + [DPCU] = { + .name = "DPCU", + .description = "PCU Interface", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, +}; + +static int filter_fn(const struct log_context *ctx, struct log_target *tar) +{ + const struct gsm_subscriber *subscr = ctx->ctx[LOG_CTX_VLR_SUBSCR]; + const struct bsc_subscr *bsub = ctx->ctx[LOG_CTX_BSC_SUBSCR]; + const struct gprs_nsvc *nsvc = ctx->ctx[LOG_CTX_GB_NSVC]; + const struct gprs_nsvc *bvc = ctx->ctx[LOG_CTX_GB_BVC]; + + if ((tar->filter_map & (1 << LOG_FLT_VLR_SUBSCR)) != 0 + && subscr && subscr == tar->filter_data[LOG_FLT_VLR_SUBSCR]) + return 1; + + if ((tar->filter_map & (1 << LOG_FLT_BSC_SUBSCR)) != 0 + && bsub && bsub == tar->filter_data[LOG_FLT_BSC_SUBSCR]) + return 1; + + /* Filter on the NS Virtual Connection */ + if ((tar->filter_map & (1 << LOG_FLT_GB_NSVC)) != 0 + && nsvc && (nsvc == tar->filter_data[LOG_FLT_GB_NSVC])) + return 1; + + /* Filter on the NS Virtual Connection */ + if ((tar->filter_map & (1 << LOG_FLT_GB_BVC)) != 0 + && bvc && (bvc == tar->filter_data[LOG_FLT_GB_BVC])) + return 1; + + return 0; +} + +const struct log_info log_info = { + .filter_fn = filter_fn, + .cat = default_categories, + .num_cat = ARRAY_SIZE(default_categories), +}; + +void log_set_filter_vlr_subscr(struct log_target *target, + struct gsm_subscriber *vlr_subscr) +{ + struct gsm_subscriber **fsub = (void*)&target->filter_data[LOG_FLT_VLR_SUBSCR]; + + /* free the old data */ + if (*fsub) { + subscr_put(*fsub); + *fsub = NULL; + } + + if (vlr_subscr) { + target->filter_map |= (1 << LOG_FLT_VLR_SUBSCR); + *fsub = subscr_get(vlr_subscr); + } else + target->filter_map &= ~(1 << LOG_FLT_VLR_SUBSCR); +} diff --git a/src/libcommon/gsm_data.c b/src/libcommon/gsm_data.c new file mode 100644 index 000000000..db7de082d --- /dev/null +++ b/src/libcommon/gsm_data.c @@ -0,0 +1,473 @@ +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <stdbool.h> +#include <netinet/in.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/abis_nm.h> +#include <osmocom/core/statistics.h> +#include <osmocom/gsm/protocol/gsm_04_08.h> + +#include <openbsc/gsm_data.h> +#include <openbsc/bsc_msc_data.h> +#include <openbsc/abis_nm.h> + +void *tall_bsc_ctx; + +static LLIST_HEAD(bts_models); + +void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr, + uint8_t e1_ts, uint8_t e1_ts_ss) +{ + ts->e1_link.e1_nr = e1_nr; + ts->e1_link.e1_ts = e1_ts; + ts->e1_link.e1_ts_ss = e1_ts_ss; +} + +static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type) +{ + struct gsm_bts_model *model; + + llist_for_each_entry(model, &bts_models, list) { + if (model->type == type) + return model; + } + + return NULL; +} + +int gsm_bts_model_register(struct gsm_bts_model *model) +{ + if (bts_model_find(model->type)) + return -EEXIST; + + tlv_def_patch(&model->nm_att_tlvdef, &abis_nm_att_tlvdef); + llist_add_tail(&model->list, &bts_models); + return 0; +} + +/* Get reference to a neighbor cell on a given BCCH ARFCN */ +struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts, + uint16_t arfcn, uint8_t bsic) +{ + struct gsm_bts *neigh; + /* FIXME: use some better heuristics here to determine which cell + * using this ARFCN really is closest to the target cell. For + * now we simply assume that each ARFCN will only be used by one + * cell */ + + llist_for_each_entry(neigh, &bts->network->bts_list, list) { + if (neigh->c0->arfcn == arfcn && + neigh->bsic == bsic) + return neigh; + } + + return NULL; +} + +const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = { + { GSM_BTS_TYPE_UNKNOWN, "Unknown BTS Type" }, + { GSM_BTS_TYPE_BS11, "Siemens BTS (BS-11 or compatible)" }, + { GSM_BTS_TYPE_NANOBTS, "ip.access nanoBTS or compatible" }, + { GSM_BTS_TYPE_RBS2000, "Ericsson RBS2000 Series" }, + { GSM_BTS_TYPE_NOKIA_SITE, "Nokia {Metro,Ultra,In}Site" }, + { GSM_BTS_TYPE_OSMOBTS, "sysmocom sysmoBTS" }, + { 0, NULL } +}; + +struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr) +{ + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->nr == nr) + return trx; + } + return NULL; +} + +/* Search for a BTS in the given Location Area; optionally start searching + * with start_bts (for continuing to search after the first result) */ +struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, + struct gsm_bts *start_bts) +{ + int i; + struct gsm_bts *bts; + int skip = 0; + + if (start_bts) + skip = 1; + + for (i = 0; i < net->num_bts; i++) { + bts = gsm_bts_num(net, i); + + if (skip) { + if (start_bts == bts) + skip = 0; + continue; + } + + if (lac == GSM_LAC_RESERVED_ALL_BTS || bts->location_area_code == lac) + return bts; + } + return NULL; +} + +static const struct value_string auth_policy_names[] = { + { GSM_AUTH_POLICY_CLOSED, "closed" }, + { GSM_AUTH_POLICY_ACCEPT_ALL, "accept-all" }, + { GSM_AUTH_POLICY_TOKEN, "token" }, + { GSM_AUTH_POLICY_REGEXP, "regexp" }, + { 0, NULL } +}; + +enum gsm_auth_policy gsm_auth_policy_parse(const char *arg) +{ + return get_string_value(auth_policy_names, arg); +} + +const char *gsm_auth_policy_name(enum gsm_auth_policy policy) +{ + return get_value_string(auth_policy_names, policy); +} + +static const struct value_string rrlp_mode_names[] = { + { RRLP_MODE_NONE, "none" }, + { RRLP_MODE_MS_BASED, "ms-based" }, + { RRLP_MODE_MS_PREF, "ms-preferred" }, + { RRLP_MODE_ASS_PREF, "ass-preferred" }, + { 0, NULL } +}; + +enum rrlp_mode rrlp_mode_parse(const char *arg) +{ + return get_string_value(rrlp_mode_names, arg); +} + +const char *rrlp_mode_name(enum rrlp_mode mode) +{ + return get_value_string(rrlp_mode_names, mode); +} + +static const struct value_string bts_gprs_mode_names[] = { + { BTS_GPRS_NONE, "none" }, + { BTS_GPRS_GPRS, "gprs" }, + { BTS_GPRS_EGPRS, "egprs" }, + { 0, NULL } +}; + +enum bts_gprs_mode bts_gprs_mode_parse(const char *arg, int *valid) +{ + int rc; + + rc = get_string_value(bts_gprs_mode_names, arg); + if (valid) + *valid = rc != -EINVAL; + return rc; +} + +const char *bts_gprs_mode_name(enum bts_gprs_mode mode) +{ + return get_value_string(bts_gprs_mode_names, mode); +} + +int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode) +{ + if (mode != BTS_GPRS_NONE && + !gsm_btsmodel_has_feature(bts->model, BTS_FEAT_GPRS)) { + return 0; + } + if (mode == BTS_GPRS_EGPRS && + !gsm_btsmodel_has_feature(bts->model, BTS_FEAT_EGPRS)) { + return 0; + } + + return 1; +} + +struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan) +{ + struct gsm_meas_rep *meas_rep; + + meas_rep = &lchan->meas_rep[lchan->meas_rep_idx]; + memset(meas_rep, 0, sizeof(*meas_rep)); + meas_rep->lchan = lchan; + lchan->meas_rep_idx = (lchan->meas_rep_idx + 1) + % ARRAY_SIZE(lchan->meas_rep); + + return meas_rep; +} + +int gsm_btsmodel_set_feature(struct gsm_bts_model *model, enum gsm_bts_features feat) +{ + OSMO_ASSERT(_NUM_BTS_FEAT < MAX_BTS_FEATURES); + return bitvec_set_bit_pos(&model->features, feat, 1); +} + +bool gsm_btsmodel_has_feature(struct gsm_bts_model *model, enum gsm_bts_features feat) +{ + OSMO_ASSERT(_NUM_BTS_FEAT < MAX_BTS_FEATURES); + return bitvec_get_bit_pos(&model->features, feat); +} + +int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) +{ + struct gsm_bts_model *model; + + model = bts_model_find(type); + if (!model) + return -EINVAL; + + bts->type = type; + bts->model = model; + + if (model->start && !model->started) { + int ret = model->start(bts->network); + if (ret < 0) + return ret; + + model->started = true; + } + + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + case GSM_BTS_TYPE_OSMOBTS: + /* Set the default OML Stream ID to 0xff */ + bts->oml_tei = 0xff; + bts->c0->nominal_power = 23; + break; + case GSM_BTS_TYPE_RBS2000: + INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups); + INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups); + break; + case GSM_BTS_TYPE_BS11: + case GSM_BTS_TYPE_UNKNOWN: + case GSM_BTS_TYPE_NOKIA_SITE: + /* Set default BTS reset timer */ + bts->nokia.bts_reset_timer_cnf = 15; + case _NUM_GSM_BTS_TYPE: + break; + } + + return 0; +} + +struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type, + uint8_t bsic) +{ + struct gsm_bts_model *model = bts_model_find(type); + struct gsm_bts *bts; + + if (!model && type != GSM_BTS_TYPE_UNKNOWN) + return NULL; + + bts = gsm_bts_alloc(net); + if (!bts) + return NULL; + + bts->network = net; + bts->nr = net->num_bts++; + bts->type = type; + bts->model = model; + bts->bsic = bsic; + bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED; + bts->dtxd = false; + bts->gprs.ctrl_ack_type_use_block = true; /* use RLC/MAC control block */ + bts->neigh_list_manual_mode = 0; + bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */ + bts->si_common.cell_sel_par.rxlev_acc_min = 0; + bts->si_common.si2quater_neigh_list.arfcn = bts->si_common.data.earfcn_list; + bts->si_common.si2quater_neigh_list.meas_bw = bts->si_common.data.meas_bw_list; + bts->si_common.si2quater_neigh_list.length = MAX_EARFCN_LIST; + bts->si_common.si2quater_neigh_list.thresh_hi = 0; + osmo_earfcn_init(&bts->si_common.si2quater_neigh_list); + bts->si_common.neigh_list.data = bts->si_common.data.neigh_list; + bts->si_common.neigh_list.data_len = + sizeof(bts->si_common.data.neigh_list); + bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list; + bts->si_common.si5_neigh_list.data_len = + sizeof(bts->si_common.data.si5_neigh_list); + bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc; + bts->si_common.cell_alloc.data_len = + sizeof(bts->si_common.data.cell_alloc); + bts->si_common.rach_control.re = 1; /* no re-establishment */ + bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */ + bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */ + bts->si_common.rach_control.t2 = 4; /* no emergency calls */ + bts->si_common.chan_desc.att = 1; /* attachment required */ + bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* paging frames */ + bts->si_common.chan_desc.bs_ag_blks_res = 1; /* reserved AGCH blocks */ + bts->si_common.chan_desc.t3212 = 5; /* Use 30 min periodic update interval as sane default */ + gsm_bts_set_radio_link_timeout(bts, 32); /* Use RADIO LINK TIMEOUT of 32 */ + + llist_add_tail(&bts->list, &net->bts_list); + + INIT_LLIST_HEAD(&bts->abis_queue); + + INIT_LLIST_HEAD(&bts->loc_list); + + return bts; +} + +void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts) +{ + raid->mcc = bts->network->country_code; + raid->mnc = bts->network->network_code; + raid->lac = bts->location_area_code; + raid->rac = bts->gprs.rac; +} + +int gsm48_ra_id_by_bts(uint8_t *buf, struct gsm_bts *bts) +{ + struct gprs_ra_id raid; + + gprs_ra_id_by_bts(&raid, bts); + + return gsm48_construct_ra(buf, &raid); +} + +int gsm_parse_reg(void *ctx, regex_t *reg, char **str, int argc, const char **argv) +{ + int ret; + + ret = 0; + if (*str) { + talloc_free(*str); + *str = NULL; + } + regfree(reg); + + if (argc > 0) { + *str = talloc_strdup(ctx, argv[0]); + ret = regcomp(reg, argv[0], 0); + + /* handle compilation failures */ + if (ret != 0) { + talloc_free(*str); + *str = NULL; + } + } + + return ret; +} + +/* Assume there are only 256 possible bts */ +osmo_static_assert(sizeof(((struct gsm_bts *) 0)->nr) == 1, _bts_nr_is_256); +static void depends_calc_index_bit(int bts_nr, int *idx, int *bit) +{ + *idx = bts_nr / (8 * 4); + *bit = bts_nr % (8 * 4); +} + +void bts_depend_mark(struct gsm_bts *bts, int dep) +{ + int idx, bit; + depends_calc_index_bit(dep, &idx, &bit); + + bts->depends_on[idx] |= 1 << bit; +} + +void bts_depend_clear(struct gsm_bts *bts, int dep) +{ + int idx, bit; + depends_calc_index_bit(dep, &idx, &bit); + + bts->depends_on[idx] &= ~(1 << bit); +} + +int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other) +{ + int idx, bit; + depends_calc_index_bit(other->nr, &idx, &bit); + + /* Check if there is a depends bit */ + return (base->depends_on[idx] & (1 << bit)) > 0; +} + +static int bts_is_online(struct gsm_bts *bts) +{ + /* TODO: support E1 BTS too */ + if (!is_ipaccess_bts(bts)) + return 1; + + if (!bts->oml_link) + return 0; + + return bts->mo.nm_state.operational == NM_OPSTATE_ENABLED; +} + +int bts_depend_check(struct gsm_bts *bts) +{ + struct gsm_bts *other_bts; + + llist_for_each_entry(other_bts, &bts->network->bts_list, list) { + if (!bts_depend_is_depedency(bts, other_bts)) + continue; + if (bts_is_online(other_bts)) + continue; + return 0; + } + return 1; +} + +/* get the radio link timeout (based on SACCH decode errors, according + * to algorithm specified in TS 05.08 section 5.2. A value of -1 + * indicates we should use an infinitely long timeout, which only works + * with OsmoBTS as the BTS implementation */ +int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts) +{ + const struct gsm48_cell_options *cell_options = &bts->si_common.cell_options; + + if (bts->infinite_radio_link_timeout) + return -1; + else { + /* Encoding as per Table 10.5.21 of TS 04.08 */ + return (cell_options->radio_link_timeout + 1) << 2; + } +} + +/* set the radio link timeout (based on SACCH decode errors, according + * to algorithm specified in TS 05.08 Section 5.2. A value of -1 + * indicates we should use an infinitely long timeout, which only works + * with OsmoBTS as the BTS implementation */ +void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value) +{ + struct gsm48_cell_options *cell_options = &bts->si_common.cell_options; + + if (value < 0) + bts->infinite_radio_link_timeout = true; + else { + bts->infinite_radio_link_timeout = false; + /* Encoding as per Table 10.5.21 of TS 04.08 */ + if (value < 4) + value = 4; + if (value > 64) + value = 64; + cell_options->radio_link_timeout = (value >> 2) - 1; + } +} diff --git a/src/libcommon/gsm_data_shared.c b/src/libcommon/gsm_data_shared.c new file mode 100644 index 000000000..89926364c --- /dev/null +++ b/src/libcommon/gsm_data_shared.c @@ -0,0 +1,849 @@ +/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include <netinet/in.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/gsm/gsm_utils.h> +#include <osmocom/gsm/abis_nm.h> +#include <osmocom/core/statistics.h> + +#include <openbsc/gsm_data.h> + +void gsm_abis_mo_reset(struct gsm_abis_mo *mo) +{ + mo->nm_state.operational = NM_OPSTATE_NULL; + mo->nm_state.availability = NM_AVSTATE_POWER_OFF; +} + +static void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts, + uint8_t obj_class, uint8_t p1, uint8_t p2, uint8_t p3) +{ + mo->bts = bts; + mo->obj_class = obj_class; + mo->obj_inst.bts_nr = p1; + mo->obj_inst.trx_nr = p2; + mo->obj_inst.ts_nr = p3; + gsm_abis_mo_reset(mo); +} + +const struct value_string bts_attribute_names[] = { + OSMO_VALUE_STRING(BTS_TYPE_VARIANT), + OSMO_VALUE_STRING(BTS_SUB_MODEL), + OSMO_VALUE_STRING(TRX_PHY_VERSION), + { 0, NULL } +}; + +enum bts_attribute str2btsattr(const char *s) +{ + return get_string_value(bts_attribute_names, s); +} + +const char *btsatttr2str(enum bts_attribute v) +{ + return get_value_string(bts_attribute_names, v); +} + +const struct value_string osmo_bts_variant_names[_NUM_BTS_VARIANT + 1] = { + { BTS_UNKNOWN, "unknown" }, + { BTS_OSMO_LITECELL15, "osmo-bts-lc15" }, + { BTS_OSMO_OCTPHY, "osmo-bts-octphy" }, + { BTS_OSMO_SYSMO, "osmo-bts-sysmo" }, + { BTS_OSMO_TRX, "omso-bts-trx" }, + { 0, NULL } +}; + +enum gsm_bts_type_variant str2btsvariant(const char *arg) +{ + return get_string_value(osmo_bts_variant_names, arg); +} + +const char *btsvariant2str(enum gsm_bts_type_variant v) +{ + return get_value_string(osmo_bts_variant_names, v); +} + +const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE + 1] = { + { GSM_BTS_TYPE_UNKNOWN, "unknown" }, + { GSM_BTS_TYPE_BS11, "bs11" }, + { GSM_BTS_TYPE_NANOBTS, "nanobts" }, + { GSM_BTS_TYPE_RBS2000, "rbs2000" }, + { GSM_BTS_TYPE_NOKIA_SITE, "nokia_site" }, + { GSM_BTS_TYPE_OSMOBTS, "sysmobts" }, + { 0, NULL } +}; + +enum gsm_bts_type str2btstype(const char *arg) +{ + return get_string_value(bts_type_names, arg); +} + +const char *btstype2str(enum gsm_bts_type type) +{ + return get_value_string(bts_type_names, type); +} + +const struct value_string gsm_bts_features_descs[] = { + { BTS_FEAT_HSCSD, "HSCSD" }, + { BTS_FEAT_GPRS, "GPRS" }, + { BTS_FEAT_EGPRS, "EGPRS" }, + { BTS_FEAT_ECSD, "ECSD" }, + { BTS_FEAT_HOPPING, "Frequency Hopping" }, + { BTS_FEAT_MULTI_TSC, "Multi-TSC" }, + { BTS_FEAT_OML_ALERTS, "OML Alerts" }, + { BTS_FEAT_AGCH_PCH_PROP, "AGCH/PCH proportional allocation" }, + { BTS_FEAT_CBCH, "CBCH" }, + { 0, NULL } +}; + +const struct value_string gsm_chreq_descs[] = { + { GSM_CHREQ_REASON_EMERG, "emergency call" }, + { GSM_CHREQ_REASON_PAG, "answer to paging" }, + { GSM_CHREQ_REASON_CALL, "call re-establishment" }, + { GSM_CHREQ_REASON_LOCATION_UPD,"Location updating" }, + { GSM_CHREQ_REASON_PDCH, "one phase packet access" }, + { GSM_CHREQ_REASON_OTHER, "other" }, + { 0, NULL } +}; + +const struct value_string gsm_pchant_names[13] = { + { GSM_PCHAN_NONE, "NONE" }, + { GSM_PCHAN_CCCH, "CCCH" }, + { GSM_PCHAN_CCCH_SDCCH4,"CCCH+SDCCH4" }, + { GSM_PCHAN_TCH_F, "TCH/F" }, + { GSM_PCHAN_TCH_H, "TCH/H" }, + { GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH8" }, + { GSM_PCHAN_PDCH, "PDCH" }, + { GSM_PCHAN_TCH_F_PDCH, "TCH/F_PDCH" }, + { GSM_PCHAN_UNKNOWN, "UNKNOWN" }, + { GSM_PCHAN_CCCH_SDCCH4_CBCH, "CCCH+SDCCH4+CBCH" }, + { GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH8+CBCH" }, + { GSM_PCHAN_TCH_F_TCH_H_PDCH, "TCH/F_TCH/H_PDCH" }, + { 0, NULL } +}; + +const struct value_string gsm_pchant_descs[13] = { + { GSM_PCHAN_NONE, "Physical Channel not configured" }, + { GSM_PCHAN_CCCH, "FCCH + SCH + BCCH + CCCH (Comb. IV)" }, + { GSM_PCHAN_CCCH_SDCCH4, + "FCCH + SCH + BCCH + CCCH + 4 SDCCH + 2 SACCH (Comb. V)" }, + { GSM_PCHAN_TCH_F, "TCH/F + FACCH/F + SACCH (Comb. I)" }, + { GSM_PCHAN_TCH_H, "2 TCH/H + 2 FACCH/H + 2 SACCH (Comb. II)" }, + { GSM_PCHAN_SDCCH8_SACCH8C, "8 SDCCH + 4 SACCH (Comb. VII)" }, + { GSM_PCHAN_PDCH, "Packet Data Channel for GPRS/EDGE" }, + { GSM_PCHAN_TCH_F_PDCH, "Dynamic TCH/F or GPRS PDCH" }, + { GSM_PCHAN_UNKNOWN, "Unknown / Unsupported channel combination" }, + { GSM_PCHAN_CCCH_SDCCH4_CBCH, "FCCH + SCH + BCCH + CCCH + CBCH + 3 SDCCH + 2 SACCH (Comb. V)" }, + { GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "7 SDCCH + 4 SACCH + CBCH (Comb. VII)" }, + { GSM_PCHAN_TCH_F_TCH_H_PDCH, "Dynamic TCH/F or TCH/H or GPRS PDCH" }, + { 0, NULL } +}; + +const char *gsm_pchan_name(enum gsm_phys_chan_config c) +{ + return get_value_string(gsm_pchant_names, c); +} + +enum gsm_phys_chan_config gsm_pchan_parse(const char *name) +{ + return get_string_value(gsm_pchant_names, name); +} + +/* TODO: move to libosmocore, next to gsm_chan_t_names? */ +const char *gsm_lchant_name(enum gsm_chan_t c) +{ + return get_value_string(gsm_chan_t_names, c); +} + +static const struct value_string lchan_s_names[] = { + { LCHAN_S_NONE, "NONE" }, + { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" }, + { LCHAN_S_ACTIVE, "ACTIVE" }, + { LCHAN_S_INACTIVE, "INACTIVE" }, + { LCHAN_S_REL_REQ, "RELEASE REQUESTED" }, + { LCHAN_S_REL_ERR, "RELEASE DUE ERROR" }, + { LCHAN_S_BROKEN, "BROKEN UNUSABLE" }, + { 0, NULL } +}; + +const char *gsm_lchans_name(enum gsm_lchan_state s) +{ + return get_value_string(lchan_s_names, s); +} + +static const struct value_string chreq_names[] = { + { GSM_CHREQ_REASON_EMERG, "EMERGENCY" }, + { GSM_CHREQ_REASON_PAG, "PAGING" }, + { GSM_CHREQ_REASON_CALL, "CALL" }, + { GSM_CHREQ_REASON_LOCATION_UPD,"LOCATION_UPDATE" }, + { GSM_CHREQ_REASON_OTHER, "OTHER" }, + { 0, NULL } +}; + +const char *gsm_chreq_name(enum gsm_chreq_reason_t c) +{ + return get_value_string(chreq_names, c); +} + +struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num) +{ + struct gsm_bts *bts; + + if (num >= net->num_bts) + return NULL; + + llist_for_each_entry(bts, &net->bts_list, list) { + if (bts->nr == num) + return bts; + } + + return NULL; +} + +struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx); + int k; + + if (!trx) + return NULL; + + trx->bts = bts; + trx->nr = bts->num_trx++; + trx->mo.nm_state.administrative = NM_STATE_UNLOCKED; + + gsm_mo_init(&trx->mo, bts, NM_OC_RADIO_CARRIER, + bts->nr, trx->nr, 0xff); + gsm_mo_init(&trx->bb_transc.mo, bts, NM_OC_BASEB_TRANSC, + bts->nr, trx->nr, 0xff); + + for (k = 0; k < TRX_NR_TS; k++) { + struct gsm_bts_trx_ts *ts = &trx->ts[k]; + int l; + + ts->trx = trx; + ts->nr = k; + ts->pchan = GSM_PCHAN_NONE; + ts->dyn.pchan_is = GSM_PCHAN_NONE; + ts->dyn.pchan_want = GSM_PCHAN_NONE; + ts->tsc = -1; + + gsm_mo_init(&ts->mo, bts, NM_OC_CHANNEL, + bts->nr, trx->nr, ts->nr); + + ts->hopping.arfcns.data_len = sizeof(ts->hopping.arfcns_data); + ts->hopping.arfcns.data = ts->hopping.arfcns_data; + ts->hopping.ma.data_len = sizeof(ts->hopping.ma_data); + ts->hopping.ma.data = ts->hopping.ma_data; + + for (l = 0; l < TS_MAX_LCHAN; l++) { + struct gsm_lchan *lchan; + char *name; + lchan = &ts->lchan[l]; + + lchan->ts = ts; + lchan->nr = l; + lchan->type = GSM_LCHAN_NONE; + + name = gsm_lchan_name_compute(lchan); + lchan->name = talloc_strdup(trx, name); +#ifndef ROLE_BSC + INIT_LLIST_HEAD(&lchan->sapi_cmds); +#endif + } + } + + if (trx->nr != 0) + trx->nominal_power = bts->c0->nominal_power; + + llist_add_tail(&trx->list, &bts->trx_list); + + return trx; +} + + +static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 }; +static const uint8_t bts_cell_timer_default[] = + { 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 }; +static const struct gprs_rlc_cfg rlc_cfg_default = { + .parameter = { + [RLC_T3142] = 20, + [RLC_T3169] = 5, + [RLC_T3191] = 5, + [RLC_T3193] = 160, /* 10ms */ + [RLC_T3195] = 5, + [RLC_N3101] = 10, + [RLC_N3103] = 4, + [RLC_N3105] = 8, + [CV_COUNTDOWN] = 15, + [T_DL_TBF_EXT] = 250 * 10, /* ms */ + [T_UL_TBF_EXT] = 250 * 10, /* ms */ + }, + .paging = { + .repeat_time = 5 * 50, /* ms */ + .repeat_count = 3, + }, + .cs_mask = 0x1fff, + .initial_cs = 2, + .initial_mcs = 6, +}; + +struct gsm_bts *gsm_bts_alloc(void *ctx) +{ + struct gsm_bts *bts = talloc_zero(ctx, struct gsm_bts); + int i; + + if (!bts) + return NULL; + + bts->num_trx = 0; + INIT_LLIST_HEAD(&bts->trx_list); + bts->ms_max_power = 15; /* dBm */ + + gsm_mo_init(&bts->mo, bts, NM_OC_BTS, + bts->nr, 0xff, 0xff); + gsm_mo_init(&bts->site_mgr.mo, bts, NM_OC_SITE_MANAGER, + 0xff, 0xff, 0xff); + + for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { + bts->gprs.nsvc[i].bts = bts; + bts->gprs.nsvc[i].id = i; + gsm_mo_init(&bts->gprs.nsvc[i].mo, bts, NM_OC_GPRS_NSVC, + bts->nr, i, 0xff); + } + memcpy(&bts->gprs.nse.timer, bts_nse_timer_default, + sizeof(bts->gprs.nse.timer)); + gsm_mo_init(&bts->gprs.nse.mo, bts, NM_OC_GPRS_NSE, + bts->nr, 0xff, 0xff); + memcpy(&bts->gprs.cell.timer, bts_cell_timer_default, + sizeof(bts->gprs.cell.timer)); + gsm_mo_init(&bts->gprs.cell.mo, bts, NM_OC_GPRS_CELL, + bts->nr, 0xff, 0xff); + memcpy(&bts->gprs.cell.rlc_cfg, &rlc_cfg_default, + sizeof(bts->gprs.cell.rlc_cfg)); + + /* create our primary TRX */ + bts->c0 = gsm_bts_trx_alloc(bts); + if (!bts->c0) { + talloc_free(bts); + return NULL; + } + bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4; + + bts->rach_b_thresh = -1; + bts->rach_ldavg_slots = -1; + bts->paging.free_chans_need = -1; + bts->features.data = &bts->_features_data[0]; + bts->features.data_len = sizeof(bts->_features_data); + + /* si handling */ + bts->bcch_change_mark = 1; + + return bts; +} + +/* reset the state of all MO in the BTS */ +void gsm_bts_mo_reset(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + unsigned int i; + + gsm_abis_mo_reset(&bts->mo); + gsm_abis_mo_reset(&bts->site_mgr.mo); + for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) + gsm_abis_mo_reset(&bts->gprs.nsvc[i].mo); + gsm_abis_mo_reset(&bts->gprs.nse.mo); + gsm_abis_mo_reset(&bts->gprs.cell.mo); + + llist_for_each_entry(trx, &bts->trx_list, list) { + gsm_abis_mo_reset(&trx->mo); + gsm_abis_mo_reset(&trx->bb_transc.mo); + + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + gsm_abis_mo_reset(&ts->mo); + } + } +} + +struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num) +{ + struct gsm_bts_trx *trx; + + if (num >= bts->num_trx) + return NULL; + + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->nr == num) + return trx; + } + + return NULL; +} + +static char ts2str[255]; + +char *gsm_trx_name(const struct gsm_bts_trx *trx) +{ + if (!trx) + snprintf(ts2str, sizeof(ts2str), "(trx=NULL)"); + else + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)", + trx->bts->nr, trx->nr); + + return ts2str; +} + + +char *gsm_ts_name(const struct gsm_bts_trx_ts *ts) +{ + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)", + ts->trx->bts->nr, ts->trx->nr, ts->nr); + + return ts2str; +} + +/*! Log timeslot number with full pchan information */ +char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts) +{ + switch (ts->pchan) { + case GSM_PCHAN_TCH_F_TCH_H_PDCH: + if (ts->dyn.pchan_is == ts->dyn.pchan_want) + snprintf(ts2str, sizeof(ts2str), + "(bts=%d,trx=%d,ts=%d,pchan=%s as %s)", + ts->trx->bts->nr, ts->trx->nr, ts->nr, + gsm_pchan_name(ts->pchan), + gsm_pchan_name(ts->dyn.pchan_is)); + else + snprintf(ts2str, sizeof(ts2str), + "(bts=%d,trx=%d,ts=%d,pchan=%s" + " switching %s -> %s)", + ts->trx->bts->nr, ts->trx->nr, ts->nr, + gsm_pchan_name(ts->pchan), + gsm_pchan_name(ts->dyn.pchan_is), + gsm_pchan_name(ts->dyn.pchan_want)); + break; + case GSM_PCHAN_TCH_F_PDCH: + if ((ts->flags & TS_F_PDCH_PENDING_MASK) == 0) + snprintf(ts2str, sizeof(ts2str), + "(bts=%d,trx=%d,ts=%d,pchan=%s as %s)", + ts->trx->bts->nr, ts->trx->nr, ts->nr, + gsm_pchan_name(ts->pchan), + (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH" + : "TCH/F"); + else + snprintf(ts2str, sizeof(ts2str), + "(bts=%d,trx=%d,ts=%d,pchan=%s" + " switching %s -> %s)", + ts->trx->bts->nr, ts->trx->nr, ts->nr, + gsm_pchan_name(ts->pchan), + (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH" + : "TCH/F", + (ts->flags & TS_F_PDCH_ACT_PENDING)? "PDCH" + : "TCH/F"); + break; + default: + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,pchan=%s)", + ts->trx->bts->nr, ts->trx->nr, ts->nr, + gsm_pchan_name(ts->pchan)); + break; + } + + return ts2str; +} + +char *gsm_lchan_name_compute(const struct gsm_lchan *lchan) +{ + struct gsm_bts_trx_ts *ts = lchan->ts; + + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,ss=%d)", + ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr); + + return ts2str; +} + +/* obtain the MO structure for a given object instance */ +struct gsm_abis_mo * +gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class, + const struct abis_om_obj_inst *obj_inst) +{ + struct gsm_bts_trx *trx; + struct gsm_abis_mo *mo = NULL; + + switch (obj_class) { + case NM_OC_BTS: + mo = &bts->mo; + break; + case NM_OC_RADIO_CARRIER: + if (obj_inst->trx_nr >= bts->num_trx) { + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + mo = &trx->mo; + break; + case NM_OC_BASEB_TRANSC: + if (obj_inst->trx_nr >= bts->num_trx) { + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + mo = &trx->bb_transc.mo; + break; + case NM_OC_CHANNEL: + if (obj_inst->trx_nr >= bts->num_trx) { + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + if (obj_inst->ts_nr >= TRX_NR_TS) + return NULL; + mo = &trx->ts[obj_inst->ts_nr].mo; + break; + case NM_OC_SITE_MANAGER: + mo = &bts->site_mgr.mo; + break; + case NM_OC_BS11: + switch (obj_inst->bts_nr) { + case BS11_OBJ_CCLK: + mo = &bts->bs11.cclk.mo; + break; + case BS11_OBJ_BBSIG: + if (obj_inst->ts_nr > bts->num_trx) + return NULL; + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + mo = &trx->bs11.bbsig.mo; + break; + case BS11_OBJ_PA: + if (obj_inst->ts_nr > bts->num_trx) + return NULL; + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + mo = &trx->bs11.pa.mo; + break; + default: + return NULL; + } + break; + case NM_OC_BS11_RACK: + mo = &bts->bs11.rack.mo; + break; + case NM_OC_BS11_ENVABTSE: + if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse)) + return NULL; + mo = &bts->bs11.envabtse[obj_inst->trx_nr].mo; + break; + case NM_OC_GPRS_NSE: + mo = &bts->gprs.nse.mo; + break; + case NM_OC_GPRS_CELL: + mo = &bts->gprs.cell.mo; + break; + case NM_OC_GPRS_NSVC: + if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) + return NULL; + mo = &bts->gprs.nsvc[obj_inst->trx_nr].mo; + break; + } + return mo; +} + +/* obtain the gsm_nm_state data structure for a given object instance */ +struct gsm_nm_state * +gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class, + const struct abis_om_obj_inst *obj_inst) +{ + struct gsm_abis_mo *mo; + + mo = gsm_objclass2mo(bts, obj_class, obj_inst); + if (!mo) + return NULL; + + return &mo->nm_state; +} + +/* obtain the in-memory data structure of a given object instance */ +void * +gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class, + const struct abis_om_obj_inst *obj_inst) +{ + struct gsm_bts_trx *trx; + void *obj = NULL; + + switch (obj_class) { + case NM_OC_BTS: + obj = bts; + break; + case NM_OC_RADIO_CARRIER: + if (obj_inst->trx_nr >= bts->num_trx) { + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + obj = trx; + break; + case NM_OC_BASEB_TRANSC: + if (obj_inst->trx_nr >= bts->num_trx) { + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + obj = &trx->bb_transc; + break; + case NM_OC_CHANNEL: + if (obj_inst->trx_nr >= bts->num_trx) { + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + if (obj_inst->ts_nr >= TRX_NR_TS) + return NULL; + obj = &trx->ts[obj_inst->ts_nr]; + break; + case NM_OC_SITE_MANAGER: + obj = &bts->site_mgr; + break; + case NM_OC_GPRS_NSE: + obj = &bts->gprs.nse; + break; + case NM_OC_GPRS_CELL: + obj = &bts->gprs.cell; + break; + case NM_OC_GPRS_NSVC: + if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) + return NULL; + obj = &bts->gprs.nsvc[obj_inst->trx_nr]; + break; + } + return obj; +} + +/* See Table 10.5.25 of GSM04.08 */ +uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan, + uint8_t ts_nr, uint8_t lchan_nr) +{ + uint8_t cbits, chan_nr; + + switch (pchan) { + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_PDCH: + case GSM_PCHAN_TCH_F_PDCH: + OSMO_ASSERT(lchan_nr == 0); + cbits = 0x01; + break; + case GSM_PCHAN_TCH_H: + OSMO_ASSERT(lchan_nr < 2); + cbits = 0x02; + cbits += lchan_nr; + break; + case GSM_PCHAN_CCCH_SDCCH4: + case GSM_PCHAN_CCCH_SDCCH4_CBCH: + /* + * As a special hack for BCCH, lchan_nr == 4 may be passed + * here. This should never be sent in an RSL message. + * See osmo-bts-xxx/oml.c:opstart_compl(). + */ + if (lchan_nr == CCCH_LCHAN) + chan_nr = 0; + else + OSMO_ASSERT(lchan_nr < 4); + cbits = 0x04; + cbits += lchan_nr; + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + case GSM_PCHAN_SDCCH8_SACCH8C_CBCH: + OSMO_ASSERT(lchan_nr < 8); + cbits = 0x08; + cbits += lchan_nr; + break; + default: + case GSM_PCHAN_CCCH: +#ifdef ROLE_BSC + OSMO_ASSERT(lchan_nr == 0); +#else + /* + * FIXME: On octphy and litecell, we hit above assertion (see + * Max's comment at https://gerrit.osmocom.org/589 ); disabled + * for BTS until this is clarified; remove the #ifdef when it + * is fixed. + */ +#warning "fix caller that passes lchan_nr != 0" +#endif + cbits = 0x10; + break; + } + + chan_nr = (cbits << 3) | (ts_nr & 0x7); + + return chan_nr; +} + +uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan) +{ + enum gsm_phys_chan_config pchan = lchan->ts->pchan; + if (pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) + return gsm_lchan_as_pchan2chan_nr(lchan, + lchan->ts->dyn.pchan_is); + return gsm_pchan2chan_nr(lchan->ts->pchan, lchan->ts->nr, lchan->nr); +} + +uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan, + enum gsm_phys_chan_config as_pchan) +{ + if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH + && as_pchan == GSM_PCHAN_PDCH) + return RSL_CHAN_OSMO_PDCH | (lchan->ts->nr & ~RSL_CHAN_NR_MASK); + return gsm_pchan2chan_nr(as_pchan, lchan->ts->nr, lchan->nr); +} + +/* return the gsm_lchan for the CBCH (if it exists at all) */ +struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts) +{ + struct gsm_lchan *lchan = NULL; + struct gsm_bts_trx *trx = bts->c0; + + if (trx->ts[0].pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH) + lchan = &trx->ts[0].lchan[2]; + else { + int i; + for (i = 0; i < 8; i++) { + if (trx->ts[i].pchan == GSM_PCHAN_SDCCH8_SACCH8C_CBCH) { + lchan = &trx->ts[i].lchan[2]; + break; + } + } + } + + return lchan; +} + +/* determine logical channel based on TRX and channel number IE */ +struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr, + int *rc) +{ + uint8_t ts_nr = chan_nr & 0x07; + uint8_t cbits = chan_nr >> 3; + uint8_t lch_idx; + struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; + bool ok = true; + + if (rc) + *rc = -EINVAL; + + if (cbits == 0x01) { + lch_idx = 0; /* TCH/F */ + if (ts->pchan != GSM_PCHAN_TCH_F && + ts->pchan != GSM_PCHAN_PDCH && + ts->pchan != GSM_PCHAN_TCH_F_PDCH + && !(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH + && (ts->dyn.pchan_is == GSM_PCHAN_TCH_F + || ts->dyn.pchan_want == GSM_PCHAN_TCH_F))) + ok = false; + } else if ((cbits & 0x1e) == 0x02) { + lch_idx = cbits & 0x1; /* TCH/H */ + if (ts->pchan != GSM_PCHAN_TCH_H + && !(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH + && (ts->dyn.pchan_is == GSM_PCHAN_TCH_H + || ts->dyn.pchan_want == GSM_PCHAN_TCH_H))) + ok = false; + } else if ((cbits & 0x1c) == 0x04) { + lch_idx = cbits & 0x3; /* SDCCH/4 */ + if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4 && + ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH) + ok = false; + } else if ((cbits & 0x18) == 0x08) { + lch_idx = cbits & 0x7; /* SDCCH/8 */ + if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C && + ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C_CBCH) + ok = false; + } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { + lch_idx = 0; + if (ts->pchan != GSM_PCHAN_CCCH && + ts->pchan != GSM_PCHAN_CCCH_SDCCH4 && + ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH) + ok = false; + /* FIXME: we should not return first sdcch4 !!! */ + } else if ((chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_OSMO_PDCH) { + lch_idx = 0; + if (ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH) + ok = false; + } else + return NULL; + + if (rc && ok) + *rc = 0; + + return &ts->lchan[lch_idx]; +} + +static const uint8_t subslots_per_pchan[] = { + [GSM_PCHAN_NONE] = 0, + [GSM_PCHAN_CCCH] = 0, + [GSM_PCHAN_PDCH] = 0, + [GSM_PCHAN_CCCH_SDCCH4] = 4, + [GSM_PCHAN_TCH_F] = 1, + [GSM_PCHAN_TCH_H] = 2, + [GSM_PCHAN_SDCCH8_SACCH8C] = 8, + [GSM_PCHAN_CCCH_SDCCH4_CBCH] = 4, + [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 8, + /* + * GSM_PCHAN_TCH_F_PDCH and GSM_PCHAN_TCH_F_TCH_H_PDCH should not be + * part of this, those TS are handled according to their dynamic state. + */ +}; + +/*! Return the actual pchan type, also heeding dynamic TS. */ +enum gsm_phys_chan_config ts_pchan(struct gsm_bts_trx_ts *ts) +{ + switch (ts->pchan) { + case GSM_PCHAN_TCH_F_TCH_H_PDCH: + return ts->dyn.pchan_is; + case GSM_PCHAN_TCH_F_PDCH: + if (ts->flags & TS_F_PDCH_ACTIVE) + return GSM_PCHAN_PDCH; + else + return GSM_PCHAN_TCH_F; + default: + return ts->pchan; + } +} + +/*! According to ts->pchan and possibly ts->dyn_pchan, return the number of + * logical channels available in the timeslot. */ +uint8_t ts_subslots(struct gsm_bts_trx_ts *ts) +{ + return subslots_per_pchan[ts_pchan(ts)]; +} + +static bool pchan_is_tch(enum gsm_phys_chan_config pchan) +{ + switch (pchan) { + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + return true; + default: + return false; + } +} + +bool ts_is_tch(struct gsm_bts_trx_ts *ts) +{ + return pchan_is_tch(ts_pchan(ts)); +} diff --git a/src/libcommon/gsm_subscriber_base.c b/src/libcommon/gsm_subscriber_base.c new file mode 100644 index 000000000..1ecdee5a5 --- /dev/null +++ b/src/libcommon/gsm_subscriber_base.c @@ -0,0 +1,163 @@ +/* The concept of a subscriber as seen by the BSC */ + +/* (C) 2008 by Harald Welte <laforge@gnumonks.org> + * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org> + * (C) 2010 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#include <osmocom/core/talloc.h> +#include <osmocom/core/utils.h> +#include <openbsc/gsm_subscriber.h> +#include <openbsc/debug.h> + +LLIST_HEAD(active_subscribers); +void *tall_subscr_ctx; + +/* for the gsm_subscriber.c */ +struct llist_head *subscr_bsc_active_subscribers(void) +{ + return &active_subscribers; +} + + +char *subscr_name(struct gsm_subscriber *subscr) +{ + if (!subscr) + return "unknown"; + + if (strlen(subscr->name)) + return subscr->name; + + return subscr->imsi; +} + +struct gsm_subscriber *subscr_alloc(void) +{ + struct gsm_subscriber *s; + + s = talloc_zero(tall_subscr_ctx, struct gsm_subscriber); + if (!s) + return NULL; + + llist_add_tail(&s->entry, &active_subscribers); + s->use_count = 1; + s->tmsi = GSM_RESERVED_TMSI; + + INIT_LLIST_HEAD(&s->requests); + + return s; +} + +static void subscr_free(struct gsm_subscriber *subscr) +{ + llist_del(&subscr->entry); + talloc_free(subscr); +} + +void subscr_direct_free(struct gsm_subscriber *subscr) +{ + OSMO_ASSERT(subscr->use_count == 1); + subscr_free(subscr); +} + +struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr) +{ + subscr->use_count++; + DEBUGP(DREF, "subscr %s usage increases usage to: %d\n", + subscr->extension, subscr->use_count); + return subscr; +} + +struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr) +{ + subscr->use_count--; + DEBUGP(DREF, "subscr %s usage decreased usage to: %d\n", + subscr->extension, subscr->use_count); + if (subscr->use_count <= 0 && + !((subscr->group && subscr->group->keep_subscr) || + subscr->keep_in_ram)) + subscr_free(subscr); + return NULL; +} + +struct gsm_subscriber *subscr_get_or_create(struct gsm_subscriber_group *sgrp, + const char *imsi) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { + if (strcmp(subscr->imsi, imsi) == 0 && subscr->group == sgrp) + return subscr_get(subscr); + } + + subscr = subscr_alloc(); + if (!subscr) + return NULL; + + osmo_strlcpy(subscr->imsi, imsi, sizeof(subscr->imsi)); + subscr->group = sgrp; + return subscr; +} + +struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_subscriber_group *sgrp, + uint32_t tmsi) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { + if (subscr->tmsi == tmsi && subscr->group == sgrp) + return subscr_get(subscr); + } + + return NULL; +} + +struct gsm_subscriber *subscr_active_by_imsi(struct gsm_subscriber_group *sgrp, + const char *imsi) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { + if (strcmp(subscr->imsi, imsi) == 0 && subscr->group == sgrp) + return subscr_get(subscr); + } + + return NULL; +} + +int subscr_purge_inactive(struct gsm_subscriber_group *sgrp) +{ + struct gsm_subscriber *subscr, *tmp; + int purged = 0; + + llist_for_each_entry_safe(subscr, tmp, subscr_bsc_active_subscribers(), entry) { + if (subscr->group == sgrp && subscr->use_count <= 0) { + subscr_free(subscr); + purged += 1; + } + } + + return purged; +} diff --git a/src/libcommon/gsup_client.c b/src/libcommon/gsup_client.c new file mode 100644 index 000000000..de00d8d4f --- /dev/null +++ b/src/libcommon/gsup_client.c @@ -0,0 +1,341 @@ +/* Generic Subscriber Update Protocol client */ + +/* (C) 2014-2016 by Sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Jacob Erlbeck + * Author: Neels Hofmeyr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <openbsc/gsup_client.h> + +#include <osmocom/abis/ipa.h> +#include <osmocom/gsm/protocol/ipaccess.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/logging.h> + +#include <openbsc/debug.h> + +#include <errno.h> +#include <string.h> + +extern void *tall_bsc_ctx; + +static void start_test_procedure(struct gsup_client *gsupc); + +static void gsup_client_send_ping(struct gsup_client *gsupc) +{ + struct msgb *msg = gsup_client_msgb_alloc(); + + msg->l2h = msgb_put(msg, 1); + msg->l2h[0] = IPAC_MSGT_PING; + ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS); + ipa_client_conn_send(gsupc->link, msg); +} + +static int gsup_client_connect(struct gsup_client *gsupc) +{ + int rc; + + if (gsupc->is_connected) + return 0; + + if (osmo_timer_pending(&gsupc->connect_timer)) { + LOGP(DLGSUP, LOGL_DEBUG, + "GSUP connect: connect timer already running\n"); + osmo_timer_del(&gsupc->connect_timer); + } + + if (osmo_timer_pending(&gsupc->ping_timer)) { + LOGP(DLGSUP, LOGL_DEBUG, + "GSUP connect: ping timer already running\n"); + osmo_timer_del(&gsupc->ping_timer); + } + + if (ipa_client_conn_clear_queue(gsupc->link) > 0) + LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n"); + + rc = ipa_client_conn_open(gsupc->link); + + if (rc >= 0) { + LOGP(DLGSUP, LOGL_INFO, "GSUP connecting to %s:%d\n", + gsupc->link->addr, gsupc->link->port); + return 0; + } + + LOGP(DLGSUP, LOGL_INFO, "GSUP failed to connect to %s:%d: %s\n", + gsupc->link->addr, gsupc->link->port, strerror(-rc)); + + if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT || + rc == -EINVAL) + return rc; + + osmo_timer_schedule(&gsupc->connect_timer, + GSUP_CLIENT_RECONNECT_INTERVAL, 0); + + LOGP(DLGSUP, LOGL_INFO, "Scheduled timer to retry GSUP connect to %s:%d\n", + gsupc->link->addr, gsupc->link->port); + + return 0; +} + +static void connect_timer_cb(void *gsupc_) +{ + struct gsup_client *gsupc = gsupc_; + + if (gsupc->is_connected) + return; + + gsup_client_connect(gsupc); +} + +static void client_send(struct gsup_client *gsupc, int proto_ext, + struct msgb *msg_tx) +{ + ipa_prepend_header_ext(msg_tx, proto_ext); + ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO); + ipa_client_conn_send(gsupc->link, msg_tx); + /* msg_tx is now queued and will be freed. */ +} + +static void gsup_client_oap_register(struct gsup_client *gsupc) +{ + struct msgb *msg_tx; + int rc; + rc = oap_client_register(&gsupc->oap_state, &msg_tx); + + if ((rc < 0) || (!msg_tx)) { + LOGP(DLGSUP, LOGL_ERROR, "GSUP OAP set up, but cannot register.\n"); + return; + } + + client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); +} + +static void gsup_client_updown_cb(struct ipa_client_conn *link, int up) +{ + struct gsup_client *gsupc = link->data; + + LOGP(DLGSUP, LOGL_INFO, "GSUP link to %s:%d %s\n", + link->addr, link->port, up ? "UP" : "DOWN"); + + gsupc->is_connected = up; + + if (up) { + start_test_procedure(gsupc); + + if (gsupc->oap_state.state == OAP_INITIALIZED) + gsup_client_oap_register(gsupc); + + osmo_timer_del(&gsupc->connect_timer); + } else { + osmo_timer_del(&gsupc->ping_timer); + + osmo_timer_schedule(&gsupc->connect_timer, + GSUP_CLIENT_RECONNECT_INTERVAL, 0); + } +} + +static int gsup_client_oap_handle(struct gsup_client *gsupc, struct msgb *msg_rx) +{ + int rc; + struct msgb *msg_tx; + + /* If the oap_state is disabled, this will reject the messages. */ + rc = oap_client_handle(&gsupc->oap_state, msg_rx, &msg_tx); + msgb_free(msg_rx); + if (rc < 0) + return rc; + + if (msg_tx) + client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); + + return 0; +} + +static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg) +{ + struct ipaccess_head *hh = (struct ipaccess_head *) msg->data; + struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg); + struct gsup_client *gsupc = (struct gsup_client *)link->data; + int rc; + static struct ipaccess_unit ipa_dev = { + .unit_name = "SGSN" + }; + + msg->l2h = &hh->data[0]; + + rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg); + + if (rc < 0) { + LOGP(DLGSUP, LOGL_NOTICE, + "GSUP received an invalid IPA/CCM message from %s:%d\n", + link->addr, link->port); + /* Link has been closed */ + gsupc->is_connected = 0; + msgb_free(msg); + return -1; + } + + if (rc == 1) { + uint8_t msg_type = *(msg->l2h); + /* CCM message */ + if (msg_type == IPAC_MSGT_PONG) { + LOGP(DLGSUP, LOGL_DEBUG, "GSUP receiving PONG\n"); + gsupc->got_ipa_pong = 1; + } + + msgb_free(msg); + return 0; + } + + if (hh->proto != IPAC_PROTO_OSMO) + goto invalid; + + if (!he || msgb_l2len(msg) < sizeof(*he)) + goto invalid; + + msg->l2h = &he->data[0]; + + if (he->proto == IPAC_PROTO_EXT_GSUP) { + OSMO_ASSERT(gsupc->read_cb != NULL); + gsupc->read_cb(gsupc, msg); + /* expecting read_cb() to free msg */ + } else if (he->proto == IPAC_PROTO_EXT_OAP) { + return gsup_client_oap_handle(gsupc, msg); + /* gsup_client_oap_handle frees msg */ + } else + goto invalid; + + return 0; + +invalid: + LOGP(DLGSUP, LOGL_NOTICE, + "GSUP received an invalid IPA message from %s:%d, size = %d\n", + link->addr, link->port, msgb_length(msg)); + + msgb_free(msg); + return -1; +} + +static void ping_timer_cb(void *gsupc_) +{ + struct gsup_client *gsupc = gsupc_; + + LOGP(DLGSUP, LOGL_INFO, "GSUP ping callback (%s, %s PONG)\n", + gsupc->is_connected ? "connected" : "not connected", + gsupc->got_ipa_pong ? "got" : "didn't get"); + + if (gsupc->got_ipa_pong) { + start_test_procedure(gsupc); + return; + } + + LOGP(DLGSUP, LOGL_NOTICE, "GSUP ping timed out, reconnecting\n"); + ipa_client_conn_close(gsupc->link); + gsupc->is_connected = 0; + + gsup_client_connect(gsupc); +} + +static void start_test_procedure(struct gsup_client *gsupc) +{ + osmo_timer_setup(&gsupc->ping_timer, ping_timer_cb, gsupc); + + gsupc->got_ipa_pong = 0; + osmo_timer_schedule(&gsupc->ping_timer, GSUP_CLIENT_PING_INTERVAL, 0); + LOGP(DLGSUP, LOGL_DEBUG, "GSUP sending PING\n"); + gsup_client_send_ping(gsupc); +} + +struct gsup_client *gsup_client_create(const char *ip_addr, + unsigned int tcp_port, + gsup_client_read_cb_t read_cb, + struct oap_client_config *oapc_config) +{ + struct gsup_client *gsupc; + int rc; + + gsupc = talloc_zero(tall_bsc_ctx, struct gsup_client); + OSMO_ASSERT(gsupc); + + /* a NULL oapc_config will mark oap_state disabled. */ + rc = oap_client_init(oapc_config, &gsupc->oap_state); + if (rc != 0) + goto failed; + + gsupc->link = ipa_client_conn_create(gsupc, + /* no e1inp */ NULL, + 0, + ip_addr, tcp_port, + gsup_client_updown_cb, + gsup_client_read_cb, + /* default write_cb */ NULL, + gsupc); + if (!gsupc->link) + goto failed; + + osmo_timer_setup(&gsupc->connect_timer, connect_timer_cb, gsupc); + + rc = gsup_client_connect(gsupc); + + if (rc < 0) + goto failed; + + gsupc->read_cb = read_cb; + + return gsupc; + +failed: + gsup_client_destroy(gsupc); + return NULL; +} + +void gsup_client_destroy(struct gsup_client *gsupc) +{ + osmo_timer_del(&gsupc->connect_timer); + osmo_timer_del(&gsupc->ping_timer); + + if (gsupc->link) { + ipa_client_conn_close(gsupc->link); + ipa_client_conn_destroy(gsupc->link); + gsupc->link = NULL; + } + talloc_free(gsupc); +} + +int gsup_client_send(struct gsup_client *gsupc, struct msgb *msg) +{ + if (!gsupc) { + msgb_free(msg); + return -ENOTCONN; + } + + if (!gsupc->is_connected) { + msgb_free(msg); + return -EAGAIN; + } + + client_send(gsupc, IPAC_PROTO_EXT_GSUP, msg); + + return 0; +} + +struct msgb *gsup_client_msgb_alloc(void) +{ + return msgb_alloc_headroom(4000, 64, __func__); +} diff --git a/src/libcommon/gsup_test_client.c b/src/libcommon/gsup_test_client.c new file mode 100644 index 000000000..8fc38d60d --- /dev/null +++ b/src/libcommon/gsup_test_client.c @@ -0,0 +1,298 @@ +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <signal.h> + +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/msgb.h> +#include <osmocom/core/select.h> +#include <osmocom/core/application.h> +#include <osmocom/core/utils.h> +#include <osmocom/gsm/gsup.h> + +#include <openbsc/gsup_client.h> +#include <openbsc/debug.h> + +static struct gsup_client *g_gc; + + +/*********************************************************************** + * IMSI Operation + ***********************************************************************/ +static LLIST_HEAD(g_imsi_ops); + +struct imsi_op_stats { + uint32_t num_alloc; + uint32_t num_released; + uint32_t num_rx_success; + uint32_t num_rx_error; + uint32_t num_timeout; +}; + +enum imsi_op_type { + IMSI_OP_SAI, + IMSI_OP_LU, + IMSI_OP_ISD, + _NUM_IMSI_OP +}; + +static const struct value_string imsi_op_names[] = { + { IMSI_OP_SAI, "SAI" }, + { IMSI_OP_LU, "LU" }, + { IMSI_OP_ISD, "ISD" }, + { 0, NULL } +}; + +static struct imsi_op_stats imsi_op_stats[_NUM_IMSI_OP]; + +struct imsi_op { + struct llist_head list; + char imsi[17]; + enum imsi_op_type type; + struct osmo_timer_list timer; +}; + +static struct imsi_op *imsi_op_find(const char *imsi, + enum imsi_op_type type) +{ + struct imsi_op *io; + + llist_for_each_entry(io, &g_imsi_ops, list) { + if (!strcmp(io->imsi, imsi) && io->type == type) + return io; + } + return NULL; +} + +static void imsi_op_timer_cb(void *data); + +static struct imsi_op *imsi_op_alloc(void *ctx, const char *imsi, + enum imsi_op_type type) +{ + struct imsi_op *io; + + if (imsi_op_find(imsi, type)) + return NULL; + + io = talloc_zero(ctx, struct imsi_op); + osmo_strlcpy(io->imsi, imsi, sizeof(io->imsi)); + io->type = type; + osmo_timer_setup(&io->timer, imsi_op_timer_cb, io); + llist_add(&io->list, &g_imsi_ops); + imsi_op_stats[type].num_alloc++; + + return io; +} + +static void imsi_op_release(struct imsi_op *io) +{ + osmo_timer_del(&io->timer); + llist_del(&io->list); + imsi_op_stats[io->type].num_released++; + talloc_free(io); +} + +static void imsi_op_timer_cb(void *data) +{ + struct imsi_op *io = data; + printf("%s: Timer expiration\n", io->imsi); + imsi_op_stats[io->type].num_timeout++; + imsi_op_release(io); +} + +/* allocate + generate + send Send-Auth-Info */ +int req_auth_info(const char *imsi) +{ + struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_SAI); + struct osmo_gsup_message gsup = {0}; + struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); + + osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); + gsup.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST; + + osmo_gsup_encode(msg, &gsup); + + return gsup_client_send(g_gc, msg); +} + +/* allocate + generate + send Send-Auth-Info */ +int req_loc_upd(const char *imsi) +{ + struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_LU); + struct osmo_gsup_message gsup = {0}; + struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); + + osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); + gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST; + + osmo_gsup_encode(msg, &gsup); + + return gsup_client_send(g_gc, msg); +} + +int resp_isd(struct imsi_op *io) +{ + struct osmo_gsup_message gsup = {0}; + struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); + + osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); + gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT; + + osmo_gsup_encode(msg, &gsup); + + imsi_op_release(io); + + return gsup_client_send(g_gc, msg); +} + +/* receive an incoming GSUP message */ +static void imsi_op_rx_gsup(struct imsi_op *io, const struct osmo_gsup_message *gsup) +{ + int is_error = 0; + + if (OSMO_GSUP_IS_MSGT_ERROR(gsup->message_type)) { + imsi_op_stats[io->type].num_rx_error++; + is_error = 1; + } else + imsi_op_stats[io->type].num_rx_success++; + + switch (io->type) { + case IMSI_OP_SAI: + printf("%s; SAI Response%s\n", io->imsi, is_error ? ": ERROR" : ""); + /* now that we have auth tuples, request LU */ + req_loc_upd(io->imsi); + imsi_op_release(io); + break; + case IMSI_OP_LU: + printf("%s; LU Response%s\n", io->imsi, is_error ? ": ERROR" : ""); + imsi_op_release(io); + break; + case IMSI_OP_ISD: + printf("%s; ISD Request%s\n", io->imsi, is_error ? ": ERROR" : ""); + resp_isd(io); + break; + default: + printf("%s: Unknown\n", io->imsi); + imsi_op_release(io); + break; + } +} + +static int op_type_by_gsup_msgt(enum osmo_gsup_message_type msg_type) +{ + switch (msg_type) { + case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT: + case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR: + return IMSI_OP_SAI; + case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: + case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR: + return IMSI_OP_LU; + case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST: + return IMSI_OP_ISD; + default: + printf("Unknown GSUP msg_type %u\n", msg_type); + return -1; + } +} + +static int gsupc_read_cb(struct gsup_client *gsupc, struct msgb *msg) +{ + struct osmo_gsup_message gsup_msg = {0}; + struct imsi_op *io; + int rc; + + DEBUGP(DGPRS, "Rx GSUP %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg))); + + rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup_msg); + if (rc < 0) + return rc; + + if (!gsup_msg.imsi[0]) + return -1; + + rc = op_type_by_gsup_msgt(gsup_msg.message_type); + if (rc < 0) + return rc; + + switch (rc) { + case IMSI_OP_SAI: + case IMSI_OP_LU: + io = imsi_op_find(gsup_msg.imsi, rc); + if (!io) + return -1; + break; + case IMSI_OP_ISD: + /* ISD is an inbound transaction */ + io = imsi_op_alloc(g_gc, gsup_msg.imsi, IMSI_OP_ISD); + break; + } + + imsi_op_rx_gsup(io, &gsup_msg); + msgb_free(msg); + + return 0; +} + +static void print_report(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(imsi_op_stats); i++) { + struct imsi_op_stats *st = &imsi_op_stats[i]; + const char *name = get_value_string(imsi_op_names, i); + printf("%s: %u alloc, %u released, %u success, %u error , %u tout\n", + name, st->num_alloc, st->num_released, st->num_rx_success, + st->num_rx_error, st->num_timeout); + } +} + +static void sig_cb(int sig) +{ + switch (sig) { + case SIGINT: + print_report(); + exit(0); + break; + } +} + +void *tall_bsc_ctx = NULL; + +/* default categories */ +static struct log_info_cat default_categories[] = { +}; + +static const struct log_info gsup_test_client_log_info = { + .cat = default_categories, + .num_cat = ARRAY_SIZE(default_categories), +}; + +int main(int argc, char **argv) +{ + unsigned long long i; + char *server_host = "127.0.0.1"; + uint16_t server_port = 2222; + + osmo_init_logging(&gsup_test_client_log_info); + + g_gc = gsup_client_create(server_host, server_port, gsupc_read_cb, + NULL); + + + signal(SIGINT, sig_cb); + + for (i = 0; i < 10000; i++) { + unsigned long long imsi = 901790000000000 + i; + char imsi_buf[17]; + snprintf(imsi_buf, sizeof(imsi_buf), "%015llu", imsi); + req_auth_info(imsi_buf); + osmo_select_main(0); + } + + while (1) { + osmo_select_main(0); + } + + print_report(); + exit(0); +} diff --git a/src/libcommon/oap_client.c b/src/libcommon/oap_client.c new file mode 100644 index 000000000..5128ac119 --- /dev/null +++ b/src/libcommon/oap_client.c @@ -0,0 +1,280 @@ +/* Osmocom Authentication Protocol API */ + +/* (C) 2015 by Sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Neels Hofmeyr + * + * 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 <osmocom/core/utils.h> +#include <osmocom/crypt/auth.h> +#include <osmocom/gsm/oap.h> + +#include <openbsc/oap_client.h> +#include <openbsc/debug.h> + +int oap_client_init(struct oap_client_config *config, + struct oap_client_state *state) +{ + OSMO_ASSERT(state->state == OAP_UNINITIALIZED); + + if (!config) + goto disable; + + if (config->client_id == 0) + goto disable; + + if (config->secret_k_present == 0) { + LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret K missing.\n"); + goto disable; + } + + if (config->secret_opc_present == 0) { + LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret OPC missing.\n"); + goto disable; + } + + state->client_id = config->client_id; + memcpy(state->secret_k, config->secret_k, sizeof(state->secret_k)); + memcpy(state->secret_opc, config->secret_opc, sizeof(state->secret_opc)); + state->state = OAP_INITIALIZED; + return 0; + +disable: + state->state = OAP_DISABLED; + return 0; +} + +/* From the given state and received RAND and AUTN octets, validate the + * server's authenticity and formulate the matching milenage reply octets in + * *tx_xres. The state is not modified. + * On success, and if tx_res is not NULL, exactly 8 octets will be written to + * *tx_res. If not NULL, tx_res must point at allocated memory of at least 8 + * octets. The caller will want to send XRES back to the server in a challenge + * response message and update the state. + * Return 0 on success; -1 if OAP is disabled; -2 if rx_random and rx_autn fail + * the authentication check; -3 for any other errors. */ +static int oap_evaluate_challenge(const struct oap_client_state *state, + const uint8_t *rx_random, + const uint8_t *rx_autn, + uint8_t *tx_xres) +{ + struct osmo_auth_vector vec; + + struct osmo_sub_auth_data auth = { + .type = OSMO_AUTH_TYPE_UMTS, + .algo = OSMO_AUTH_ALG_MILENAGE, + }; + + osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.k) + == sizeof(state->secret_k), _secret_k_size_match); + osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.opc) + == sizeof(state->secret_opc), _secret_opc_size_match); + + switch (state->state) { + case OAP_UNINITIALIZED: + case OAP_DISABLED: + return -1; + default: + break; + } + + memcpy(auth.u.umts.k, state->secret_k, sizeof(auth.u.umts.k)); + memcpy(auth.u.umts.opc, state->secret_opc, sizeof(auth.u.umts.opc)); + memset(auth.u.umts.amf, '\0', sizeof(auth.u.umts.amf)); + auth.u.umts.sqn = 41; /* TODO use incrementing sequence nr */ + + memset(&vec, 0, sizeof(vec)); + osmo_auth_gen_vec(&vec, &auth, rx_random); + + if (vec.res_len != 8) { + LOGP(DLOAP, LOGL_ERROR, "OAP: Expected XRES to be 8 octets, got %d\n", + vec.res_len); + return -3; + } + + if (osmo_constant_time_cmp(vec.autn, rx_autn, sizeof(vec.autn)) != 0) { + LOGP(DLOAP, LOGL_ERROR, "OAP: AUTN mismatch!\n"); + LOGP(DLOAP, LOGL_INFO, "OAP: AUTN from server: %s\n", + osmo_hexdump_nospc(rx_autn, sizeof(vec.autn))); + LOGP(DLOAP, LOGL_INFO, "OAP: AUTN expected: %s\n", + osmo_hexdump_nospc(vec.autn, sizeof(vec.autn))); + return -2; + } + + if (tx_xres != NULL) + memcpy(tx_xres, vec.res, 8); + return 0; +} + +struct msgb *oap_client_encoded(const struct osmo_oap_message *oap_msg) +{ + struct msgb *msg = msgb_alloc_headroom(1000, 64, __func__); + OSMO_ASSERT(msg); + osmo_oap_encode(msg, oap_msg); + return msg; +} + +/* Create a new msgb containing an OAP registration message. + * On error, return NULL. */ +static struct msgb* oap_msg_register(uint16_t client_id) +{ + struct osmo_oap_message oap_msg = {0}; + + if (client_id < 1) { + LOGP(DLOAP, LOGL_ERROR, "OAP: Invalid client ID: %d\n", client_id); + return NULL; + } + + oap_msg.message_type = OAP_MSGT_REGISTER_REQUEST; + oap_msg.client_id = client_id; + return oap_client_encoded(&oap_msg); +} + +int oap_client_register(struct oap_client_state *state, struct msgb **msg_tx) +{ + *msg_tx = oap_msg_register(state->client_id); + if (!(*msg_tx)) + return -1; + + state->state = OAP_REQUESTED_CHALLENGE; + return 0; +} + +/* Create a new msgb containing an OAP challenge response message. + * xres must point at 8 octets to return as challenge response. + * On error, return NULL. */ +static struct msgb* oap_msg_challenge_response(uint8_t *xres) +{ + struct osmo_oap_message oap_reply = {0}; + + oap_reply.message_type = OAP_MSGT_CHALLENGE_RESULT; + memcpy(oap_reply.xres, xres, sizeof(oap_reply.xres)); + oap_reply.xres_present = 1; + return oap_client_encoded(&oap_reply); +} + +static int handle_challenge(struct oap_client_state *state, + struct osmo_oap_message *oap_rx, + struct msgb **msg_tx) +{ + int rc; + uint8_t xres[8]; + + if (!(oap_rx->rand_present && oap_rx->autn_present)) { + LOGP(DLOAP, LOGL_ERROR, + "OAP challenge incomplete (rand_present: %d, autn_present: %d)\n", + oap_rx->rand_present, oap_rx->autn_present); + rc = -2; + goto failure; + } + + rc = oap_evaluate_challenge(state, + oap_rx->rand, + oap_rx->autn, + xres); + if (rc < 0) + goto failure; + + *msg_tx = oap_msg_challenge_response(xres); + if ((*msg_tx) == NULL) { + rc = -1; + goto failure; + } + + state->state = OAP_SENT_CHALLENGE_RESULT; + return 0; + +failure: + OSMO_ASSERT(rc < 0); + state->state = OAP_INITIALIZED; + return rc; +} + +int oap_client_handle(struct oap_client_state *state, + const struct msgb *msg_rx, struct msgb **msg_tx) +{ + uint8_t *data = msgb_l2(msg_rx); + size_t data_len = msgb_l2len(msg_rx); + struct osmo_oap_message oap_msg = {0}; + int rc = 0; + + *msg_tx = NULL; + + OSMO_ASSERT(data); + + rc = osmo_oap_decode(&oap_msg, data, data_len); + if (rc < 0) { + LOGP(DLOAP, LOGL_ERROR, + "Decoding OAP message failed with error '%s' (%d)\n", + get_value_string(gsm48_gmm_cause_names, -rc), -rc); + return -10; + } + + switch (state->state) { + case OAP_UNINITIALIZED: + LOGP(DLOAP, LOGL_ERROR, + "Received OAP message %d, but the OAP client is" + " not initialized\n", oap_msg.message_type); + return -ENOTCONN; + case OAP_DISABLED: + LOGP(DLOAP, LOGL_ERROR, + "Received OAP message %d, but the OAP client is" + " disabled\n", oap_msg.message_type); + return -ENOTCONN; + default: + break; + } + + switch (oap_msg.message_type) { + case OAP_MSGT_CHALLENGE_REQUEST: + return handle_challenge(state, &oap_msg, msg_tx); + + case OAP_MSGT_REGISTER_RESULT: + /* successfully registered */ + state->state = OAP_REGISTERED; + break; + + case OAP_MSGT_REGISTER_ERROR: + LOGP(DLOAP, LOGL_ERROR, + "OAP registration failed\n"); + state->state = OAP_INITIALIZED; + if (state->registration_failures < 3) { + state->registration_failures ++; + return oap_client_register(state, msg_tx); + } + return -11; + + case OAP_MSGT_REGISTER_REQUEST: + case OAP_MSGT_CHALLENGE_RESULT: + LOGP(DLOAP, LOGL_ERROR, + "Received invalid OAP message type for OAP client side: %d\n", + (int)oap_msg.message_type); + return -12; + + default: + LOGP(DLOAP, LOGL_ERROR, + "Unknown OAP message type: %d\n", + (int)oap_msg.message_type); + return -13; + } + + return 0; +} diff --git a/src/libcommon/socket.c b/src/libcommon/socket.c new file mode 100644 index 000000000..2a64767f8 --- /dev/null +++ b/src/libcommon/socket.c @@ -0,0 +1,111 @@ +/* OpenBSC sokcet code, taken from Abis input driver for ip.access */ + +/* (C) 2009 by Harald Welte <laforge@gnumonks.org> + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/fcntl.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> + +#include <osmocom/core/select.h> +#include <osmocom/gsm/tlv.h> +#include <osmocom/core/msgb.h> +#include <openbsc/debug.h> +#include <openbsc/gsm_data.h> +#include <osmocom/core/talloc.h> + +int make_sock(struct osmo_fd *bfd, int proto, + uint32_t ip, uint16_t port, int priv_nr, + int (*cb)(struct osmo_fd *fd, unsigned int what), void *data) +{ + struct sockaddr_in addr; + int ret, on = 1; + int type = SOCK_STREAM; + + switch (proto) { + case IPPROTO_TCP: + type = SOCK_STREAM; + break; + case IPPROTO_UDP: + type = SOCK_DGRAM; + break; +#ifdef IPPROTO_GRE + case IPPROTO_GRE: + type = SOCK_RAW; + break; +#endif + default: + return -EINVAL; + } + + bfd->fd = socket(AF_INET, type, proto); + bfd->cb = cb; + bfd->when = BSC_FD_READ; + bfd->data = data; + bfd->priv_nr = priv_nr; + + if (bfd->fd < 0) { + LOGP(DLINP, LOGL_ERROR, "could not create socket.\n"); + return -EIO; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (ip != INADDR_ANY) + addr.sin_addr.s_addr = htonl(ip); + else + addr.sin_addr.s_addr = INADDR_ANY; + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); + if (ret < 0) { + LOGP(DLINP, LOGL_ERROR, "could not bind socket %s\n", + strerror(errno)); + close(bfd->fd); + return -EIO; + } + + if (proto == IPPROTO_TCP) { + ret = listen(bfd->fd, 1); + if (ret < 0) { + perror("listen"); + close(bfd->fd); + return ret; + } + } + + ret = osmo_fd_register(bfd); + if (ret < 0) { + perror("register_listen_fd"); + close(bfd->fd); + return ret; + } + return 0; +} diff --git a/src/libcommon/talloc_ctx.c b/src/libcommon/talloc_ctx.c new file mode 100644 index 000000000..5e3d9aebe --- /dev/null +++ b/src/libcommon/talloc_ctx.c @@ -0,0 +1,56 @@ +/* OpenBSC allocation contexts initialization code */ +/* (C) 2011-2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * 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 <osmocom/core/talloc.h> +#include <osmocom/core/msgb.h> + +extern void *tall_bsc_ctx; +extern void *tall_fle_ctx; +extern void *tall_locop_ctx; +extern void *tall_authciphop_ctx; +extern void *tall_gsms_ctx; +extern void *tall_subscr_ctx; +extern void *tall_sub_req_ctx; +extern void *tall_call_ctx; +extern void *tall_paging_ctx; +extern void *tall_sigh_ctx; +extern void *tall_tqe_ctx; +extern void *tall_trans_ctx; +extern void *tall_map_ctx; +extern void *tall_upq_ctx; +extern void *tall_ctr_ctx; + +void talloc_ctx_init(void *ctx_root) +{ + msgb_talloc_ctx_init(ctx_root, 0); + tall_fle_ctx = talloc_named_const(ctx_root, 0, "bs11_file_list_entry"); + tall_locop_ctx = talloc_named_const(ctx_root, 0, "loc_updating_oper"); + tall_authciphop_ctx = talloc_named_const(ctx_root, 0, "auth_ciph_oper"); + tall_gsms_ctx = talloc_named_const(ctx_root, 0, "sms"); + tall_subscr_ctx = talloc_named_const(ctx_root, 0, "subscriber"); + tall_sub_req_ctx = talloc_named_const(ctx_root, 0, "subscr_request"); + tall_call_ctx = talloc_named_const(ctx_root, 0, "gsm_call"); + tall_paging_ctx = talloc_named_const(ctx_root, 0, "paging_request"); + tall_sigh_ctx = talloc_named_const(ctx_root, 0, "signal_handler"); + tall_tqe_ctx = talloc_named_const(ctx_root, 0, "subch_txq_entry"); + tall_trans_ctx = talloc_named_const(ctx_root, 0, "transaction"); + tall_map_ctx = talloc_named_const(ctx_root, 0, "trau_map_entry"); + tall_upq_ctx = talloc_named_const(ctx_root, 0, "trau_upq_entry"); + tall_ctr_ctx = talloc_named_const(ctx_root, 0, "counter"); +} |