From 61692adb2b0cd090c8fb8c81376a28bca099d079 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Fri, 20 May 2016 21:59:55 +0200 Subject: Implement IuCS (large refactoring and addition) osmo-nitb becomes osmo-msc add DIUCS debug log constant add iucs.[hc] add msc vty, remove nitb vty add libiudummy, to avoid linking Iu deps in tests Use new msc_tx_dtap() instead of gsm0808_submit_dtap() libmgcp: add mgcpgw client API bridge calls via mgcpgw mgcp: hack RAB success from nano3G: patch first RTP payload The ip.access nano3G needs the first RTP payload's first two bytes to read hex 'e400', or it will reject the RAB assignment. Add flag patched_first_rtp_payload to mgcp_rtp_state to detect the first RTP payload on a stream, and overwrite its first bytes with e400. This should probably be configurable, but seems to not harm other femto cells (as long as we patch only the first RTP payload in each stream). Only do this when sending to the BTS side. Change-Id: Ie13ff348117e892d41b8355ab6c24915301eaeaf --- openbsc/src/Makefile.am | 2 +- openbsc/src/gprs/gprs_gmm.c | 10 +- openbsc/src/gprs/gprs_sgsn.c | 1 + openbsc/src/gprs/sgsn_libgtp.c | 2 +- openbsc/src/gprs/sgsn_main.c | 9 - openbsc/src/gprs/sgsn_vty.c | 12 + openbsc/src/libbsc/bsc_init.c | 6 - openbsc/src/libbsc/bsc_vty.c | 30 -- openbsc/src/libbsc/gsm_04_08_utils.c | 1 + openbsc/src/libbsc/paging.c | 6 + openbsc/src/libcommon-cs/common_cs_vty.c | 30 ++ openbsc/src/libcommon/debug.c | 5 + openbsc/src/libcommon/gsm_data.c | 7 + openbsc/src/libiu/iu.c | 53 ++- openbsc/src/libiu/iu_vty.c | 72 +++- openbsc/src/libmgcp/Makefile.am | 3 + openbsc/src/libmgcp/mgcp_common.c | 54 +++ openbsc/src/libmgcp/mgcp_network.c | 101 +++++- openbsc/src/libmgcp/mgcp_protocol.c | 23 +- openbsc/src/libmgcp/mgcpgw_client.c | 549 ++++++++++++++++++++++++++++++ openbsc/src/libmgcp/mgcpgw_client_vty.c | 116 +++++++ openbsc/src/libmsc/Makefile.am | 4 + openbsc/src/libmsc/a_iface.c | 8 + openbsc/src/libmsc/gsm_04_08.c | 135 ++++++-- openbsc/src/libmsc/gsm_04_11.c | 7 +- openbsc/src/libmsc/gsm_04_80.c | 10 +- openbsc/src/libmsc/gsm_subscriber.c | 130 +++---- openbsc/src/libmsc/iucs.c | 191 +++++++++++ openbsc/src/libmsc/iucs_ranap.c | 106 ++++++ openbsc/src/libmsc/msc_ifaces.c | 240 ++++++++++++- openbsc/src/libmsc/msc_vty.c | 181 ++++++++++ openbsc/src/libmsc/osmo_msc.c | 72 ++-- openbsc/src/libmsc/silent_call.c | 3 +- openbsc/src/libmsc/subscr_conn.c | 62 +++- openbsc/src/libmsc/transaction.c | 24 +- openbsc/src/libmsc/vty_interface_layer3.c | 102 +----- openbsc/src/libvlr/vlr.c | 4 + openbsc/src/libvlr/vlr_access_req_fsm.c | 12 + openbsc/src/libvlr/vlr_lu_fsm.c | 12 + openbsc/src/osmo-bsc/osmo_bsc_api.c | 2 +- openbsc/src/osmo-msc/Makefile.am | 53 +++ openbsc/src/osmo-msc/msc_main.c | 521 ++++++++++++++++++++++++++++ openbsc/src/osmo-nitb/Makefile.am | 46 --- openbsc/src/osmo-nitb/bsc_hack.c | 416 ---------------------- 44 files changed, 2623 insertions(+), 810 deletions(-) create mode 100644 openbsc/src/libmgcp/mgcp_common.c create mode 100644 openbsc/src/libmgcp/mgcpgw_client.c create mode 100644 openbsc/src/libmgcp/mgcpgw_client_vty.c create mode 100644 openbsc/src/libmsc/iucs.c create mode 100644 openbsc/src/libmsc/iucs_ranap.c create mode 100644 openbsc/src/libmsc/msc_vty.c create mode 100644 openbsc/src/osmo-msc/Makefile.am create mode 100644 openbsc/src/osmo-msc/msc_main.c delete mode 100644 openbsc/src/osmo-nitb/Makefile.am delete mode 100644 openbsc/src/osmo-nitb/bsc_hack.c (limited to 'openbsc/src') diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am index c66f9e56b..7e9e1dca7 100644 --- a/openbsc/src/Makefile.am +++ b/openbsc/src/Makefile.am @@ -40,7 +40,7 @@ endif # Programs SUBDIRS += \ - osmo-nitb \ + osmo-msc \ osmo-bsc_mgcp \ utils \ ipaccess \ diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c index 08e0dc047..56e8c4fc8 100644 --- a/openbsc/src/gprs/gprs_gmm.c +++ b/openbsc/src/gprs/gprs_gmm.c @@ -295,6 +295,10 @@ static void msgid2mmctx(struct sgsn_mm_ctx *mm, const struct msgb *msg) mm->gb.nsei = msgb_nsei(msg); /* In case a Iu connection is reconnected we need to update the ue ctx */ mm->iu.ue_ctx = msg->dst; + if (mm->ran_type == MM_CTX_T_UTRAN_Iu + && mm->iu.ue_ctx) + mm->iu.ue_ctx->rab_assign_addr_enc = + sgsn->cfg.iu.rab_assign_addr_enc; } /* Store BVCI/NSEI in MM context */ @@ -978,7 +982,7 @@ void activate_pdp_rabs(struct sgsn_mm_ctx *ctx) if (ctx->ran_type != MM_CTX_T_UTRAN_Iu) return; llist_for_each_entry(pdp, &ctx->pdp_list, list) { - iu_rab_act_ps(pdp->nsapi, pdp, 1); + iu_rab_act_ps(pdp->nsapi, pdp); } } #endif @@ -2897,14 +2901,16 @@ int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli, } #ifdef BUILD_IU -int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp, bool use_x213_nsap) +int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp) { struct msgb *msg; struct sgsn_mm_ctx *mm = pdp->mm; struct ue_conn_ctx *uectx; uint32_t ggsn_ip; + bool use_x213_nsap; uectx = mm->iu.ue_ctx; + use_x213_nsap = (uectx->rab_assign_addr_enc == NSAP_ADDR_ENC_X213); /* Get the IP address for ggsn user plane */ memcpy(&ggsn_ip, pdp->lib->gsnru.v, pdp->lib->gsnru.l); diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c index 071dd97c8..18625aefe 100644 --- a/openbsc/src/gprs/gprs_sgsn.c +++ b/openbsc/src/gprs/gprs_sgsn.c @@ -247,6 +247,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx) ctx->ran_type = MM_CTX_T_UTRAN_Iu; ctx->iu.ue_ctx = uectx; + ctx->iu.ue_ctx->rab_assign_addr_enc = sgsn->cfg.iu.rab_assign_addr_enc; ctx->iu.new_key = 1; ctx->gmm_state = GMM_DEREGISTERED; ctx->pmm_state = PMM_DETACHED; diff --git a/openbsc/src/gprs/sgsn_libgtp.c b/openbsc/src/gprs/sgsn_libgtp.c index cd1093167..7595bf83c 100644 --- a/openbsc/src/gprs/sgsn_libgtp.c +++ b/openbsc/src/gprs/sgsn_libgtp.c @@ -407,7 +407,7 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) } else if (pctx->mm->ran_type == MM_CTX_T_UTRAN_Iu) { #ifdef BUILD_IU /* Activate a radio bearer */ - iu_rab_act_ps(pdp->nsapi, pctx, 1); + iu_rab_act_ps(pdp->nsapi, pctx); return 0; #else return -ENOTSUP; diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c index 221ee7976..f2f1344f6 100644 --- a/openbsc/src/gprs/sgsn_main.c +++ b/openbsc/src/gprs/sgsn_main.c @@ -316,12 +316,6 @@ static const struct log_info gprs_log_info = { .num_cat = ARRAY_SIZE(gprs_categories), }; -/* Implement the extern asn_debug from libasn1c to indicate whether the ASN.1 - * binary code decoded and encoded during Iu communication should be logged to - * stderr. See osmocom's libasn1c, asn_internal.h, at "if (asn_debug)": - * http://git.osmocom.org/libasn1c/tree/include/asn1c/asn_internal.h */ -int asn_debug = 0; - int sgsn_ranap_iu_event(struct ue_conn_ctx *ctx, enum iu_event_type type, void *data); int main(int argc, char **argv) @@ -349,9 +343,6 @@ int main(int argc, char **argv) osmo_stats_vty_add_cmds(&gprs_log_info); sgsn_vty_init(&sgsn_inst.cfg); ctrl_vty_init(tall_bsc_ctx); -#ifdef BUILD_IU - iu_vty_init(&asn_debug); -#endif handle_options(argc, argv); diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c index 1cefe37cf..2ec200bd3 100644 --- a/openbsc/src/gprs/sgsn_vty.c +++ b/openbsc/src/gprs/sgsn_vty.c @@ -44,6 +44,12 @@ #include +#include "../../bscconfig.h" + +#ifdef BUILD_IU +#include +#endif + static struct sgsn_config *g_cfg = NULL; const struct value_string sgsn_auth_pol_strs[] = { @@ -297,6 +303,8 @@ static int config_write_sgsn(struct vty *vty) } else vty_out(vty, " no compression v42bis%s", VTY_NEWLINE); + iu_vty_config_write(vty, " "); + return CMD_SUCCESS; } @@ -1284,6 +1292,10 @@ int sgsn_vty_init(struct sgsn_config *cfg) install_element(SGSN_NODE, &cfg_no_comp_v42bis_cmd); install_element(SGSN_NODE, &cfg_comp_v42bis_cmd); install_element(SGSN_NODE, &cfg_comp_v42bisp_cmd); + +#ifdef BUILD_IU + iu_vty_init(SGSN_NODE, &g_cfg->iu.rab_assign_addr_enc); +#endif return 0; } diff --git a/openbsc/src/libbsc/bsc_init.c b/openbsc/src/libbsc/bsc_init.c index e12b8801f..64dcd157b 100644 --- a/openbsc/src/libbsc/bsc_init.c +++ b/openbsc/src/libbsc/bsc_init.c @@ -516,12 +516,6 @@ int bsc_network_alloc(mncc_recv_cb_t mncc_recv) bsc_gsmnet->name_long = talloc_strdup(bsc_gsmnet, "OpenBSC"); bsc_gsmnet->name_short = talloc_strdup(bsc_gsmnet, "OpenBSC"); - /* TODO: move to libmsc when gsm_network is split between libbsc and - * libmsc */ - bsc_gsmnet->gsup_server_addr_str = talloc_strdup(bsc_gsmnet, - MSC_HLR_REMOTE_IP_DEFAULT); - bsc_gsmnet->gsup_server_port = MSC_HLR_REMOTE_PORT_DEFAULT; - return 0; } diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c index dcb1e6ee7..554b55298 100644 --- a/openbsc/src/libbsc/bsc_vty.c +++ b/openbsc/src/libbsc/bsc_vty.c @@ -2274,34 +2274,6 @@ DEFUN(cfg_bts_penalty_time_rsvd, cfg_bts_penalty_time_rsvd_cmd, return CMD_SUCCESS; } -DEFUN(cfg_bts_per_loc_upd, cfg_bts_per_loc_upd_cmd, - "periodic location update <6-1530>", - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval in Minutes\n") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.chan_desc.t3212 = atoi(argv[0]) / 6; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_per_loc_upd, cfg_bts_no_per_loc_upd_cmd, - "no periodic location update", - NO_STR - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval\n") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.chan_desc.t3212 = 0; - - return CMD_SUCCESS; -} - DEFUN(cfg_bts_radio_link_timeout, cfg_bts_radio_link_timeout_cmd, "radio-link-timeout <4-64>", "Radio link timeout criterion (BTS side)\n" @@ -4196,8 +4168,6 @@ int bsc_vty_init(struct gsm_network *network) install_element(BTS_NODE, &cfg_bts_rach_ec_allowed_cmd); install_element(BTS_NODE, &cfg_bts_rach_ac_class_cmd); install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd); - install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd); - install_element(BTS_NODE, &cfg_bts_no_per_loc_upd_cmd); install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd); install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd); install_element(BTS_NODE, &cfg_bts_cell_bar_qualify_cmd); diff --git a/openbsc/src/libbsc/gsm_04_08_utils.c b/openbsc/src/libbsc/gsm_04_08_utils.c index f9a613601..d68004109 100644 --- a/openbsc/src/libbsc/gsm_04_08_utils.c +++ b/openbsc/src/libbsc/gsm_04_08_utils.c @@ -270,6 +270,7 @@ int send_siemens_mrpci(struct gsm_lchan *lchan, return rsl_siemens_mrpci(lchan, &mrpci); } +/* TODO MSCSPLIT remove gsm48_handle_paging_resp() */ int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, struct msgb *msg, struct bsc_subscr *bsub) { diff --git a/openbsc/src/libbsc/paging.c b/openbsc/src/libbsc/paging.c index 8c1445cc6..e19c2d1c4 100644 --- a/openbsc/src/libbsc/paging.c +++ b/openbsc/src/libbsc/paging.c @@ -56,6 +56,12 @@ void *tall_paging_ctx; #define PAGING_TIMER 0, 500000 +/* + * TODO MSCSPLIT: the paging in libbsc is closely tied to MSC land in that the + * MSC realm callback functions used to be invoked from the BSC/BTS level. So + * this entire file needs to be rewired for use with an A interface. + */ + /* * Kill one paging request update the internal list... */ diff --git a/openbsc/src/libcommon-cs/common_cs_vty.c b/openbsc/src/libcommon-cs/common_cs_vty.c index 4b11f2c0a..ac732e7fa 100644 --- a/openbsc/src/libcommon-cs/common_cs_vty.c +++ b/openbsc/src/libcommon-cs/common_cs_vty.c @@ -296,6 +296,34 @@ DEFUN(cfg_net_no_timezone, return CMD_SUCCESS; } +DEFUN(cfg_net_per_loc_upd, cfg_net_per_loc_upd_cmd, + "periodic location update <6-1530>", + "Periodic Location Updating Interval\n" + "Periodic Location Updating Interval\n" + "Periodic Location Updating Interval\n" + "Periodic Location Updating Interval in Minutes\n") +{ + struct gsm_network *net = vty->index; + + net->t3212 = atoi(argv[0]) / 6; + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_no_per_loc_upd, cfg_net_no_per_loc_upd_cmd, + "no periodic location update", + NO_STR + "Periodic Location Updating Interval\n" + "Periodic Location Updating Interval\n" + "Periodic Location Updating Interval\n") +{ + struct gsm_network *net = vty->index; + + net->t3212 = 0; + + return CMD_SUCCESS; +} + static struct gsm_network *vty_global_gsm_network = NULL; /* initialize VTY elements used in both BSC and MSC */ @@ -325,6 +353,8 @@ int common_cs_vty_init(struct gsm_network *network, install_element(GSMNET_NODE, &cfg_net_timezone_cmd); install_element(GSMNET_NODE, &cfg_net_timezone_dst_cmd); install_element(GSMNET_NODE, &cfg_net_no_timezone_cmd); + install_element(GSMNET_NODE, &cfg_net_per_loc_upd_cmd); + install_element(GSMNET_NODE, &cfg_net_no_per_loc_upd_cmd); install_element(GSMNET_NODE, &cfg_net_dyn_ts_allow_tch_f_cmd); return CMD_SUCCESS; diff --git a/openbsc/src/libcommon/debug.c b/openbsc/src/libcommon/debug.c index dc79a843e..7dbbc6ac0 100644 --- a/openbsc/src/libcommon/debug.c +++ b/openbsc/src/libcommon/debug.c @@ -184,6 +184,11 @@ static const struct log_info_cat default_categories[] = { .description = "Visitor Location Register", .enabled = 1, .loglevel = LOGL_DEBUG, }, + [DIUCS] = { + .name = "DIUCS", + .description = "Iu-CS Protocol", + .enabled = 1, .loglevel = LOGL_DEBUG, + }, }; static int filter_fn(const struct log_context *ctx, struct log_target *tar) diff --git a/openbsc/src/libcommon/gsm_data.c b/openbsc/src/libcommon/gsm_data.c index f6fde37bd..b5bf0599e 100644 --- a/openbsc/src/libcommon/gsm_data.c +++ b/openbsc/src/libcommon/gsm_data.c @@ -449,3 +449,10 @@ bool classmark_is_r99(struct gsm_classmark *cm) rev_lev = (cm->classmark2[0] >> 5) & 0x3; return rev_lev >= 2; } + +const struct value_string ran_type_names[] = { + OSMO_VALUE_STRING(RAN_UNKNOWN), + OSMO_VALUE_STRING(RAN_GERAN_A), + OSMO_VALUE_STRING(RAN_UTRAN_IU), + { 0, NULL } +}; diff --git a/openbsc/src/libiu/iu.c b/openbsc/src/libiu/iu.c index 932b21718..5d56a4a37 100644 --- a/openbsc/src/libiu/iu.c +++ b/openbsc/src/libiu/iu.c @@ -80,7 +80,15 @@ struct iu_rnc { void *talloc_iu_ctx; -int asn1_xer_print = 1; +/* Implement the extern asn_debug from libasn1c to indicate whether to print + * asn.1 debug messages (see libasn1c). */ +int asn_debug = 0; + +/* Implement the extern asn1_xer_print to indicate whether the ASN.1 binary + * code decoded and encoded during Iu communication should be logged to stderr + * (see asn.1 generated code in osmo-iuh). */ +int asn1_xer_print = 0; + void *talloc_asn1_ctx; iu_recv_cb_t global_iu_recv_cb = NULL; @@ -241,6 +249,25 @@ int iu_tx_sec_mode_cmd(struct ue_conn_ctx *uectx, struct gsm_auth_tuple *tp, return 0; } +int iu_tx_common_id(struct ue_conn_ctx *uectx, const char *imsi) +{ + struct msgb *msg; + struct osmo_scu_prim *prim; + + LOGP(DRANAP, LOGL_INFO, "Transmitting RANAP CommonID (SUA link %p conn_id %u)\n", + uectx->link, uectx->conn_id); + + msg = ranap_new_msg_common_id(imsi); + msg->l2h = msg->data; + prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); + prim->u.data.conn_id = uectx->conn_id; + osmo_prim_init(&prim->oph, SCCP_SAP_USER, + OSMO_SCU_PRIM_N_DATA, + PRIM_OP_REQUEST, msg); + osmo_sua_user_link_down(uectx->link, &prim->oph); + return 0; +} + static int iu_grnc_id_parse(struct iu_grnc_id *dst, struct RANAP_GlobalRNC_ID *src) { @@ -375,20 +402,35 @@ int iu_tx(struct msgb *msg_nas, uint8_t sapi) return 0; } -static int ranap_handle_co_iu_rel_req(struct ue_conn_ctx *ctx, RANAP_Iu_ReleaseRequestIEs_t *ies) +/* Send Iu Release for the given UE connection. + * If cause is NULL, the standard "No remaining RAB" cause is sent, otherwise + * the provided cause. */ +int iu_tx_release(struct ue_conn_ctx *ctx, const struct RANAP_Cause *cause) { struct msgb *msg; struct osmo_scu_prim *prim; + static const struct RANAP_Cause default_cause = { + .present = RANAP_Cause_PR_radioNetwork, + .choice.radioNetwork = RANAP_CauseRadioNetwork_no_remaining_rab, + }; - LOGP(DRANAP, LOGL_INFO, "Received Iu Release Request, Sending Release Command\n"); - msg = ranap_new_msg_iu_rel_cmd(&ies->cause); + if (!cause) + cause = &default_cause; + + msg = ranap_new_msg_iu_rel_cmd(cause); msg->l2h = msg->data; prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); prim->u.data.conn_id = ctx->conn_id; osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, msg); - osmo_sua_user_link_down(ctx->link, &prim->oph); + return osmo_sua_user_link_down(ctx->link, &prim->oph); +} + +static int ranap_handle_co_iu_rel_req(struct ue_conn_ctx *ctx, RANAP_Iu_ReleaseRequestIEs_t *ies) +{ + LOGP(DRANAP, LOGL_INFO, "Received Iu Release Request, Sending Release Command\n"); + iu_tx_release(ctx, &ies->cause); return 0; } @@ -413,6 +455,7 @@ static int ranap_handle_co_rab_ass_resp(struct ue_conn_ctx *ctx, RANAP_RAB_Assig ranap_free_rab_setupormodifieditemies(&setup_ies); } + /* FIXME: handle RAB Ass failure? */ return rc; } diff --git a/openbsc/src/libiu/iu_vty.c b/openbsc/src/libiu/iu_vty.c index 91eed96be..73ad126ba 100644 --- a/openbsc/src/libiu/iu_vty.c +++ b/openbsc/src/libiu/iu_vty.c @@ -18,33 +18,91 @@ */ #include +#include +#include #include #include -/* Pointer to the actual asn_debug value as passed from main scopes. */ -static int *g_asn_debug_p = NULL; +#include + +static enum nsap_addr_enc *g_rab_assign_addr_enc = NULL; DEFUN(logging_asn_debug, logging_asn_debug_cmd, "logging asn1-debug (1|0)", LOGGING_STR + "Log ASN.1 debug messages to stderr\n" + "Log ASN.1 debug messages to stderr\n" + "Do not log ASN.1 debug messages to stderr\n") +{ + asn_debug = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(logging_asn_xer_print, + logging_asn_xer_print_cmd, + "logging asn1-xer-print (1|0)", + LOGGING_STR "Log human readable representations of all ASN.1 messages to stderr\n" "Log decoded ASN.1 messages to stderr\n" "Do not log decoded ASN.1 messages to stderr\n") { - if (!g_asn_debug_p) { - vty_out(vty, "%%ASN.1 debugging not available%s", VTY_NEWLINE); + asn1_xer_print = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_iu_rab_assign_addr_enc, cfg_iu_rab_assign_addr_enc_cmd, + "iu rab-assign-addr-enc (x213|v4raw)", + "Iu interface protocol options\n" + "Choose RAB Assignment's Transport Layer Address encoding\n" + "ITU-T X.213 compliant address encoding (default)\n" + "32bit length raw IPv4 address (for ip.access nano3G)\n") +{ + if (!g_rab_assign_addr_enc) { + vty_out(vty, "%%RAB Assignment Transport Layer Address" + " encoding not available%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (strcmp(argv[0], "v4raw") == 0) + *g_rab_assign_addr_enc = NSAP_ADDR_ENC_V4RAW; + else + *g_rab_assign_addr_enc = NSAP_ADDR_ENC_X213; + return CMD_SUCCESS; +} + +int iu_vty_config_write(struct vty *vty, const char *indent) +{ + if (!g_rab_assign_addr_enc) { + vty_out(vty, "%%RAB Assignment Transport Layer Address" + " encoding not available%s", VTY_NEWLINE); + return CMD_WARNING; + } + + switch (*g_rab_assign_addr_enc) { + case NSAP_ADDR_ENC_V4RAW: + vty_out(vty, "%siu rab-assign-addr-enc v4raw%s", indent, + VTY_NEWLINE); + break; + case NSAP_ADDR_ENC_X213: + /* default value, no need to write anything */ + break; + default: + LOGP(0, LOGL_ERROR, "Invalid value for" + " net.iu.rab_assign_addr_enc: %d\n", + *g_rab_assign_addr_enc); return CMD_WARNING; } - *g_asn_debug_p = atoi(argv[0]); return CMD_SUCCESS; } -void iu_vty_init(int *asn_debug_p) +void iu_vty_init(int iu_parent_node, enum nsap_addr_enc *rab_assign_addr_enc) { - g_asn_debug_p = asn_debug_p; + g_rab_assign_addr_enc = rab_assign_addr_enc; install_element(CFG_LOG_NODE, &logging_asn_debug_cmd); + install_element(CFG_LOG_NODE, &logging_asn_xer_print_cmd); + install_element(iu_parent_node, &cfg_iu_rab_assign_addr_enc_cmd); } diff --git a/openbsc/src/libmgcp/Makefile.am b/openbsc/src/libmgcp/Makefile.am index 5faf6027a..5d7844da4 100644 --- a/openbsc/src/libmgcp/Makefile.am +++ b/openbsc/src/libmgcp/Makefile.am @@ -30,11 +30,14 @@ noinst_HEADERS = \ $(NULL) libmgcp_a_SOURCES = \ + mgcp_common.c \ mgcp_protocol.c \ mgcp_network.c \ mgcp_vty.c \ mgcp_osmux.c \ mgcp_sdp.c \ + mgcpgw_client.c \ + mgcpgw_client_vty.c \ $(NULL) if BUILD_MGCP_TRANSCODING libmgcp_a_SOURCES += \ diff --git a/openbsc/src/libmgcp/mgcp_common.c b/openbsc/src/libmgcp/mgcp_common.c new file mode 100644 index 000000000..43c866768 --- /dev/null +++ b/openbsc/src/libmgcp/mgcp_common.c @@ -0,0 +1,54 @@ +/* Media Gateway Control Protocol Media Gateway: RFC 3435 */ +/* Implementations useful both for the MGCP GW as well as MGCP GW clients */ + +/* + * (C) 2016 by sysmocom s.m.f.c. GmbH + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include + +#include +#include + +const struct value_string mgcp_connection_mode_strs[] = { + { MGCP_CONN_NONE, "none" }, + { MGCP_CONN_RECV_SEND, "sendrecv" }, + { MGCP_CONN_SEND_ONLY, "sendonly" }, + { MGCP_CONN_RECV_ONLY, "recvonly" }, + { MGCP_CONN_LOOPBACK, "loopback" }, + { 0, NULL } +}; + +/* Ensure that the msg->l2h is NUL terminated. */ +int mgcp_msg_terminate_nul(struct msgb *msg) +{ + unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */ + if (tail[-1] == '\0') + /* nothing to do */; + else if (msgb_tailroom(msg) > 0) + tail[0] = '\0'; + else if (tail[-1] == '\r' || tail[-1] == '\n') + tail[-1] = '\0'; + else { + LOGP(DMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: " + "Length: %d, Buffer size: %d\n", + msgb_l2len(msg), msg->data_len); + return -ENOTSUP; + } + return 0; +} diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index abce6e49d..c9fe17973 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -537,7 +537,11 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *sta if (payload < 0) return; +#if 0 + DEBUGP(DMGCP, "Payload hdr payload %u -> endp payload %u\n", + rtp_hdr->payload_type, payload); rtp_hdr->payload_type = payload; +#endif } /* @@ -588,6 +592,14 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, struct mgcp_rtp_state *rtp_state; int tap_idx; + LOGP(DMGCP, LOGL_DEBUG, + "endpoint %x dest %s tcfg->audio_loop %d endp->conn_mode %d (== loopback: %d)\n", + ENDPOINT_NUMBER(endp), + dest == MGCP_DEST_NET? "net" : "bts", + tcfg->audio_loop, + endp->conn_mode, + endp->conn_mode == MGCP_CONN_LOOPBACK); + /* For loop toggle the destination and then dispatch. */ if (tcfg->audio_loop) dest = !dest; @@ -605,10 +617,35 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, rtp_state = &endp->net_state; tap_idx = MGCP_TAP_BTS_OUT; } + LOGP(DMGCP, LOGL_DEBUG, + "endpoint %x dest %s net_end %s %d %d bts_end %s %d %d rtp_end %s %d %d\n", + ENDPOINT_NUMBER(endp), + dest == MGCP_DEST_NET? "net" : "bts", + + inet_ntoa(endp->net_end.addr), + ntohs(endp->net_end.rtp_port), + ntohs(endp->net_end.rtcp_port), + + inet_ntoa(endp->bts_end.addr), + ntohs(endp->bts_end.rtp_port), + ntohs(endp->bts_end.rtcp_port), - if (!rtp_end->output_enabled) + inet_ntoa(rtp_end->addr), + ntohs(rtp_end->rtp_port), + ntohs(rtp_end->rtcp_port) + ); + + if (!rtp_end->output_enabled) { rtp_end->dropped_packets += 1; - else if (is_rtp) { + LOGP(DMGCP, LOGL_DEBUG, + "endpoint %x output disabled, drop to %s %s %d %d\n", + ENDPOINT_NUMBER(endp), + dest == MGCP_DEST_NET? "net" : "bts", + inet_ntoa(rtp_end->addr), + ntohs(rtp_end->rtp_port), + ntohs(rtp_end->rtcp_port) + ); + } else if (is_rtp) { int cont; int nbytes = 0; int len = rc; @@ -619,8 +656,17 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, break; mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, buf, len); + LOGP(DMGCP, LOGL_DEBUG, + "endpoint %x process/send to %s %s %d %d\n", + ENDPOINT_NUMBER(endp), + (dest == MGCP_DEST_NET)? "net" : "bts", + inet_ntoa(rtp_end->addr), + ntohs(rtp_end->rtp_port), + ntohs(rtp_end->rtcp_port) + ); forward_data(rtp_end->rtp.fd, &endp->taps[tap_idx], buf, len); + rc = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port, buf, len); @@ -632,6 +678,15 @@ int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, } while (len > 0); return nbytes; } else if (!tcfg->omit_rtcp) { + LOGP(DMGCP, LOGL_DEBUG, + "endpoint %x send to %s %s %d %d\n", + ENDPOINT_NUMBER(endp), + dest == MGCP_DEST_NET? "net" : "bts", + inet_ntoa(rtp_end->addr), + ntohs(rtp_end->rtp_port), + ntohs(rtp_end->rtcp_port) + ); + return mgcp_udp_send(rtp_end->rtcp.fd, &rtp_end->addr, rtp_end->rtcp_port, buf, rc); @@ -676,9 +731,28 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) if (rc <= 0) return -1; + LOGP(DMGCP, LOGL_DEBUG, + "endpoint %x", + ENDPOINT_NUMBER(endp)); + LOGPC(DMGCP, LOGL_DEBUG, + " from net %s %d", + inet_ntoa(addr.sin_addr), + ntohs(addr.sin_port)); + LOGPC(DMGCP, LOGL_DEBUG, + " net_end %s %d %d", + inet_ntoa(endp->net_end.addr), + ntohs(endp->net_end.rtp_port), + ntohs(endp->net_end.rtcp_port)); + LOGPC(DMGCP, LOGL_DEBUG, + " bts_end %s %d %d\n", + inet_ntoa(endp->bts_end.addr), + ntohs(endp->bts_end.rtp_port), + ntohs(endp->bts_end.rtcp_port) + ); + if (memcmp(&addr.sin_addr, &endp->net_end.addr, sizeof(addr.sin_addr)) != 0) { LOGP(DMGCP, LOGL_ERROR, - "Endpoint 0x%x data from wrong address %s vs. ", + "rtp_data_net: Endpoint 0x%x data from wrong address %s vs. ", ENDPOINT_NUMBER(endp), inet_ntoa(addr.sin_addr)); LOGPC(DMGCP, LOGL_ERROR, "%s\n", inet_ntoa(endp->net_end.addr)); @@ -691,7 +765,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) if (endp->net_end.rtp_port != addr.sin_port && endp->net_end.rtcp_port != addr.sin_port) { LOGP(DMGCP, LOGL_ERROR, - "Data from wrong source port %d on 0x%x\n", + "rtp_data_net: Data from wrong source port %d on 0x%x\n", ntohs(addr.sin_port), ENDPOINT_NUMBER(endp)); return -1; } @@ -701,6 +775,12 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what) break; } + LOGP(DMGCP, LOGL_DEBUG, + "rtp_data_net: Endpoint %x data from %s %d\n", + ENDPOINT_NUMBER(endp), + inet_ntoa(addr.sin_addr), + ntohs(addr.sin_port)); + /* throw away the dummy message */ if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) { LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from network on 0x%x\n", @@ -780,7 +860,7 @@ static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) if (memcmp(&endp->bts_end.addr, &addr.sin_addr, sizeof(addr.sin_addr)) != 0) { LOGP(DMGCP, LOGL_ERROR, - "Data from wrong bts %s on 0x%x\n", + "rtp_data_bts: Data from wrong bts %s on 0x%x\n", inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp)); return -1; } @@ -788,11 +868,17 @@ static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) if (endp->bts_end.rtp_port != addr.sin_port && endp->bts_end.rtcp_port != addr.sin_port) { LOGP(DMGCP, LOGL_ERROR, - "Data from wrong bts source port %d on 0x%x\n", + "rtp_data_bts: ata from wrong bts source port %d on 0x%x\n", ntohs(addr.sin_port), ENDPOINT_NUMBER(endp)); return -1; } + LOGP(DMGCP, LOGL_DEBUG, + "rtp_data_bts: Endpoint %x data from %s %d\n", + ENDPOINT_NUMBER(endp), + inet_ntoa(addr.sin_addr), + ntohs(addr.sin_port)); + /* throw away the dummy message */ if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) { LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from bts on 0x%x\n", @@ -808,6 +894,9 @@ static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) switch (endp->type) { case MGCP_RTP_DEFAULT: + LOGP(DMGCP, LOGL_DEBUG, + "rtp_data_bts: Endpoint %x MGCP_RTP_DEFAULT\n", + ENDPOINT_NUMBER(endp)); return mgcp_send(endp, MGCP_DEST_NET, proto == MGCP_PROTO_RTP, &addr, buf, rc); case MGCP_RTP_TRANSCODED: diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index 4fcadd949..78e41f193 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -318,26 +318,14 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) int i, code, handled = 0; struct msgb *resp = NULL; char *data; - unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */ if (msgb_l2len(msg) < 4) { LOGP(DMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len); return NULL; } - /* Ensure that the msg->l2h is NUL terminated. */ - if (tail[-1] == '\0') - /* nothing to do */; - else if (msgb_tailroom(msg) > 0) - tail[0] = '\0'; - else if (tail[-1] == '\r' || tail[-1] == '\n') - tail[-1] = '\0'; - else { - LOGP(DMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: " - "Length: %d, Buffer size: %d\n", - msgb_l2len(msg), msg->data_len); + if (mgcp_msg_terminate_nul(msg)) return NULL; - } /* attempt to treat it as a response */ if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) { @@ -547,6 +535,11 @@ static int parse_conn_mode(const char *msg, struct mgcp_endpoint *endp) endp->bts_end.output_enabled = endp->conn_mode & MGCP_CONN_RECV_ONLY ? 1 : 0; + LOGP(DMGCP, LOGL_DEBUG, "endpoint %x connection mode '%s' %d output_enabled net %d bts %d\n", + ENDPOINT_NUMBER(endp), + msg, endp->conn_mode, endp->net_end.output_enabled, + endp->bts_end.output_enabled); + return ret; } @@ -972,6 +965,8 @@ static struct msgb *handle_modify_con(struct mgcp_parse_data *p) break; case MGCP_POLICY_DEFER: /* stop processing */ + LOGP(DMGCP, LOGL_DEBUG, "endp %x MDCX defer\n", + ENDPOINT_NUMBER(endp)); return NULL; break; case MGCP_POLICY_CONT: @@ -1003,6 +998,8 @@ error3: out_silent: + LOGP(DMGCP, LOGL_DEBUG, "endp %x Modify endpoint: silent exit\n", + ENDPOINT_NUMBER(endp)); return NULL; } diff --git a/openbsc/src/libmgcp/mgcpgw_client.c b/openbsc/src/libmgcp/mgcpgw_client.c new file mode 100644 index 000000000..9f0c84de2 --- /dev/null +++ b/openbsc/src/libmgcp/mgcpgw_client.c @@ -0,0 +1,549 @@ +/* mgcp_utils - common functions to setup an MGCP connection + */ +/* (C) 2016 by sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +struct mgcpgw_client { + struct mgcpgw_client_conf actual; + uint32_t remote_addr; + struct osmo_wqueue wq; + mgcp_trans_id_t next_trans_id; + uint16_t next_endpoint; + struct llist_head responses_pending; +}; + +void mgcpgw_client_conf_init(struct mgcpgw_client_conf *conf) +{ + /* NULL and -1 default to MGCPGW_CLIENT_*_DEFAULT values */ + *conf = (struct mgcpgw_client_conf){ + .local_addr = NULL, + .local_port = -1, + .remote_addr = NULL, + .remote_port = -1, + }; +} + +unsigned int mgcpgw_client_next_endpoint(struct mgcpgw_client *client) +{ + return client->next_endpoint ++; +} + +static void mgcpgw_client_handle_response(struct mgcpgw_client *mgcp, + struct mgcp_response_pending *pending, + struct mgcp_response *response) +{ + if (!pending) { + LOGP(DMGCP, LOGL_ERROR, + "Cannot handle NULL response\n"); + return; + } + if (pending->response_cb) + pending->response_cb(response, pending->priv); + else + LOGP(DMGCP, LOGL_INFO, "MGCP response ignored (NULL cb)\n"); + talloc_free(pending); +} + +static int mgcp_response_parse_head(struct mgcp_response *r, struct msgb *msg) +{ + int comment_pos; + char *end; + + if (mgcp_msg_terminate_nul(msg)) + goto response_parse_failure; + + r->body = (char *)msg->data; + + if (sscanf(r->body, "%3d %u %n", + &r->head.response_code, &r->head.trans_id, + &comment_pos) != 2) + goto response_parse_failure; + + r->head.comment = r->body + comment_pos; + end = strchr(r->head.comment, '\r'); + if (!end) + goto response_parse_failure; + /* Mark the end of the comment */ + *end = '\0'; + r->body = end + 1; + if (r->body[0] == '\n') + r->body ++; + return 0; + +response_parse_failure: + LOGP(DMGCP, LOGL_ERROR, + "Failed to parse MGCP response header\n"); + return -EINVAL; +} + +/* TODO undup against mgcp_protocol.c:mgcp_check_param() */ +static bool mgcp_line_is_valid(const char *line) +{ + const size_t line_len = strlen(line); + if (line[0] == '\0') + return true; + + if (line_len < 2 + || line[1] != '=') { + LOGP(DMGCP, LOGL_ERROR, + "Wrong MGCP option format: '%s'\n", + line); + return false; + } + + return true; +} + +/* Parse a line like "m=audio 16002 RTP/AVP 98" */ +static int mgcp_parse_audio(struct mgcp_response *r, const char *line) +{ + if (sscanf(line, "m=audio %hu", + &r->audio_port) != 1) + goto response_parse_failure; + + return 0; + +response_parse_failure: + LOGP(DMGCP, LOGL_ERROR, + "Failed to parse MGCP response header\n"); + return -EINVAL; +} + +int mgcp_response_parse_params(struct mgcp_response *r) +{ + char *line; + int rc; + OSMO_ASSERT(r->body); + char *data = strstr(r->body, "\n\n"); + + if (!data) { + LOGP(DMGCP, LOGL_ERROR, + "MGCP response: cannot find start of parameters\n"); + return -EINVAL; + } + + /* Advance to after the \n\n, replace the second \n with \0. That's + * where the parameters start. */ + data ++; + *data = '\0'; + data ++; + + for_each_line(line, data) { + if (!mgcp_line_is_valid(line)) + return -EINVAL; + + switch (line[0]) { + case 'm': + rc = mgcp_parse_audio(r, line); + if (rc) + return rc; + break; + default: + /* skip unhandled parameters */ + break; + } + } + return 0; +} + +static struct mgcp_response_pending *mgcpgw_client_response_pending_get( + struct mgcpgw_client *mgcp, + struct mgcp_response *r) +{ + struct mgcp_response_pending *pending; + if (!r) + return NULL; + llist_for_each_entry(pending, &mgcp->responses_pending, entry) { + if (pending->trans_id == r->head.trans_id) { + llist_del(&pending->entry); + return pending; + } + } + return NULL; +} + +/* Feed an MGCP message into the receive processing. + * Parse the head and call any callback registered for the transaction id found + * in the MGCP message. This is normally called directly from the internal + * mgcp_do_read that reads from the socket connected to the MGCP gateway. This + * function is published mainly to be able to feed data from the test suite. + */ +int mgcpgw_client_rx(struct mgcpgw_client *mgcp, struct msgb *msg) +{ + struct mgcp_response r = { 0 }; + struct mgcp_response_pending *pending; + int rc; + + rc = mgcp_response_parse_head(&r, msg); + if (rc) { + LOGP(DMGCP, LOGL_ERROR, "Cannot parse MGCP response\n"); + return -1; + } + + pending = mgcpgw_client_response_pending_get(mgcp, &r); + if (!pending) { + LOGP(DMGCP, LOGL_ERROR, + "Cannot find matching MGCP transaction for trans_id %d\n", + r.head.trans_id); + return -1; + } + + mgcpgw_client_handle_response(mgcp, pending, &r); + return 0; +} + +static int mgcp_do_read(struct osmo_fd *fd) +{ + struct mgcpgw_client *mgcp = fd->data; + struct msgb *msg; + int ret; + + msg = msgb_alloc_headroom(4096, 128, "mgcp_from_gw"); + if (!msg) { + LOGP(DMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n"); + return -1; + } + + ret = read(fd->fd, msg->data, 4096 - 128); + if (ret <= 0) { + LOGP(DMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno)); + msgb_free(msg); + return -1; + } else if (ret > 4096 - 128) { + LOGP(DMGCP, LOGL_ERROR, "Too much data: %d\n", ret); + msgb_free(msg); + return -1; + } + + msg->l2h = msgb_put(msg, ret); + ret = mgcpgw_client_rx(mgcp, msg); + talloc_free(msg); + return ret; +} + +static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg) +{ + int ret; + static char strbuf[4096]; + unsigned int l = msg->len < sizeof(strbuf)-1 ? msg->len : sizeof(strbuf)-1; + strncpy(strbuf, (const char*)msg->data, l); + strbuf[l] = '\0'; + DEBUGP(DMGCP, "Tx MGCP msg to MGCP GW: '%s'\n", strbuf); + + LOGP(DMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len); + + ret = write(fd->fd, msg->data, msg->len); + if (ret != msg->len) + LOGP(DMGCP, LOGL_ERROR, "Failed to forward message to MGCP" + " GW: %s\n", strerror(errno)); + + return ret; +} + +struct mgcpgw_client *mgcpgw_client_init(void *ctx, + struct mgcpgw_client_conf *conf) +{ + struct mgcpgw_client *mgcp; + + mgcp = talloc_zero(ctx, struct mgcpgw_client); + + INIT_LLIST_HEAD(&mgcp->responses_pending); + + mgcp->next_trans_id = 1; + mgcp->next_endpoint = 1; + + mgcp->actual.local_addr = conf->local_addr ? conf->local_addr : + MGCPGW_CLIENT_LOCAL_ADDR_DEFAULT; + mgcp->actual.local_port = conf->local_port >= 0 ? (uint16_t)conf->local_port : + MGCPGW_CLIENT_LOCAL_PORT_DEFAULT; + + mgcp->actual.remote_addr = conf->remote_addr ? conf->remote_addr : + MGCPGW_CLIENT_REMOTE_ADDR_DEFAULT; + mgcp->actual.remote_port = conf->remote_port >= 0 ? (uint16_t)conf->remote_port : + MGCPGW_CLIENT_REMOTE_PORT_DEFAULT; + + return mgcp; +} + +int mgcpgw_client_connect(struct mgcpgw_client *mgcp) +{ + int on; + struct sockaddr_in addr; + struct osmo_wqueue *wq; + int rc; + + if (!mgcp) { + LOGP(DMGCP, LOGL_FATAL, "MGCPGW client not initialized properly\n"); + return -EINVAL; + } + + wq = &mgcp->wq; + + wq->bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); + if (wq->bfd.fd < 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno); + return -errno; + } + + on = 1; + if (setsockopt(wq->bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { + LOGP(DMGCP, LOGL_FATAL, + "Failed to initialize socket for MGCP GW: %s\n", + strerror(errno)); + rc = -errno; + goto error_close_fd; + } + + /* bind socket */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + inet_aton(mgcp->actual.local_addr, &addr.sin_addr); + addr.sin_port = htons(mgcp->actual.local_port); + if (bind(wq->bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + LOGP(DMGCP, LOGL_FATAL, + "Failed to bind for MGCP GW to %s %u\n", + mgcp->actual.local_addr, mgcp->actual.local_port); + rc = -errno; + goto error_close_fd; + } + + /* connect to the remote */ + inet_aton(mgcp->actual.remote_addr, &addr.sin_addr); + addr.sin_port = htons(mgcp->actual.remote_port); + if (connect(wq->bfd.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + LOGP(DMGCP, LOGL_FATAL, + "Failed to connect to MGCP GW at %s %u: %s\n", + mgcp->actual.remote_addr, mgcp->actual.remote_port, + strerror(errno)); + rc = -errno; + goto error_close_fd; + } + + mgcp->remote_addr = htonl(addr.sin_addr.s_addr); + + osmo_wqueue_init(wq, 10); + wq->bfd.when = BSC_FD_READ; + wq->bfd.data = mgcp; + wq->read_cb = mgcp_do_read; + wq->write_cb = mgcp_do_write; + + if (osmo_fd_register(&wq->bfd) != 0) { + LOGP(DMGCP, LOGL_FATAL, "Failed to register BFD\n"); + rc = -EIO; + goto error_close_fd; + } + LOGP(DMGCP, LOGL_INFO, "MGCP GW connection: %s:%u -> %s:%u\n", + mgcp->actual.local_addr, mgcp->actual.local_port, + mgcp->actual.remote_addr, mgcp->actual.remote_port); + + return 0; +error_close_fd: + close(wq->bfd.fd); + wq->bfd.fd = -1; + return rc; +} + +const char *mgcpgw_client_remote_addr_str(struct mgcpgw_client *mgcp) +{ + return mgcp->actual.remote_addr; +} + +uint16_t mgcpgw_client_remote_port(struct mgcpgw_client *mgcp) +{ + return mgcp->actual.remote_port; +} + +/* Return the MGCP GW binary IPv4 address in network byte order. */ +uint32_t mgcpgw_client_remote_addr_n(struct mgcpgw_client *mgcp) +{ + return mgcp->remote_addr; +} + +struct mgcp_response_pending * mgcpgw_client_pending_add( + struct mgcpgw_client *mgcp, + mgcp_trans_id_t trans_id, + mgcp_response_cb_t response_cb, + void *priv) +{ + struct mgcp_response_pending *pending; + + pending = talloc_zero(mgcp, struct mgcp_response_pending); + pending->trans_id = trans_id; + pending->response_cb = response_cb; + pending->priv = priv; + llist_add_tail(&pending->entry, &mgcp->responses_pending); + + return pending; +} + +/* Send the MGCP message in msg to the MGCP GW and handle a response with + * response_cb. NOTE: the response_cb still needs to call + * mgcp_response_parse_params(response) to get the parsed parameters -- to + * potentially save some CPU cycles, only the head line has been parsed when + * the response_cb is invoked. */ +int mgcpgw_client_tx(struct mgcpgw_client *mgcp, struct msgb *msg, + mgcp_response_cb_t response_cb, void *priv) +{ + struct mgcp_response_pending *pending; + mgcp_trans_id_t trans_id; + int rc; + + trans_id = msg->cb[MSGB_CB_MGCP_TRANS_ID]; + if (!trans_id) { + LOGP(DMGCP, LOGL_ERROR, + "Unset transaction id in mgcp send request\n"); + talloc_free(msg); + return -EINVAL; + } + + pending = mgcpgw_client_pending_add(mgcp, trans_id, response_cb, priv); + + if (msgb_l2len(msg) > 4096) { + LOGP(DMGCP, LOGL_ERROR, + "Cannot send, MGCP message too large: %u\n", + msgb_l2len(msg)); + msgb_free(msg); + rc = -EINVAL; + goto mgcp_tx_error; + } + + rc = osmo_wqueue_enqueue(&mgcp->wq, msg); + if (rc) { + LOGP(DMGCP, LOGL_FATAL, "Could not queue message to MGCP GW\n"); + msgb_free(msg); + goto mgcp_tx_error; + } else + LOGP(DMGCP, LOGL_INFO, "Queued %u bytes for MGCP GW\n", + msgb_l2len(msg)); + return 0; + +mgcp_tx_error: + /* Pass NULL to response cb to indicate an error */ + mgcpgw_client_handle_response(mgcp, pending, NULL); + return -1; +} + +static struct msgb *mgcp_msg_from_buf(mgcp_trans_id_t trans_id, + const char *buf, int len) +{ + struct msgb *msg; + + if (len > (4096 - 128)) { + LOGP(DMGCP, LOGL_ERROR, "Cannot send to MGCP GW:" + " message too large: %d\n", len); + return NULL; + } + + msg = msgb_alloc_headroom(4096, 128, "MGCP tx"); + OSMO_ASSERT(msg); + + char *dst = (char*)msgb_put(msg, len); + memcpy(dst, buf, len); + msg->l2h = msg->data; + msg->cb[MSGB_CB_MGCP_TRANS_ID] = trans_id; + + return msg; +} + +static struct msgb *mgcp_msg_from_str(mgcp_trans_id_t trans_id, + const char *fmt, ...) +{ + static char compose[4096 - 128]; + va_list ap; + int len; + OSMO_ASSERT(fmt); + + va_start(ap, fmt); + len = vsnprintf(compose, sizeof(compose), fmt, ap); + va_end(ap); + if (len >= sizeof(compose)) { + LOGP(DMGCP, LOGL_ERROR, + "Message too large: trans_id=%u len=%d\n", + trans_id, len); + return NULL; + } + if (len < 1) { + LOGP(DMGCP, LOGL_ERROR, + "Failed to compose message: trans_id=%u len=%d\n", + trans_id, len); + return NULL; + } + return mgcp_msg_from_buf(trans_id, compose, len); +} + +static mgcp_trans_id_t mgcpgw_client_next_trans_id(struct mgcpgw_client *mgcp) +{ + /* avoid zero trans_id to distinguish from unset trans_id */ + if (!mgcp->next_trans_id) + mgcp->next_trans_id ++; + return mgcp->next_trans_id ++; +} + +struct msgb *mgcp_msg_crcx(struct mgcpgw_client *mgcp, + uint16_t rtp_endpoint, unsigned int call_id, + enum mgcp_connection_mode mode) +{ + mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp); + return mgcp_msg_from_str(trans_id, + "CRCX %u %x@mgw MGCP 1.0\r\n" + "C: %x\r\n" + "L: p:20, a:AMR, nt:IN\r\n" + "M: %s\r\n" + , + trans_id, + rtp_endpoint, + call_id, + mgcp_cmode_name(mode)); +} + +struct msgb *mgcp_msg_mdcx(struct mgcpgw_client *mgcp, + uint16_t rtp_endpoint, const char *rtp_conn_addr, + uint16_t rtp_port, enum mgcp_connection_mode mode) + +{ + mgcp_trans_id_t trans_id = mgcpgw_client_next_trans_id(mgcp); + return mgcp_msg_from_str(trans_id, + "MDCX %u %x@mgw MGCP 1.0\r\n" + "M: %s\r\n" + "\r\n" + "c=IN IP4 %s\r\n" + "m=audio %u RTP/AVP 255\r\n" + , + trans_id, + rtp_endpoint, + mgcp_cmode_name(mode), + rtp_conn_addr, + rtp_port); +} diff --git a/openbsc/src/libmgcp/mgcpgw_client_vty.c b/openbsc/src/libmgcp/mgcpgw_client_vty.c new file mode 100644 index 000000000..a42ee4e5d --- /dev/null +++ b/openbsc/src/libmgcp/mgcpgw_client_vty.c @@ -0,0 +1,116 @@ +/* MGCPGW client interface to quagga VTY */ +/* (C) 2016 by sysmocom s.m.f.c. GmbH + * Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c) + * (C) 2009 by Harald Welte + * (C) 2009-2011 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 . + * + */ + +#include +#include +#include + +#include + +#include +#include + +#define MGCPGW_STR "MGCP gateway configuration for RTP streams\n" + +struct mgcpgw_client_conf *global_mgcpgw_client_conf = NULL; + +DEFUN(cfg_mgcpgw_local_ip, cfg_mgcpgw_local_ip_cmd, + "mgcpgw local-ip A.B.C.D", + MGCPGW_STR "local bind to connect to MGCP gateway with\n" + "local bind IP address\n") +{ + if (!global_mgcpgw_client_conf) + return CMD_ERR_NOTHING_TODO; + global_mgcpgw_client_conf->local_addr = + talloc_strdup(gsmnet_from_vty(vty), argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcpgw_local_port, cfg_mgcpgw_local_port_cmd, + "mgcpgw local-port <0-65535>", + MGCPGW_STR "local bind to connect to MGCP gateway with\n" + "local bind port\n") +{ + if (!global_mgcpgw_client_conf) + return CMD_ERR_NOTHING_TODO; + global_mgcpgw_client_conf->local_port = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcpgw_remote_ip, cfg_mgcpgw_remote_ip_cmd, + "mgcpgw remote-ip A.B.C.D", + MGCPGW_STR "remote bind to connect to MGCP gateway with\n" + "remote bind IP address\n") +{ + if (!global_mgcpgw_client_conf) + return CMD_ERR_NOTHING_TODO; + global_mgcpgw_client_conf->remote_addr = + talloc_strdup(gsmnet_from_vty(vty), argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcpgw_remote_port, cfg_mgcpgw_remote_port_cmd, + "mgcpgw remote-port <0-65535>", + MGCPGW_STR "remote bind to connect to MGCP gateway with\n" + "remote bind port\n") +{ + if (!global_mgcpgw_client_conf) + return CMD_ERR_NOTHING_TODO; + global_mgcpgw_client_conf->remote_port = atoi(argv[0]); + return CMD_SUCCESS; +} + +int mgcpgw_client_config_write(struct vty *vty, const char *indent) +{ + const char *addr; + int port; + + addr = global_mgcpgw_client_conf->local_addr; + if (addr) + vty_out(vty, "%smgcpgw local-ip %s%s", indent, addr, + VTY_NEWLINE); + port = global_mgcpgw_client_conf->local_port; + if (port >= 0) + vty_out(vty, "%smgcpgw local-port %u%s", indent, + (uint16_t)port, VTY_NEWLINE); + + addr = global_mgcpgw_client_conf->remote_addr; + if (addr) + vty_out(vty, "%smgcpgw remote-ip %s%s", indent, addr, + VTY_NEWLINE); + port = global_mgcpgw_client_conf->remote_port; + if (port >= 0) + vty_out(vty, "%smgcpgw remote-port %u%s", indent, + (uint16_t)port, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +void mgcpgw_client_vty_init(int node, struct mgcpgw_client_conf *conf) +{ + global_mgcpgw_client_conf = conf; + + install_element(node, &cfg_mgcpgw_local_ip_cmd); + install_element(node, &cfg_mgcpgw_local_port_cmd); + install_element(node, &cfg_mgcpgw_remote_ip_cmd); + install_element(node, &cfg_mgcpgw_remote_port_cmd); +} diff --git a/openbsc/src/libmsc/Makefile.am b/openbsc/src/libmsc/Makefile.am index 7ab30d029..a320f7d28 100644 --- a/openbsc/src/libmsc/Makefile.am +++ b/openbsc/src/libmsc/Makefile.am @@ -12,6 +12,7 @@ AM_CFLAGS = \ $(COVERAGE_CFLAGS) \ $(LIBCRYPTO_CFLAGS) \ $(LIBSMPP34_CFLAGS) \ + $(LIBASN1C_CFLAGS) \ $(NULL) noinst_HEADERS = \ @@ -25,11 +26,14 @@ noinst_LIBRARIES = \ libmsc_a_SOURCES = \ a_iface.c \ auth.c \ + msc_vty.c \ db.c \ gsm_04_08.c \ gsm_04_11.c \ gsm_04_80.c \ gsm_subscriber.c \ + iucs.c \ + iucs_ranap.c \ mncc.c \ mncc_builtin.c \ mncc_sock.c \ diff --git a/openbsc/src/libmsc/a_iface.c b/openbsc/src/libmsc/a_iface.c index 1f471f97b..caf9d4b06 100644 --- a/openbsc/src/libmsc/a_iface.c +++ b/openbsc/src/libmsc/a_iface.c @@ -35,6 +35,14 @@ int a_tx(struct msgb *msg) return -1; } +int a_page(const char *imsi, uint32_t tmsi, uint16_t lac) +{ + LOGP(DMSC, LOGL_ERROR, "Paging to be sent to BSC, but A-interface" + " not implemented: IMSI %s TMSI 0x%08x LAC %u\n", + imsi, tmsi, lac); + return -1; +} + int msc_gsm0808_tx_cipher_mode(struct gsm_subscriber_connection *conn, int cipher, const uint8_t *key, int len, int include_imeisv) { diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c index f8d47e4da..67eb5c135 100644 --- a/openbsc/src/libmsc/gsm_04_08.c +++ b/openbsc/src/libmsc/gsm_04_08.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "bscconfig.h" @@ -69,6 +70,10 @@ #include #include #include +#include + +#include +#include #include @@ -113,7 +118,7 @@ static int gsm48_conn_sendmsg(struct msgb *msg, struct gsm_subscriber_connection gh->proto_discr = trans->protocol | (trans->transaction_id << 4); } - return gsm0808_submit_dtap(conn, msg, 0, 0); + return msc_tx_dtap(conn, msg); } int gsm48_cc_tx_notify_ss(struct gsm_trans *trans, const char *message) @@ -149,7 +154,7 @@ void gsm0408_clear_all_trans(struct gsm_network *net, int protocol) } /* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */ -int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause) +static int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause) { struct msgb *msg; @@ -192,12 +197,17 @@ static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, len = gsm48_generate_mid_from_imsi(mi, conn->vsub->imsi); mid = msgb_put(msg, len); memcpy(mid, mi, len); + DEBUGP(DMM, "-> %s LOCATION UPDATE ACCEPT\n", + vlr_subscr_name(conn->vsub)); } else { /* Include the TMSI, which means that the MS will send a * TMSI REALLOCATION COMPLETE, and we should wait for * that until T3250 expiration */ mid = msgb_put(msg, GSM48_MID_TMSI_LEN); gsm48_generate_mid_from_tmsi(mid, send_tmsi); + DEBUGP(DMM, "-> %s LOCATION UPDATE ACCEPT (TMSI = 0x%08x)\n", + vlr_subscr_name(conn->vsub), + send_tmsi); } /* TODO: Follow-on proceed */ /* TODO: CTS permission */ @@ -205,7 +215,6 @@ static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, /* TODO: Emergency Number List */ /* TODO: Per-MS T3312 */ - DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n"); return gsm48_conn_sendmsg(msg, conn, NULL); } @@ -265,11 +274,11 @@ int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg) uint8_t mi_type; char mi_string[GSM48_MI_SIZE]; enum vlr_lu_type vlr_lu_type = VLR_LU_TYPE_REGULAR; - uint32_t tmsi; char *imsi; struct osmo_location_area_id old_lai, new_lai; struct osmo_fsm_inst *lu_fsm; + bool is_utran; int rc; lu = (struct gsm48_loc_upd_req *) gh->data; @@ -332,19 +341,21 @@ int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg) &old_lai.plmn.mnc, &old_lai.lac); new_lai.plmn.mcc = conn->network->country_code; new_lai.plmn.mnc = conn->network->network_code; - new_lai.lac = conn->bts->location_area_code; + new_lai.lac = conn->lac; DEBUGP(DMM, "LU/new-LAC: %u/%u\n", old_lai.lac, new_lai.lac); + is_utran = (conn->via_ran == RAN_UTRAN_IU); lu_fsm = vlr_loc_update(conn->conn_fsm, SUBSCR_CONN_E_ACCEPTED, SUBSCR_CONN_E_CN_CLOSE, (void*)&conn_from_lu, net->vlr, conn, vlr_lu_type, tmsi, imsi, &old_lai, &new_lai, - conn->network->authentication_required, - conn->network->a5_encryption, + is_utran || conn->network->authentication_required, + is_utran? VLR_CIPH_A5_3 + : conn->network->a5_encryption, classmark_is_r99(&conn->classmark), - conn->via_ran == RAN_UTRAN_IU, + is_utran, net->vlr->cfg.assign_tmsi); if (!lu_fsm) { DEBUGP(DRR, "%s: Can't start LU FSM\n", mi_string); @@ -643,11 +654,12 @@ int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *ms uint8_t mi_len = *(classmark2 + classmark2_len); uint8_t *mi = (classmark2 + classmark2_len + 1); struct osmo_location_area_id lai; + bool is_utran; int rc; lai.plmn.mcc = conn->network->country_code; lai.plmn.mnc = conn->network->network_code; - lai.lac = conn->bts->location_area_code; + lai.lac = conn->lac; DEBUGP(DMM, "<- CM SERVICE REQUEST "); if (msg->data_len < sizeof(struct gsm48_service_request*)) { @@ -708,16 +720,18 @@ int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *ms send_siemens_mrpci(msg->lchan, classmark2-1); #endif + is_utran = (conn->via_ran == RAN_UTRAN_IU); vlr_proc_acc_req(conn->conn_fsm, SUBSCR_CONN_E_ACCEPTED, SUBSCR_CONN_E_CN_CLOSE, (void*)&conn_from_cm_service_req, net->vlr, conn, VLR_PR_ARQ_T_CM_SERV_REQ, mi-1, &lai, - conn->network->authentication_required, - conn->network->a5_encryption, + is_utran || conn->network->authentication_required, + is_utran? VLR_CIPH_A5_3 + : conn->network->a5_encryption, classmark_is_r99(&conn->classmark), - conn->via_ran == RAN_UTRAN_IU); + is_utran); return 0; } @@ -1062,6 +1076,7 @@ static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct m char mi_string[GSM48_MI_SIZE]; int rc = 0; struct osmo_location_area_id lai; + bool is_utran; lai.plmn.mcc = conn->network->country_code; lai.plmn.mnc = conn->network->network_code; @@ -1087,18 +1102,20 @@ static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct m memcpy(conn->classmark.classmark2, classmark2_lv+1, *classmark2_lv); conn->classmark.classmark2_len = *classmark2_lv; + is_utran = (conn->via_ran == RAN_UTRAN_IU); vlr_proc_acc_req(conn->conn_fsm, SUBSCR_CONN_E_ACCEPTED, SUBSCR_CONN_E_CN_CLOSE, (void*)&conn_from_paging_resp, net->vlr, conn, VLR_PR_ARQ_T_PAGING_RESP, mi_lv, &lai, - conn->network->authentication_required, - conn->network->a5_encryption, + is_utran || conn->network->authentication_required, + is_utran? VLR_CIPH_A5_3 + : conn->network->a5_encryption, classmark_is_r99(&conn->classmark), - conn->via_ran == RAN_UTRAN_IU); + is_utran); - return rc; + return 0; } static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct msgb *msg) @@ -1553,8 +1570,7 @@ static int tch_bridge(struct gsm_network *net, struct gsm_mncc_bridge *bridge) /* through-connect channel */ return tch_map(trans1->conn->lchan, trans2->conn->lchan); #else - /* not implemented yet! */ - return -1; + return msc_call_bridge(trans1, trans2); #endif } @@ -1969,15 +1985,18 @@ static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); + msc_call_assignment(trans); + return mncc_recvmsg(trans->net, trans, MNCC_CALL_CONF_IND, &call_conf); } -static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg) +static int gsm48_cc_tx_call_proc_and_assign(struct gsm_trans *trans, void *arg) { struct gsm_mncc *proceeding = arg; struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC PROC"); struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + int rc; gh->msg_type = GSM48_MT_CC_CALL_PROC; @@ -1993,7 +2012,11 @@ static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg) if (proceeding->fields & MNCC_F_PROGRESS) gsm48_encode_progress(msg, 0, &proceeding->progress); - return gsm48_conn_sendmsg(msg, trans->conn, trans); + rc = gsm48_conn_sendmsg(msg, trans->conn, trans); + if (rc) + return rc; + + return msc_call_assignment(trans); } static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) @@ -3073,7 +3096,7 @@ static struct downstate { } downstatelist[] = { /* mobile originating call establishment */ {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */ - MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc}, + MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc_and_assign}, {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */ MNCC_ALERT_REQ, gsm48_cc_tx_alerting}, {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */ @@ -3304,15 +3327,15 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) trans_free(trans); return 0; } - /* store setup informations until paging was successfull */ + /* store setup information until paging succeeds */ memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc)); /* Request a channel */ - trans->paging_request = subscr_request_channel( + trans->paging_request = subscr_request_conn( vsub, - RSL_CHANNEED_TCH_F, setup_trig_pag_evt, - trans); + trans, + "MNCC: establish call"); if (!trans->paging_request) { LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n"); vlr_subscr_put(vsub); @@ -3323,7 +3346,7 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) return 0; } - /* Assign lchan */ + /* Assign conn */ trans->conn = msc_subscr_conn_get(conn); vlr_subscr_put(vsub); } else { @@ -3577,6 +3600,16 @@ int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) return -EACCES; } + if (conn->vsub && conn->vsub->cs.attached_via_ran != conn->via_ran) { + LOGP(DMM, LOGL_ERROR, + "%s: Illegal situation: RAN type mismatch:" + " attached via %s, received message via %s\n", + vlr_subscr_name(conn->vsub), + ran_type_name(conn->vsub->cs.attached_via_ran), + ran_type_name(conn->via_ran)); + return -EACCES; + } + #if 0 if (silent_call_reroute(conn, msg)) return silent_call_rx(conn, msg); @@ -3660,7 +3693,13 @@ static int msc_vlr_tx_lu_rej(void *msc_conn_ref, uint8_t cause) static int msc_vlr_tx_cm_serv_acc(void *msc_conn_ref) { struct gsm_subscriber_connection *conn = msc_conn_ref; - return gsm48_tx_mm_serv_ack(conn); + return msc_gsm48_tx_mm_serv_ack(conn); +} + +static int msc_vlr_tx_common_id(void *msc_conn_ref) +{ + struct gsm_subscriber_connection *conn = msc_conn_ref; + return msc_tx_common_id(conn); } /* VLR asks us to transmit a CM Service Reject */ @@ -3694,7 +3733,7 @@ static int msc_vlr_tx_cm_serv_rej(void *msc_conn_ref, enum vlr_proc_arq_result r break; }; - return gsm48_tx_mm_serv_rej(conn, cause); + return msc_gsm48_tx_mm_serv_rej(conn, cause); } /* VLR asks us to start using ciphering */ @@ -3722,9 +3761,41 @@ static int msc_vlr_set_ciph_mode(void *msc_conn_ref, return -EINVAL; } - /* TODO: MSCSPLIT: don't directly push BSC buttons */ - return gsm0808_cipher_mode(conn, ciph, tuple->vec.kc, 8, - retrieve_imeisv); + switch (conn->via_ran) { + case RAN_GERAN_A: + DEBUGP(DMM, "-> CIPHER MODE COMMAND %s\n", + vlr_subscr_name(conn->vsub)); + return msc_gsm0808_tx_cipher_mode(conn, ciph, tuple->vec.kc, 8, + retrieve_imeisv); + case RAN_UTRAN_IU: + DEBUGP(DMM, "-> SECURITY MODE CONTROL %s\n", + vlr_subscr_name(conn->vsub)); + return iu_tx_sec_mode_cmd(conn->iu.ue_ctx, tuple, 0, 1); + + default: + break; + } + LOGP(DMM, LOGL_ERROR, + "%s: cannot start ciphering, unknown RAN type %d\n", + vlr_subscr_name(conn->vsub), conn->via_ran); + return -ENOTSUP; +} + +void msc_rx_sec_mode_compl(struct gsm_subscriber_connection *conn) +{ + struct vlr_ciph_result vlr_res = {}; + + if (!conn || !conn->vsub) { + LOGP(DMM, LOGL_ERROR, + "Rx Security Mode Complete for invalid conn\n"); + return; + } + + DEBUGP(DMM, "<- SECURITY MODE COMPLETE %s\n", + vlr_subscr_name(conn->vsub)); + + vlr_res.cause = VLR_CIPH_COMPL; + vlr_subscr_rx_ciph_res(conn->vsub, &vlr_res); } /* VLR informs us that the subscriber data has somehow been modified */ @@ -3740,6 +3811,7 @@ static void msc_vlr_subscr_assoc(void *msc_conn_ref, struct gsm_subscriber_connection *conn = msc_conn_ref; OSMO_ASSERT(!conn->vsub); conn->vsub = vlr_subscr_get(vsub); + conn->vsub->cs.attached_via_ran = conn->via_ran; } /* operations that we need to implement for libvlr */ @@ -3752,6 +3824,7 @@ static const struct vlr_ops msc_vlr_ops = { .tx_cm_serv_acc = msc_vlr_tx_cm_serv_acc, .tx_cm_serv_rej = msc_vlr_tx_cm_serv_rej, .set_ciph_mode = msc_vlr_set_ciph_mode, + .tx_common_id = msc_vlr_tx_common_id, .subscr_update = msc_vlr_subscr_update, .subscr_assoc = msc_vlr_subscr_assoc, }; diff --git a/openbsc/src/libmsc/gsm_04_11.c b/openbsc/src/libmsc/gsm_04_11.c index 3255a3b6f..bdf2ad7cc 100644 --- a/openbsc/src/libmsc/gsm_04_11.c +++ b/openbsc/src/libmsc/gsm_04_11.c @@ -55,7 +55,7 @@ #include #include #include -#include +#include #include #include @@ -128,7 +128,7 @@ static int gsm411_sendmsg(struct gsm_subscriber_connection *conn, struct msgb *m { DEBUGP(DLSMS, "GSM4.11 TX %s\n", osmo_hexdump(msg->data, msg->len)); msg->l3h = msg->data; - return gsm0808_submit_dtap(conn, msg, UM_SAPI_SMS, 1); + return msc_tx_dtap(conn, msg); } /* Prefix msg with a 04.08/04.11 CP header */ @@ -1016,8 +1016,7 @@ int gsm411_send_sms_subscr(struct vlr_subscr *vsub, /* if not, we have to start paging */ LOGP(DLSMS, LOGL_DEBUG, "Sending SMS: no connection open, start paging %s\n", vlr_subscr_name(vsub)); - res = subscr_request_conn(vsub, RSL_CHANNEED_SDCCH, paging_cb_send_sms, - sms); + res = subscr_request_conn(vsub, paging_cb_send_sms, sms, "send SMS"); if (!res) { send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, GSM_PAGING_BUSY); sms_free(sms); diff --git a/openbsc/src/libmsc/gsm_04_80.c b/openbsc/src/libmsc/gsm_04_80.c index 479d6fbd2..bec1d26f4 100644 --- a/openbsc/src/libmsc/gsm_04_80.c +++ b/openbsc/src/libmsc/gsm_04_80.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include @@ -106,7 +106,7 @@ int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, | (1<<7); /* TI direction = 1 */ gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; - return gsm0808_submit_dtap(conn, msg, 0, 0); + return msc_tx_dtap(conn, msg); } int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, @@ -135,7 +135,7 @@ int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */ gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; - return gsm0808_submit_dtap(conn, msg, 0, 0); + return msc_tx_dtap(conn, msg); } int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, const char *text) @@ -143,7 +143,7 @@ int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, cons struct msgb *msg = gsm0480_create_ussd_notify(level, text); if (!msg) return -1; - return gsm0808_submit_dtap(conn, msg, 0, 0); + return msc_tx_dtap(conn, msg); } int msc_send_ussd_release_complete(struct gsm_subscriber_connection *conn) @@ -151,5 +151,5 @@ int msc_send_ussd_release_complete(struct gsm_subscriber_connection *conn) struct msgb *msg = gsm0480_create_ussd_release_complete(); if (!msg) return -1; - return gsm0808_submit_dtap(conn, msg, 0, 0); + return msc_tx_dtap(conn, msg); } diff --git a/openbsc/src/libmsc/gsm_subscriber.c b/openbsc/src/libmsc/gsm_subscriber.c index f425058f0..f92101863 100644 --- a/openbsc/src/libmsc/gsm_subscriber.c +++ b/openbsc/src/libmsc/gsm_subscriber.c @@ -40,33 +40,16 @@ #include #include #include +#include +#include +#include void *tall_sub_req_ctx; int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, gsm_cbfn *cb, void *cb_data); -static struct bsc_subscr *vlr_subscr_to_bsc_sub(struct llist_head *bsc_subscribers, - struct vlr_subscr *vsub) -{ - struct bsc_subscr *sub; - /* TODO MSC split -- creating a BSC subscriber directly from MSC data - * structures in RAM. At some point the MSC will send a message to the - * BSC instead. */ - sub = bsc_subscr_find_or_create_by_imsi(bsc_subscribers, vsub->imsi); - sub->tmsi = vsub->tmsi; - sub->lac = vsub->lac; - return sub; -} - -#if 0 -TODO implement paging response in libmsc! -Excluding this to be able to link without libbsc: - -/* - * We got the channel assigned and can now hand this channel - * over to one of our callbacks. - */ +/* A connection is established and the paging callbacks may run now. */ int subscr_paging_dispatch(unsigned int hooknum, unsigned int event, struct msgb *msg, void *data, void *param) { @@ -74,38 +57,42 @@ int subscr_paging_dispatch(unsigned int hooknum, unsigned int event, struct gsm_subscriber_connection *conn = data; struct vlr_subscr *vsub = param; struct paging_signal_data sig_data; - struct bsc_subscr *bsub; - struct gsm_network *net; - OSMO_ASSERT(vsub && vsub->cs.is_paging); - net = vsub->vlr->user_ctx; + OSMO_ASSERT(vsub); + OSMO_ASSERT(hooknum == GSM_HOOK_RR_PAGING); + OSMO_ASSERT(!(conn && (conn->vsub != vsub))); + OSMO_ASSERT(!((event == GSM_PAGING_SUCCEEDED) && !conn)); + + LOGP(DPAG, LOGL_DEBUG, "Paging %s for %s (event=%d)\n", + event == GSM_PAGING_SUCCEEDED ? "success" : "failure", + vlr_subscr_name(vsub), event); - /* - * Stop paging on all other BTS. E.g. if this is - * the first timeout on a BTS then the others will - * timeout soon as well. Let's just stop everything - * and forget we wanted to page. - */ + if (!vsub->cs.is_paging) { + LOGP(DPAG, LOGL_ERROR, + "Paging Response received for subscriber" + " that is not paging.\n"); + return -EINVAL; + } - bsub = vlr_subscr_to_bsc_sub(conn->network->bsc_subscribers, vsub); - paging_request_stop(&net->bts_list, NULL, bsub, NULL, NULL); - bsc_subscr_put(bsub); + if (event == GSM_PAGING_SUCCEEDED) + msc_stop_paging(vsub); /* Inform parts of the system we don't know */ - sig_data.vsub = vsub; - sig_data.bts = conn ? conn->bts : NULL; - sig_data.conn = conn; + sig_data.vsub = vsub; + sig_data.conn = conn; sig_data.paging_result = event; - osmo_signal_dispatch( - SS_PAGING, - event == GSM_PAGING_SUCCEEDED ? - S_PAGING_SUCCEEDED : S_PAGING_EXPIRED, - &sig_data - ); + osmo_signal_dispatch(SS_PAGING, + event == GSM_PAGING_SUCCEEDED ? + S_PAGING_SUCCEEDED : S_PAGING_EXPIRED, + &sig_data); llist_for_each_entry_safe(request, tmp, &vsub->cs.requests, entry) { llist_del(&request->entry); - request->cbfn(hooknum, event, msg, data, request->param); + if (request->cbfn) { + LOGP(DPAG, LOGL_DEBUG, "Calling paging cbfn.\n"); + request->cbfn(hooknum, event, msg, data, request->param); + } else + LOGP(DPAG, LOGL_DEBUG, "Paging without action.\n"); talloc_free(request); } @@ -115,29 +102,48 @@ int subscr_paging_dispatch(unsigned int hooknum, unsigned int event, return 0; } -struct subscr_request *subscr_request_channel(struct vlr_subscr *vsub, - int channel_type, - gsm_cbfn *cbfn, void *param) +int msc_paging_request(struct vlr_subscr *vsub) +{ + /* The subscriber was last seen in subscr->lac. Find out which + * BSCs/RNCs are responsible and send them a paging request via open + * SCCP connections (if any). */ + /* TODO Implementing only RNC paging, since this is code on the iu branch. + * Need to add BSC paging at some point. */ + switch (vsub->cs.attached_via_ran) { + case RAN_GERAN_A: + return a_page(vsub->imsi, vsub->tmsi, vsub->lac); + case RAN_UTRAN_IU: + return iu_page_cs(vsub->imsi, + vsub->tmsi == GSM_RESERVED_TMSI? + NULL : &vsub->tmsi, + vsub->lac); + default: + break; + } + + LOGP(DPAG, LOGL_ERROR, "%s: Cannot page, subscriber not attached\n", + vlr_subscr_name(vsub)); + return -EINVAL; +} + +/*! \brief Start a paging request for vsub, call cbfn(param) when done. + * \param vsub subscriber to page. + * \param cbfn function to call when the conn is established. + * \param param caller defined param to pass to cbfn(). + * \param label human readable label of the request kind used for logging. + */ +struct subscr_request *subscr_request_conn(struct vlr_subscr *vsub, + gsm_cbfn *cbfn, void *param, + const char *label) { int rc; struct subscr_request *request; - struct bsc_subscr *bsub; - struct gsm_network *net = vsub->vlr->user_ctx; /* Start paging.. we know it is async so we can do it before */ if (!vsub->cs.is_paging) { - LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet.\n", + LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet, start paging.\n", vlr_subscr_name(vsub)); -#if 0 - TODO implement paging response in libmsc! - Excluding this to be able to link without libbsc: - - bsub = vlr_subscr_to_bsc_sub(net->bsc_subscribers, vsub); - rc = paging_request(net, bsub, channel_type, NULL, NULL); - bsc_subscr_put(bsub); -#else - rc = -ENOTSUP; -#endif + rc = msc_paging_request(vsub); if (rc <= 0) { LOGP(DMM, LOGL_ERROR, "Subscriber %s paging failed: %d\n", vlr_subscr_name(vsub), rc); @@ -146,6 +152,9 @@ struct subscr_request *subscr_request_channel(struct vlr_subscr *vsub, /* reduced on the first paging callback */ vlr_subscr_get(vsub); vsub->cs.is_paging = true; + } else { + LOGP(DMM, LOGL_DEBUG, "Subscriber %s already paged.\n", + vlr_subscr_name(vsub)); } /* TODO: Stop paging in case of memory allocation failure */ @@ -177,4 +186,3 @@ struct gsm_subscriber_connection *connection_for_subscr(struct vlr_subscr *vsub) return NULL; } -#endif diff --git a/openbsc/src/libmsc/iucs.c b/openbsc/src/libmsc/iucs.c new file mode 100644 index 000000000..aeda1406a --- /dev/null +++ b/openbsc/src/libmsc/iucs.c @@ -0,0 +1,191 @@ +/* Code to manage MSC subscriber connections over IuCS interface */ + +/* + * (C) 2016,2017 by sysmocom s.f.m.c. GmbH + * + * Author: Neels Hofmeyr + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +/* For A-interface see libbsc/bsc_api.c subscr_con_allocate() */ +static struct gsm_subscriber_connection *subscr_conn_allocate_iu(struct gsm_network *network, + struct ue_conn_ctx *ue, + uint16_t lac) +{ + struct gsm_subscriber_connection *conn; + + DEBUGP(DIUCS, "Allocating IuCS subscriber conn: lac %d, link_id %p, conn_id %" PRIx32 "\n", + lac, ue->link, ue->conn_id); + + conn = talloc_zero(network, struct gsm_subscriber_connection); + if (!conn) + return NULL; + + conn->network = network; + conn->via_ran = RAN_UTRAN_IU; + conn->iu.ue_ctx = ue; + conn->iu.ue_ctx->rab_assign_addr_enc = network->iu.rab_assign_addr_enc; + conn->lac = lac; + + llist_add_tail(&conn->entry, &network->subscr_conns); + return conn; +} + +static int same_ue_conn(struct ue_conn_ctx *a, struct ue_conn_ctx *b) +{ + if (a == b) + return 1; + return (a->link == b->link) + && (a->conn_id == b->conn_id); +} + +static inline void log_subscribers(struct gsm_network *network) +{ + if (!log_check_level(DIUCS, LOGL_DEBUG)) + return; + + struct gsm_subscriber_connection *conn; + int i = 0; + llist_for_each_entry(conn, &network->subscr_conns, entry) { + DEBUGP(DIUCS, "%3d: %s", i, vlr_subscr_name(conn->vsub)); + switch (conn->via_ran) { + case RAN_UTRAN_IU: + DEBUGPC(DIUCS, " Iu"); + if (conn->iu.ue_ctx) { + DEBUGPC(DIUCS, " link %p, conn_id %d", + conn->iu.ue_ctx->link, + conn->iu.ue_ctx->conn_id + ); + } + break; + case RAN_GERAN_A: + DEBUGPC(DIUCS, " A"); + /* TODO log A-interface connection details */ + break; + case RAN_UNKNOWN: + DEBUGPC(DIUCS, " ?"); + break; + default: + DEBUGPC(DIUCS, " invalid"); + break; + } + DEBUGPC(DIUCS, "\n"); + i++; + } + DEBUGP(DIUCS, "subscribers registered: %d\n", i); +} + +/* Return an existing IuCS subscriber connection record for the given link and + * connection IDs, or return NULL if not found. */ +struct gsm_subscriber_connection *subscr_conn_lookup_iu( + struct gsm_network *network, + struct ue_conn_ctx *ue) +{ + struct gsm_subscriber_connection *conn; + + DEBUGP(DIUCS, "Looking for IuCS subscriber: link_id %p, conn_id %" PRIx32 "\n", + ue->link, ue->conn_id); + log_subscribers(network); + + llist_for_each_entry(conn, &network->subscr_conns, entry) { + if (conn->via_ran != RAN_UTRAN_IU) + continue; + if (!same_ue_conn(conn->iu.ue_ctx, ue)) + continue; + DEBUGP(DIUCS, "Found IuCS subscriber for link_id %p, conn_id %" PRIx32 "\n", + ue->link, ue->conn_id); + return conn; + } + DEBUGP(DIUCS, "No IuCS subscriber found for link_id %p, conn_id %" PRIx32 "\n", + ue->link, ue->conn_id); + return NULL; +} + +/* Receive MM/CC/... message from IuCS (SCCP user SAP). + * msg->dst must reference a struct ue_conn_ctx, which identifies the peer that + * sent the msg. + * + * For A-interface see libbsc/bsc_api.c gsm0408_rcvmsg(). */ +int gsm0408_rcvmsg_iucs(struct gsm_network *network, struct msgb *msg, + uint16_t *lac) +{ + int rc; + struct ue_conn_ctx *ue_ctx; + struct gsm_subscriber_connection *conn; + + ue_ctx = (struct ue_conn_ctx*)msg->dst; + + /* TODO: are there message types that could allow us to skip this + * search? */ + conn = subscr_conn_lookup_iu(network, ue_ctx); + + if (conn && lac && (conn->lac != *lac)) { + LOGP(DIUCS, LOGL_ERROR, "IuCS subscriber has changed LAC" + " within the same connection, discarding connection:" + " %s from LAC %d to %d\n", + vlr_subscr_name(conn->vsub), conn->lac, *lac); + /* Deallocate conn with previous LAC */ + msc_subscr_conn_close(conn, GSM_CAUSE_INV_MAND_INFO); + /* At this point we could be tolerant and allocate a new + * connection, but changing the LAC within the same connection + * is shifty. Rather cancel everything. */ + return -1; + } + + if (conn) { + /* Make sure we don't receive RR over IuCS; otherwise all + * messages handled by gsm0408_dispatch() are of interest (CC, + * MM, SMS, NS_SS, maybe even MM_GPRS and SM_GPRS). */ + struct gsm48_hdr *gh = msgb_l3(msg); + uint8_t pdisc = gh->proto_discr & 0x0f; + OSMO_ASSERT(pdisc != GSM48_PDISC_RR); + + msc_dtap(conn, ue_ctx->conn_id, msg); + rc = 0; + } else { + /* allocate a new connection */ + + if (!lac) { + LOGP(DIUCS, LOGL_ERROR, "New IuCS subscriber" + " but no LAC available. Expecting an InitialUE" + " message containing a LAI IE." + " Dropping connection.\n"); + return -1; + } + + conn = subscr_conn_allocate_iu(network, ue_ctx, *lac); + if (!conn) + abort(); + + /* ownership of conn hereby goes to the MSC: */ + rc = msc_compl_l3(conn, msg, 0); + } + + return rc; +} diff --git a/openbsc/src/libmsc/iucs_ranap.c b/openbsc/src/libmsc/iucs_ranap.c new file mode 100644 index 000000000..b69d52bc7 --- /dev/null +++ b/openbsc/src/libmsc/iucs_ranap.c @@ -0,0 +1,106 @@ +/* Implementation of RANAP messages to/from an MSC via an Iu-CS interface. + * This keeps direct RANAP dependencies out of libmsc. */ + +/* (C) 2016 by sysmocom s.m.f.c. GmbH + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* To continue authorization after a Security Mode Complete */ +int gsm0408_authorize(struct gsm_subscriber_connection *conn); + +static int iucs_rx_rab_assign(struct gsm_subscriber_connection *conn, + RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies) +{ + uint8_t rab_id; + RANAP_RAB_SetupOrModifiedItem_t *item = &setup_ies->raB_SetupOrModifiedItem; + + rab_id = item->rAB_ID.buf[0]; + + LOGP(DIUCS, LOGL_NOTICE, + "Received RAB assignment event for %s rab_id=%hhd\n", + vlr_subscr_name(conn->vsub), rab_id); + + return 0; +} + +int iucs_rx_sec_mode_compl(struct gsm_subscriber_connection *conn, + RANAP_SecurityModeCompleteIEs_t *ies) +{ + OSMO_ASSERT(conn->via_ran == RAN_UTRAN_IU); + + /* TODO evalute ies */ + + if (conn->iu.integrity_protection) + LOGP(DIUCS, LOGL_NOTICE, "Integrity Protection" + " was already enabled for %s\n", + vlr_subscr_name(conn->vsub)); + + conn->iu.integrity_protection = INTEGRITY_PROTECTION_IK; + + msc_rx_sec_mode_compl(conn); + return 0; +} + +int iucs_rx_ranap_event(struct gsm_network *network, + struct ue_conn_ctx *ue_ctx, int type, void *data) +{ + struct gsm_subscriber_connection *conn; + + conn = subscr_conn_lookup_iu(network, ue_ctx); + + if (!conn) { + LOGP(DRANAP, LOGL_ERROR, "Cannot find subscriber for IU event %u\n", type); + return -1; + } + + switch (type) { + case IU_EVENT_IU_RELEASE: + case IU_EVENT_LINK_INVALIDATED: + LOGP(DIUCS, LOGL_INFO, "IuCS release for %s\n", + vlr_subscr_name(conn->vsub)); + msc_subscr_conn_close(conn, 0); + return 0; + + case IU_EVENT_SECURITY_MODE_COMPLETE: + LOGP(DIUCS, LOGL_INFO, "IuCS security mode complete for %s\n", + vlr_subscr_name(conn->vsub)); + return iucs_rx_sec_mode_compl(conn, + (RANAP_SecurityModeCompleteIEs_t*)data); + case IU_EVENT_RAB_ASSIGN: + return iucs_rx_rab_assign(conn, + (RANAP_RAB_SetupOrModifiedItemIEs_t*)data); + default: + LOGP(DIUCS, LOGL_NOTICE, "Unknown message received:" + " RANAP event: %i\n", type); + return -1; + } +} diff --git a/openbsc/src/libmsc/msc_ifaces.c b/openbsc/src/libmsc/msc_ifaces.c index 500c99c2e..1a7d878a2 100644 --- a/openbsc/src/libmsc/msc_ifaces.c +++ b/openbsc/src/libmsc/msc_ifaces.c @@ -23,9 +23,25 @@ #include #include #include +#include +#include +#include +#include +#include +#include + +#include "../../bscconfig.h" + +extern struct msgb *ranap_new_msg_rab_assign_voice(uint8_t rab_id, + uint32_t rtp_ip, + uint16_t rtp_port, + bool use_x213_nsap); static int msc_tx(struct gsm_subscriber_connection *conn, struct msgb *msg) { + DEBUGP(DMSC, "msc_tx %u bytes to %s via %s\n", + msg->len, vlr_subscr_name(conn->vsub), + ran_type_name(conn->via_ran)); switch (conn->via_ran) { case RAN_GERAN_A: msg->dst = conn; @@ -60,7 +76,8 @@ int msc_gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn) gh->proto_discr = GSM48_PDISC_MM; gh->msg_type = GSM48_MT_MM_CM_SERV_ACC; - DEBUGP(DMM, "-> CM SERVICE ACCEPT\n"); + DEBUGP(DMM, "-> CM SERVICE ACCEPT %s\n", + vlr_subscr_name(conn->vsub)); return msc_tx_dtap(conn, msg); } @@ -70,6 +87,7 @@ int msc_gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, enum gsm48_reject_value value) { struct msgb *msg; + conn->received_cm_service_request = false; msg = gsm48_create_mm_serv_rej(value); if (!msg) { @@ -81,3 +99,223 @@ int msc_gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, return msc_tx_dtap(conn, msg); } + +int msc_tx_common_id(struct gsm_subscriber_connection *conn) +{ + /* Common ID is only sent over IuCS */ + if (conn->via_ran != RAN_UTRAN_IU) { + LOGP(DMM, LOGL_INFO, + "%s: Asked to transmit Common ID, but skipping" + " because this is not on UTRAN\n", + vlr_subscr_name(conn->vsub)); + return 0; + } + +#ifdef BUILD_IU + DEBUGP(DIUCS, "%s: tx CommonID %s\n", + vlr_subscr_name(conn->vsub), conn->vsub->imsi); + return iu_tx_common_id(conn->iu.ue_ctx, conn->vsub->imsi); +#else + LOGP(DMM, LOGL_ERROR, + "Cannot send CommonID: RAN_UTRAN_IU but IuCS support not built\n"); + return -ENOTSUP; +#endif +} + +#ifdef BUILD_IU +static void iu_rab_act_cs(struct ue_conn_ctx *uectx, uint8_t rab_id, + uint32_t rtp_ip, uint16_t rtp_port) +{ + struct msgb *msg; + bool use_x213_nsap; + uint32_t conn_id = uectx->conn_id; + + use_x213_nsap = (uectx->rab_assign_addr_enc == NSAP_ADDR_ENC_X213); + + LOGP(DIUCS, LOGL_DEBUG, "Assigning RAB: conn_id=%u, rab_id=%d," + " rtp=%x:%u, use_x213_nsap=%d\n", conn_id, rab_id, rtp_ip, + rtp_port, use_x213_nsap); + + msg = ranap_new_msg_rab_assign_voice(rab_id, rtp_ip, rtp_port, + use_x213_nsap); + msg->l2h = msg->data; + + if (iu_rab_act(uectx, msg)) + LOGP(DIUCS, LOGL_ERROR, "Failed to send RAB Assignment:" + " conn_id=%d rab_id=%d rtp=%x:%u\n", + conn_id, rab_id, rtp_ip, rtp_port); +} + +static void mgcp_response_rab_act_cs_crcx(struct mgcp_response *r, void *priv) +{ + struct gsm_trans *trans = priv; + struct gsm_subscriber_connection *conn = trans->conn; + struct ue_conn_ctx *uectx = conn->iu.ue_ctx; + uint32_t rtp_ip; + int rc; + + if (r->head.response_code != 200) { + LOGP(DMGCP, LOGL_ERROR, + "MGCPGW response yields error: %d %s\n", + r->head.response_code, r->head.comment); + goto rab_act_cs_error; + } + + rc = mgcp_response_parse_params(r); + if (rc) { + LOGP(DMGCP, LOGL_ERROR, + "Cannot parse MGCP response, for %s\n", + vlr_subscr_name(trans->vsub)); + goto rab_act_cs_error; + } + + conn->iu.mgcp_rtp_port_cn = r->audio_port; + + rtp_ip = mgcpgw_client_remote_addr_n(conn->network->mgcpgw.client); + iu_rab_act_cs(uectx, conn->iu.rab_id, rtp_ip, + conn->iu.mgcp_rtp_port_ue); + /* use_x213_nsap == 0 for ip.access nano3G */ + +rab_act_cs_error: + /* FIXME abort call, invalidate conn, ... */ + return; +} + +static int conn_iu_rab_act_cs(struct gsm_trans *trans) +{ + struct gsm_subscriber_connection *conn = trans->conn; + struct mgcpgw_client *mgcp = conn->network->mgcpgw.client; + struct msgb *msg; + + /* HACK. where to scope the RAB Id? At the conn / subscriber / + * ue_conn_ctx? */ + static uint8_t next_rab_id = 1; + conn->iu.rab_id = next_rab_id ++; + + conn->iu.mgcp_rtp_endpoint = + mgcpgw_client_next_endpoint(conn->network->mgcpgw.client); + /* HACK: the addresses should be known from CRCX response + * and config. */ + conn->iu.mgcp_rtp_port_ue = 4000 + 2 * conn->iu.mgcp_rtp_endpoint; + + /* Establish the RTP stream first as looping back to the originator. + * The MDCX will patch through to the counterpart. TODO: play a ring + * tone instead. */ + msg = mgcp_msg_crcx(mgcp, conn->iu.mgcp_rtp_endpoint, trans->callref, + MGCP_CONN_LOOPBACK); + return mgcpgw_client_tx(mgcp, msg, mgcp_response_rab_act_cs_crcx, trans); +} +#endif + +int msc_call_assignment(struct gsm_trans *trans) +{ + struct gsm_subscriber_connection *conn = trans->conn; + + switch (conn->via_ran) { + case RAN_GERAN_A: + LOGP(DMSC, LOGL_ERROR, + "msc_call_assignment(): A-interface BSSMAP Assignment" + " Request not yet implemented\n"); + return -ENOTSUP; + + case RAN_UTRAN_IU: +#ifdef BUILD_IU + return conn_iu_rab_act_cs(trans); +#else + LOGP(DMSC, LOGL_ERROR, + "msc_call_assignment(): IuCS RAB Activation not supported" + " in this build\n"); + return -ENOTSUP; +#endif + + default: + LOGP(DMSC, LOGL_ERROR, + "msc_tx(): conn->via_ran invalid (%d)\n", + conn->via_ran); + return -EINVAL; + } +} + +static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv); + +static void mgcp_bridge(struct gsm_trans *from, struct gsm_trans *to, + enum bridge_state state, + enum mgcp_connection_mode mode) +{ + struct gsm_subscriber_connection *conn1 = from->conn; + struct gsm_subscriber_connection *conn2 = to->conn; + struct mgcpgw_client *mgcp = conn1->network->mgcpgw.client; + const char *ip; + struct msgb *msg; + + OSMO_ASSERT(mgcp); + + from->bridge.peer = to; + from->bridge.state = state; + + /* Loop back to the same MGCP GW */ + ip = mgcpgw_client_remote_addr_str(mgcp); + + msg = mgcp_msg_mdcx(mgcp, + conn1->iu.mgcp_rtp_endpoint, + ip, conn2->iu.mgcp_rtp_port_cn, + mode); + if (mgcpgw_client_tx(mgcp, msg, mgcp_response_bridge_mdcx, from)) + LOGP(DMGCP, LOGL_ERROR, + "Failed to send MDCX message for %s\n", + vlr_subscr_name(from->vsub)); +} + +static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv) +{ + struct gsm_trans *trans = priv; + struct gsm_trans *peer = trans->bridge.peer; + + switch (trans->bridge.state) { + case BRIDGE_STATE_LOOPBACK_PENDING: + trans->bridge.state = BRIDGE_STATE_LOOPBACK_ESTABLISHED; + + switch (peer->bridge.state) { + case BRIDGE_STATE_LOOPBACK_PENDING: + /* Wait until the other is done as well. */ + return; + case BRIDGE_STATE_LOOPBACK_ESTABLISHED: + /* Now that both are in loopback, switch both to + * forwarding. */ + mgcp_bridge(trans, peer, BRIDGE_STATE_BRIDGE_PENDING, + MGCP_CONN_RECV_SEND); + mgcp_bridge(peer, trans, BRIDGE_STATE_BRIDGE_PENDING, + MGCP_CONN_RECV_SEND); + break; + default: + LOGP(DMGCP, LOGL_ERROR, + "Unexpected bridge state: %d for %s\n", + trans->bridge.state, vlr_subscr_name(trans->vsub)); + break; + } + break; + + case BRIDGE_STATE_BRIDGE_PENDING: + trans->bridge.state = BRIDGE_STATE_BRIDGE_ESTABLISHED; + break; + + default: + LOGP(DMGCP, LOGL_ERROR, + "Unexpected bridge state: %d for %s\n", + trans->bridge.state, vlr_subscr_name(trans->vsub)); + break; + } +} + +int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2) +{ + /* First setup as loopback and configure the counterparts' endpoints, + * so that when transmission starts the originating addresses are + * already known to be valid. The mgcp callback will continue. */ + mgcp_bridge(trans1, trans2, BRIDGE_STATE_LOOPBACK_PENDING, + MGCP_CONN_LOOPBACK); + mgcp_bridge(trans2, trans1, BRIDGE_STATE_LOOPBACK_PENDING, + MGCP_CONN_LOOPBACK); + + return 0; +} diff --git a/openbsc/src/libmsc/msc_vty.c b/openbsc/src/libmsc/msc_vty.c new file mode 100644 index 000000000..b6fff56af --- /dev/null +++ b/openbsc/src/libmsc/msc_vty.c @@ -0,0 +1,181 @@ +/* MSC interface to quagga VTY */ +/* (C) 2016 by sysmocom s.m.f.c. GmbH + * Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c) + * (C) 2009 by Harald Welte + * (C) 2009-2011 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 . + * + */ + +/* NOTE: I would have liked to call this the MSC_NODE instead of the MSC_NODE, + * but MSC_NODE already exists to configure a remote MSC for osmo-bsc. */ + +#include + +#include + +#include +#include +#include +#include +#include + +static struct cmd_node msc_node = { + MSC_NODE, + "%s(config-msc)# ", + 1, +}; + +DEFUN(cfg_msc, cfg_msc_cmd, + "msc", "Configure MSC options") +{ + vty->node = MSC_NODE; + return CMD_SUCCESS; +} + +/* Note: limit on the parameter length is set by internal vty code limitations */ +DEFUN(cfg_msc_subscr_random, cfg_msc_subscr_random_cmd, + "subscriber-create-on-demand random <1-9999999999> <2-9999999999>", + "Set random parameters for a new record when a subscriber is first seen.\n" + "Set random parameters for a new record when a subscriber is first seen.\n" + "Minimum for subscriber extension\n""Maximum for subscriber extension\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + uint64_t mi = atoi(argv[0]), ma = atoi(argv[1]); + gsmnet->auto_create_subscr = true; + gsmnet->auto_assign_exten = true; + if (mi >= ma) { + vty_out(vty, "Incorrect range: %s >= %s, expected MIN < MAX%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + gsmnet->ext_min = mi; + gsmnet->ext_max = ma; + return CMD_SUCCESS; +} + +DEFUN(cfg_msc_subscr_create, cfg_msc_subscr_create_cmd, + "subscriber-create-on-demand [no-extension]", + "Make a new record when a subscriber is first seen.\n" + "Do not automatically assign extension to created subscribers\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->auto_create_subscr = true; + gsmnet->auto_assign_exten = argc ? false : true; + return CMD_SUCCESS; +} + +DEFUN(cfg_msc_no_subscr_create, cfg_msc_no_subscr_create_cmd, + "no subscriber-create-on-demand", + NO_STR "Make a new record when a subscriber is first seen.\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->auto_create_subscr = false; + return CMD_SUCCESS; +} + +DEFUN(cfg_msc_assign_tmsi, cfg_msc_assign_tmsi_cmd, + "assign-tmsi", + "Assign TMSI during Location Updating.\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->vlr->cfg.assign_tmsi = true; + return CMD_SUCCESS; +} + +DEFUN(cfg_msc_no_assign_tmsi, cfg_msc_no_assign_tmsi_cmd, + "no assign-tmsi", + NO_STR "Assign TMSI during Location Updating.\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->vlr->cfg.assign_tmsi = false; + return CMD_SUCCESS; +} + +static int config_write_msc(struct vty *vty) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + vty_out(vty, "msc%s", VTY_NEWLINE); + if (!gsmnet->auto_create_subscr) + vty_out(vty, " no subscriber-create-on-demand%s", VTY_NEWLINE); + else + vty_out(vty, " subscriber-create-on-demand%s%s", + gsmnet->auto_assign_exten ? "" : " no-extension", + VTY_NEWLINE); + + if (gsmnet->ext_min != GSM_MIN_EXTEN || gsmnet->ext_max != GSM_MAX_EXTEN) + vty_out(vty, " subscriber-create-on-demand random %"PRIu64" %" + PRIu64"%s", gsmnet->ext_min, gsmnet->ext_max, + VTY_NEWLINE); + vty_out(vty, " %sassign-tmsi%s", + gsmnet->vlr->cfg.assign_tmsi? "" : "no ", VTY_NEWLINE); + + mgcpgw_client_config_write(vty, " "); + iu_vty_config_write(vty, " "); + + return CMD_SUCCESS; +} + +static int config_write_net(struct vty *vty) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + vty_out(vty, "network%s", VTY_NEWLINE); + vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE); + vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE); + vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE); + vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE); + vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE); + vty_out(vty, " location updating reject cause %u%s", + gsmnet->reject_cause, VTY_NEWLINE); + vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE); + vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode), + VTY_NEWLINE); + vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE); + if (gsmnet->tz.override != 0) { + if (gsmnet->tz.dst) + vty_out(vty, " timezone %d %d %d%s", + gsmnet->tz.hr, gsmnet->tz.mn, gsmnet->tz.dst, + VTY_NEWLINE); + else + vty_out(vty, " timezone %d %d%s", + gsmnet->tz.hr, gsmnet->tz.mn, VTY_NEWLINE); + } + if (gsmnet->t3212 == 0) + vty_out(vty, " no periodic location update%s", VTY_NEWLINE); + else + vty_out(vty, " periodic location update %u%s", + gsmnet->t3212 * 6, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +void msc_vty_init(struct gsm_network *msc_network) +{ + common_cs_vty_init(msc_network, config_write_net); + + install_element(CONFIG_NODE, &cfg_msc_cmd); + install_node(&msc_node, config_write_msc); + vty_install_default(MSC_NODE); + install_element(MSC_NODE, &cfg_msc_subscr_create_cmd); + install_element(MSC_NODE, &cfg_msc_subscr_random_cmd); + install_element(MSC_NODE, &cfg_msc_no_subscr_create_cmd); + install_element(MSC_NODE, &cfg_msc_assign_tmsi_cmd); + install_element(MSC_NODE, &cfg_msc_no_assign_tmsi_cmd); + mgcpgw_client_vty_init(MSC_NODE, &msc_network->mgcpgw.conf); + iu_vty_init(MSC_NODE, &msc_network->iu.rab_assign_addr_enc); +} diff --git a/openbsc/src/libmsc/osmo_msc.c b/openbsc/src/libmsc/osmo_msc.c index ab53b0be7..bfe5343cf 100644 --- a/openbsc/src/libmsc/osmo_msc.c +++ b/openbsc/src/libmsc/osmo_msc.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -40,24 +41,6 @@ static void msc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci) gsm411_sapi_n_reject(conn); } -static bool keep_conn(struct gsm_subscriber_connection *conn) -{ - /* TODO: what about a silent call? */ - - if (!conn->conn_fsm) { - DEBUGP(DMM, "No conn_fsm, release conn\n"); - return false; - } - - switch (conn->conn_fsm->state) { - case SUBSCR_CONN_S_NEW: - case SUBSCR_CONN_S_ACCEPTED: - return true; - default: - return false; - } -} - static void subscr_conn_bump(struct gsm_subscriber_connection *conn) { if (!conn) @@ -65,39 +48,32 @@ static void subscr_conn_bump(struct gsm_subscriber_connection *conn) if (!conn->conn_fsm) return; if (!(conn->conn_fsm->state == SUBSCR_CONN_S_ACCEPTED - || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING)) + || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING)) { + DEBUGP(DMM, "%s: bump: conn still being established (%s)\n", + vlr_subscr_name(conn->vsub), + osmo_fsm_inst_state_name(conn->conn_fsm)); return; + } osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_BUMP, NULL); } /* receive a Level 3 Complete message and return MSC_CONN_ACCEPT or * MSC_CONN_REJECT */ -enum msc_compl_l3_rc msc_compl_l3(struct gsm_subscriber_connection *conn, - struct msgb *msg, uint16_t chosen_channel) +int msc_compl_l3(struct gsm_subscriber_connection *conn, + struct msgb *msg, uint16_t chosen_channel) { - /* Ownership of the gsm_subscriber_connection is still a bit mucky - * between libbsc and libmsc. In libmsc, we use ref counting, but not - * in libbsc. This will become simpler with the MSCSPLIT. */ - - /* reserve for the duration of this function */ msc_subscr_conn_get(conn); - gsm0408_dispatch(conn, msg); - if (!keep_conn(conn)) { - DEBUGP(DMM, "compl_l3: Discarding conn\n"); - /* keep the use_count reserved, libbsc will discard. If we - * released the ref count and discarded here, libbsc would - * double-free. And we will not change bsc_api semantics. */ - return MSC_CONN_REJECT; - } - DEBUGP(DMM, "compl_l3: Keeping conn\n"); - /* Bump whether the conn wants to be closed */ subscr_conn_bump(conn); /* If this should be kept, the conn->conn_fsm has placed a use_count */ msc_subscr_conn_put(conn); + + /* Always return acceptance, because even if the conn was not accepted, + * we assumed ownership of it and the caller shall not interfere with + * that. We may even already have discarded the conn. */ return MSC_CONN_ACCEPT; #if 0 @@ -119,7 +95,7 @@ enum msc_compl_l3_rc msc_compl_l3(struct gsm_subscriber_connection *conn, } /* Receive a DTAP message from BSC */ -static void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) +void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) { msc_subscr_conn_get(conn); gsm0408_dispatch(conn, msg); @@ -158,8 +134,8 @@ static void msc_classmark_chg(struct gsm_subscriber_connection *conn, } /* Receive a CIPHERING MODE COMPLETE from BSC */ -static void msc_ciph_m_compl(struct gsm_subscriber_connection *conn, - struct msgb *msg, uint8_t alg_id) +void msc_cipher_mode_compl(struct gsm_subscriber_connection *conn, + struct msgb *msg, uint8_t alg_id) { struct gsm48_hdr *gh = msgb_l3(msg); unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); @@ -277,7 +253,7 @@ static struct bsc_api msc_handler = { .assign_compl = msc_assign_compl, .assign_fail = msc_assign_fail, .classmark_chg = msc_classmark_chg, - .cipher_mode_compl = msc_ciph_m_compl, + .cipher_mode_compl = msc_cipher_mode_compl, .conn_cleanup = msc_subscr_con_cleanup, }; @@ -285,6 +261,10 @@ struct bsc_api *msc_bsc_api() { return &msc_handler; } +/* Signal the connection's FSM to gracefully terminate the connection by a + * SUBSCR_CONN_E_CN_CLOSE event. + * \param cause a GSM_CAUSE_* constant, e.g. GSM_CAUSE_AUTH_FAILED. + */ void msc_subscr_conn_close(struct gsm_subscriber_connection *conn, uint32_t cause) { @@ -335,8 +315,12 @@ void _msc_subscr_conn_put(struct gsm_subscriber_connection *conn, "%s: MSC conn use - 1 == %u\n", vlr_subscr_name(conn->vsub), conn->use_count); - if (conn->use_count == 0) { - gsm0808_clear(conn); - bsc_subscr_con_free(conn); - } + if (conn->use_count == 0) + msc_subscr_con_free(conn); +} + +void msc_stop_paging(struct vlr_subscr *vsub) +{ + DEBUGP(DPAG, "Paging can stop for %s\n", vlr_subscr_name(vsub)); + /* tell BSCs and RNCs to stop paging? How? */ } diff --git a/openbsc/src/libmsc/silent_call.c b/openbsc/src/libmsc/silent_call.c index 76816c29d..256b5b453 100644 --- a/openbsc/src/libmsc/silent_call.c +++ b/openbsc/src/libmsc/silent_call.c @@ -131,7 +131,8 @@ int gsm_silent_call_start(struct vlr_subscr *vsub, void *data, int type) /* FIXME the VTY command allows selecting a silent call channel type. * This doesn't apply to the situation after MSCSPLIT with an * A-interface. */ - req = subscr_request_conn(vsub, type, paging_cb_silent, data); + req = subscr_request_conn(vsub, paging_cb_silent, data, + "establish silent call"); return req != NULL; } diff --git a/openbsc/src/libmsc/subscr_conn.c b/openbsc/src/libmsc/subscr_conn.c index 9be53cf4a..9d5dd5d2c 100644 --- a/openbsc/src/libmsc/subscr_conn.c +++ b/openbsc/src/libmsc/subscr_conn.c @@ -30,6 +30,7 @@ #include #include #include +#include #define SUBSCR_CONN_TIMEOUT 5 /* seconds */ @@ -52,8 +53,8 @@ const struct value_string subscr_conn_from_names[] = { { 0, NULL } }; -static void paging_resp(struct gsm_subscriber_connection *conn, - enum gsm_paging_event pe) +static void paging_event(struct gsm_subscriber_connection *conn, + enum gsm_paging_event pe) { subscr_paging_dispatch(GSM_HOOK_RR_PAGING, pe, NULL, conn, conn->vsub); } @@ -85,11 +86,17 @@ void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data) case SUBSCR_CONN_E_MO_CLOSE: case SUBSCR_CONN_E_CN_CLOSE: + if (data) + LOGPFSM(fi, "Close event, cause %u\n", + *(uint32_t*)data); + /* will release further below, see + * 'if (fi->state != SUBSCR_CONN_S_ACCEPTED)' */ break; default: - LOGPFSM(fi, "Unexpected event: %d %s\n", - event, osmo_fsm_event_name(fi->fsm, event)); + LOGPFSML(fi, LOGL_ERROR, + "Unexpected event: %d %s\n", event, + osmo_fsm_event_name(fi->fsm, event)); break; } @@ -102,21 +109,24 @@ void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data) /* signal paging success or failure in case this was a paging */ if (from == SUBSCR_CONN_FROM_PAGING_RESP) - paging_resp(conn, - success ? GSM_PAGING_SUCCEEDED - : GSM_PAGING_EXPIRED); + paging_event(conn, + success ? GSM_PAGING_SUCCEEDED + : GSM_PAGING_EXPIRED); + + /* FIXME rate counters */ + /*rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_COMPLETED]);*/ /* On failure, discard the conn */ if (!success) { /* TODO: on MO_CLOSE or CN_CLOSE, first go to RELEASING and - * await BSC confirmation? */ + * await BSC/RNC confirmation? */ osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0); return; } if (from == SUBSCR_CONN_FROM_CM_SERVICE_REQ) { conn->received_cm_service_request = true; - LOGPFSM(fi, "received_cm_service_request = true\n"); + LOGPFSML(fi, LOGL_DEBUG, "received_cm_service_request = true\n"); } osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_BUMP, data); @@ -125,19 +135,37 @@ void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data) static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void *data) { struct gsm_subscriber_connection *conn = fi->priv; + struct gsm_trans *trans; - if (conn->silent_call) + if (conn->silent_call) { + LOGPFSML(fi, LOGL_DEBUG, "bump: silent call still active\n"); return; + } - if (conn->received_cm_service_request) + if (conn->received_cm_service_request) { + LOGPFSML(fi, LOGL_DEBUG, "bump: still awaiting first request after a CM Service Request\n"); return; + } - if (conn->vsub && !llist_empty(&conn->vsub->cs.requests)) + if (conn->vsub && !llist_empty(&conn->vsub->cs.requests)) { + struct subscr_request *sr; + if (!log_check_level(fi->fsm->log_subsys, LOGL_DEBUG)) { + llist_for_each_entry(sr, &conn->vsub->cs.requests, entry) { + LOGPFSML(fi, LOGL_DEBUG, "bump: still active: %s\n", + sr->label); + } + } return; + } - if (trans_has_conn(conn)) + if ((trans = trans_has_conn(conn))) { + LOGPFSML(fi, LOGL_DEBUG, + "bump: connection still has active transaction: %s\n", + gsm48_pdisc_name(trans->protocol)); return; + } + LOGPFSML(fi, LOGL_DEBUG, "bump: releasing conn\n"); osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0); } @@ -204,6 +232,12 @@ static void subscr_conn_fsm_cleanup(struct osmo_fsm_inst *fi, /* If we're closing in a middle of a trans, we need to clean up */ trans_conn_closed(conn); + if (conn->via_ran == RAN_UTRAN_IU) + iu_tx_release(conn->iu.ue_ctx, NULL); + /* FIXME: keep the conn until the Iu Release Outcome is + * received from the UE, or a timeout expires. For now, the log + * says "unknown UE" for each release outcome. */ + msc_subscr_conn_put(conn); } @@ -275,7 +309,7 @@ static struct osmo_fsm subscr_conn_fsm = { .num_states = ARRAY_SIZE(subscr_conn_fsm_states), .allstate_event_mask = 0, .allstate_action = NULL, - .log_subsys = DVLR, + .log_subsys = DMM, .event_names = subscr_conn_fsm_event_names, .cleanup = subscr_conn_fsm_cleanup, .timer_cb = subscr_conn_fsm_timeout, diff --git a/openbsc/src/libmsc/transaction.c b/openbsc/src/libmsc/transaction.c index 41fb5a914..73c509c2c 100644 --- a/openbsc/src/libmsc/transaction.c +++ b/openbsc/src/libmsc/transaction.c @@ -180,23 +180,35 @@ int trans_assign_trans_id(struct gsm_network *net, struct vlr_subscr *vsub, * \param[in] conn Connection to check * \returns 1 in case there is a transaction, 0 otherwise */ -int trans_has_conn(const struct gsm_subscriber_connection *conn) +struct gsm_trans *trans_has_conn(const struct gsm_subscriber_connection *conn) { struct gsm_trans *trans; llist_for_each_entry(trans, &conn->network->trans_list, entry) if (trans->conn == conn) - return 1; + return trans; - return 0; + return NULL; } +/* + * Free all transactions that are associated with the released + * connection. The transaction code will inform the CC or SMS + * facilities that will send the release indications. + */ void trans_conn_closed(struct gsm_subscriber_connection *conn) { - struct gsm_trans *trans, *t2; + struct gsm_trans *trans; - llist_for_each_entry_safe(trans, t2, &conn->network->trans_list, entry) { - if (trans->conn == conn) + /* As part of the CC REL_IND the remote leg might be released and this + * will trigger the call to trans_free. This is something the llist + * macro can not handle and we will need to re-iterate the list. + */ +restart: + llist_for_each_entry(trans, &conn->network->trans_list, entry) { + if (trans->conn == conn) { trans_free(trans); + goto restart; + } } } diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c index df2d1e4ad..c783cf101 100644 --- a/openbsc/src/libmsc/vty_interface_layer3.c +++ b/openbsc/src/libmsc/vty_interface_layer3.c @@ -21,9 +21,8 @@ #include #include #include -#include -#include #include +#include #include #include @@ -902,7 +901,6 @@ DEFUN(logging_fltr_imsi, "Filter log messages by IMSI\n" "IMSI to be used as filter\n") { struct vlr_subscr *vlr_subscr; - struct bsc_subscr *bsc_subscr; struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct log_target *tgt = osmo_log_vty2tgt(vty); const char *imsi = argv[0]; @@ -911,16 +909,14 @@ DEFUN(logging_fltr_imsi, return CMD_WARNING; vlr_subscr = vlr_subscr_find_by_imsi(gsmnet->vlr, imsi); - bsc_subscr = bsc_subscr_find_by_imsi(gsmnet->bsc_subscribers, imsi); - if (!vlr_subscr && !bsc_subscr) { + if (!vlr_subscr) { vty_out(vty, "%%no subscriber with IMSI(%s)%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } log_set_filter_vlr_subscr(tgt, vlr_subscr); - log_set_filter_bsc_subscr(tgt, bsc_subscr); return CMD_SUCCESS; } @@ -968,92 +964,6 @@ static int config_write_hlr(struct vty *vty) return CMD_SUCCESS; } -static struct cmd_node nitb_node = { - NITB_NODE, - "%s(config-nitb)# ", - 1, -}; - -DEFUN(cfg_nitb, cfg_nitb_cmd, - "nitb", "Configure NITB options") -{ - vty->node = NITB_NODE; - return CMD_SUCCESS; -} - -/* Note: limit on the parameter length is set by internal vty code limitations */ -DEFUN(cfg_nitb_subscr_random, cfg_nitb_subscr_random_cmd, - "subscriber-create-on-demand random <1-9999999999> <2-9999999999>", - "Set random parameters for a new record when a subscriber is first seen.\n" - "Set random parameters for a new record when a subscriber is first seen.\n" - "Minimum for subscriber extension\n""Maximum for subscriber extension\n") -{ - vty_out(vty, "%% 'subscriber-create-on-demand' is no longer supported.%s" - "%% This is now up to osmo-hlr.%s", - VTY_NEWLINE, VTY_NEWLINE); - return CMD_WARNING; -} - -DEFUN(cfg_nitb_subscr_create, cfg_nitb_subscr_create_cmd, - "subscriber-create-on-demand [no-extension]", - "Make a new record when a subscriber is first seen.\n" - "Do not automatically assign extension to created subscribers\n") -{ - vty_out(vty, "%% 'subscriber-create-on-demand' is no longer supported.%s" - "%% This is now up to osmo-hlr.%s", - VTY_NEWLINE, VTY_NEWLINE); - return CMD_WARNING; -} - -DEFUN(cfg_nitb_no_subscr_create, cfg_nitb_no_subscr_create_cmd, - "no subscriber-create-on-demand", - NO_STR "Make a new record when a subscriber is first seen.\n") -{ - vty_out(vty, "%% 'subscriber-create-on-demand' is no longer supported.%s" - "%% This is now up to osmo-hlr.%s", - VTY_NEWLINE, VTY_NEWLINE); - return CMD_WARNING; -} - -DEFUN(cfg_nitb_assign_tmsi, cfg_nitb_assign_tmsi_cmd, - "assign-tmsi", - "Assign TMSI during Location Updating.\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->vlr->cfg.assign_tmsi = true; - return CMD_SUCCESS; -} - -DEFUN(cfg_nitb_no_assign_tmsi, cfg_nitb_no_assign_tmsi_cmd, - "no assign-tmsi", - NO_STR "Assign TMSI during Location Updating.\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->vlr->cfg.assign_tmsi = false; - return CMD_SUCCESS; -} - -static int config_write_nitb(struct vty *vty) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - vty_out(vty, "nitb%s", VTY_NEWLINE); - if (!gsmnet->auto_create_subscr) - vty_out(vty, " no subscriber-create-on-demand%s", VTY_NEWLINE); - else - vty_out(vty, " subscriber-create-on-demand%s%s", - gsmnet->auto_assign_exten ? "" : " no-extension", - VTY_NEWLINE); - - if (gsmnet->ext_min != GSM_MIN_EXTEN || gsmnet->ext_max != GSM_MAX_EXTEN) - vty_out(vty, " subscriber-create-on-demand random %"PRIu64" %" - PRIu64"%s", gsmnet->ext_min, gsmnet->ext_max, - VTY_NEWLINE); - vty_out(vty, " %sassign-tmsi%s", - gsmnet->vlr->cfg.assign_tmsi? "" : "no ", VTY_NEWLINE); - return CMD_SUCCESS; -} - int bsc_vty_init_extra(void) { osmo_signal_register_handler(SS_SCALL, scall_cbfn, NULL); @@ -1105,13 +1015,5 @@ int bsc_vty_init_extra(void) install_element(HLR_NODE, &cfg_hlr_remote_ip_cmd); install_element(HLR_NODE, &cfg_hlr_remote_port_cmd); - install_element(CONFIG_NODE, &cfg_nitb_cmd); - install_node(&nitb_node, config_write_nitb); - install_element(NITB_NODE, &cfg_nitb_subscr_create_cmd); - install_element(NITB_NODE, &cfg_nitb_subscr_random_cmd); - install_element(NITB_NODE, &cfg_nitb_no_subscr_create_cmd); - install_element(NITB_NODE, &cfg_nitb_assign_tmsi_cmd); - install_element(NITB_NODE, &cfg_nitb_no_assign_tmsi_cmd); - return 0; } diff --git a/openbsc/src/libvlr/vlr.c b/openbsc/src/libvlr/vlr.c index 0e0d31c26..d95d1b7ec 100644 --- a/openbsc/src/libvlr/vlr.c +++ b/openbsc/src/libvlr/vlr.c @@ -961,6 +961,9 @@ struct vlr_instance *vlr_alloc(void *ctx, const struct vlr_ops *ops) { struct vlr_instance *vlr = talloc_zero(ctx, struct vlr_instance); OSMO_ASSERT(vlr); + + /* Some of these are needed only on UTRAN, but in case the caller wants + * only GERAN, she should just provide dummy callbacks. */ OSMO_ASSERT(ops->tx_auth_req); OSMO_ASSERT(ops->tx_auth_rej); OSMO_ASSERT(ops->tx_id_req); @@ -969,6 +972,7 @@ struct vlr_instance *vlr_alloc(void *ctx, const struct vlr_ops *ops) OSMO_ASSERT(ops->tx_cm_serv_acc); OSMO_ASSERT(ops->tx_cm_serv_rej); OSMO_ASSERT(ops->set_ciph_mode); + OSMO_ASSERT(ops->tx_common_id); OSMO_ASSERT(ops->subscr_update); OSMO_ASSERT(ops->subscr_assoc); diff --git a/openbsc/src/libvlr/vlr_access_req_fsm.c b/openbsc/src/libvlr/vlr_access_req_fsm.c index 96ded2aca..67c61b70d 100644 --- a/openbsc/src/libvlr/vlr_access_req_fsm.c +++ b/openbsc/src/libvlr/vlr_access_req_fsm.c @@ -250,6 +250,14 @@ static void _proc_arq_vlr_node2_post_ciph(struct osmo_fsm_inst *fi) LOGPFSM(fi, "%s()\n", __func__); + if (par->is_utran) { + int rc; + rc = par->vlr->ops.tx_common_id(par->msc_conn_ref); + if (rc) + LOGPFSML(fi, LOGL_ERROR, + "Error while sending Common ID (%d)\n", rc); + } + vsub->conf_by_radio_contact_ind = true; if (vsub->loc_conf_in_hlr_ind == false) { /* start Update_Location_Child_VLR. WE use @@ -675,6 +683,10 @@ vlr_proc_acc_req(struct osmo_fsm_inst *parent, (ciphering_required? "+Ciph" : " (no Ciph)") : ""); + if (is_utran && !authentication_required) + LOGPFSML(fi, LOGL_ERROR, + "Authentication off on UTRAN network. Good luck.\n"); + gsm48_mi_to_string(mi_string, sizeof(mi_string), mi_lv+1, mi_lv[0]); mi_type = mi_lv[1] & GSM_MI_TYPE_MASK; switch (mi_type) { diff --git a/openbsc/src/libvlr/vlr_lu_fsm.c b/openbsc/src/libvlr/vlr_lu_fsm.c index d32659f56..b2c490f98 100644 --- a/openbsc/src/libvlr/vlr_lu_fsm.c +++ b/openbsc/src/libvlr/vlr_lu_fsm.c @@ -781,6 +781,14 @@ static void vlr_loc_upd_post_ciph(struct osmo_fsm_inst *fi) OSMO_ASSERT(vsub); + if (lfp->is_utran) { + int rc; + rc = lfp->vlr->ops.tx_common_id(lfp->msc_conn_ref); + if (rc) + LOGPFSML(fi, LOGL_ERROR, + "Error while sending Common ID (%d)\n", rc); + } + vsub->conf_by_radio_contact_ind = true; /* Update LAI */ vsub->cgi.lai = lfp->new_lai; @@ -1400,6 +1408,10 @@ vlr_loc_update(struct osmo_fsm_inst *parent, (ciphering_required? "+Ciph" : " (no Ciph)") : ""); + if (is_utran && !authentication_required) + LOGPFSML(fi, LOGL_ERROR, + "Authentication off on UTRAN network. Good luck.\n"); + osmo_fsm_inst_dispatch(fi, VLR_ULA_E_UPDATE_LA, NULL); return fi; diff --git a/openbsc/src/osmo-bsc/osmo_bsc_api.c b/openbsc/src/osmo-bsc/osmo_bsc_api.c index bac5e4717..8c33e2b57 100644 --- a/openbsc/src/osmo-bsc/osmo_bsc_api.c +++ b/openbsc/src/osmo-bsc/osmo_bsc_api.c @@ -536,7 +536,7 @@ static struct bsc_api bsc_handler = { .sapi_n_reject = bsc_sapi_n_reject, .cipher_mode_compl = bsc_cipher_mode_compl, .compl_l3 = bsc_compl_l3, - .dtap = bsc_dtap, + .dtap = bsc_dtap, .assign_compl = bsc_assign_compl, .assign_fail = bsc_assign_fail, .clear_request = bsc_clear_request, diff --git a/openbsc/src/osmo-msc/Makefile.am b/openbsc/src/osmo-msc/Makefile.am new file mode 100644 index 000000000..1e6a35b86 --- /dev/null +++ b/openbsc/src/osmo-msc/Makefile.am @@ -0,0 +1,53 @@ +AM_CPPFLAGS = \ + $(all_includes) \ + -I$(top_srcdir)/include \ + -I$(top_builddir) \ + $(NULL) + +AM_CFLAGS = \ + -Wall \ + $(COVERAGE_CFLAGS) \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOVTY_CFLAGS) \ + $(LIBOSMOCTRL_CFLAGS) \ + $(LIBOSMOABIS_CFLAGS) \ + $(LIBSMPP34_CFLAGS) \ + $(LIBCRYPTO_CFLAGS) \ + $(LIBOSMORANAP_CFLAGS) \ + $(LIBASN1C_CFLAGS) \ + $(LIBOSMOSIGTRAN_CFLAGS) \ + $(NULL) + +AM_LDFLAGS = \ + $(COVERAGE_LDFLAGS) \ + $(NULL) + +bin_PROGRAMS = \ + osmo-msc \ + $(NULL) + +osmo_msc_SOURCES = \ + msc_main.c \ + $(NULL) + +osmo_msc_LDADD = \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libvlr/libvlr.a \ + $(top_builddir)/src/libcommon-cs/libcommon-cs.a \ + $(top_builddir)/src/libiu/libiu.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(top_builddir)/src/libmgcp/libmgcp.a \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOCORE_LIBS) \ + $(LIBOSMOCTRL_LIBS) \ + $(LIBOSMOABIS_LIBS) \ + $(LIBSMPP34_LIBS) \ + $(LIBCRYPTO_LIBS) \ + -ldbi \ + $(LIBOSMORANAP_LIBS) \ + $(LIBASN1C_LIBS) \ + $(LIBOSMOSIGTRAN_LIBS) \ + $(NULL) diff --git a/openbsc/src/osmo-msc/msc_main.c b/openbsc/src/osmo-msc/msc_main.c new file mode 100644 index 000000000..706d996f1 --- /dev/null +++ b/openbsc/src/osmo-msc/msc_main.c @@ -0,0 +1,521 @@ +/* OsmoMSC - Circuit-Switched Core Network (MSC+VLR+HLR+SMSC) implementation + */ + +/* (C) 2016 by sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Based on OsmoNITB: + * (C) 2008-2010 by Harald Welte + * (C) 2009-2012 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 . + * + */ + +#include +#include +#include +#include +#include +#include + +#define _GNU_SOURCE +#include + +/* build switches from the configure script */ +#include "../../bscconfig.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static const char * const osmomsc_copyright = + "OsmoMSC - Osmocom Circuit-Switched Core Network implementation\r\n" + "Copyright (C) 2016 by sysmocom s.f.m.c. GmbH \r\n" + "Based on OsmoNITB:\r\n" + " (C) 2008-2010 by Harald Welte \r\n" + " (C) 2009-2012 by Holger Hans Peter 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 \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"; + +void *tall_msc_ctx = NULL; + +/* satisfy deps from libbsc legacy. + TODO double check these */ +void *tall_fle_ctx = NULL; +void *tall_paging_ctx = NULL; +void *tall_map_ctx = NULL; +void *tall_upq_ctx = NULL; +/* end deps from libbsc legacy. */ + +static struct { + const char *database_name; + const char *config_file; + int daemonize; + const char *mncc_sock_path; + int use_db_counter; +} msc_cmdline_config = { + "sms.db", + "osmo-msc.cfg", + 0, + 0, + 1 +}; + +/* timer to store statistics */ +#define DB_SYNC_INTERVAL 60, 0 +#define EXPIRE_INTERVAL 10, 0 + +static struct osmo_timer_list db_sync_timer; + +static void create_pcap_file(char *file) +{ + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + int fd = open(file, O_WRONLY|O_TRUNC|O_CREAT, mode); + + if (fd < 0) { + perror("Failed to open file for pcap"); + return; + } + + e1_set_pcap_fd(fd); +} + +static void print_usage() +{ + printf("Usage: osmo-nitb\n"); +} + +static void print_help() +{ + printf(" Some useful help...\n"); + printf(" -h --help This text.\n"); + printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM Enable debugging.\n"); + printf(" -D --daemonize Fork the process into a background daemon.\n"); + printf(" -c --config-file filename The config file to use.\n"); + printf(" -s --disable-color\n"); + printf(" -l --database db-name The database to use.\n"); + printf(" -a --authorize-everyone Authorize every new subscriber. Dangerous!\n"); + printf(" -T --timestamp Prefix every log line with a timestamp.\n"); + printf(" -V --version Print the version of OpenBSC.\n"); + printf(" -P --rtp-proxy Enable the RTP Proxy code inside OpenBSC.\n"); + printf(" -e --log-level number Set a global loglevel.\n"); + printf(" -M --mncc-sock-path PATH Disable built-in MNCC handler and offer socket.\n"); + printf(" -m --mncc-sock Same as `-M /tmp/bsc_mncc' (deprecated).\n"); + printf(" -C --no-dbcounter Disable regular syncing of counters to database.\n"); + printf(" -r --rf-ctl PATH A unix domain socket to listen for cmds.\n"); + printf(" -p --pcap PATH Write abis communication to pcap trace file.\n"); +} + +static void handle_options(int argc, char **argv) +{ + while (1) { + int option_index = 0, c; + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"debug", 1, 0, 'd'}, + {"daemonize", 0, 0, 'D'}, + {"config-file", 1, 0, 'c'}, + {"disable-color", 0, 0, 's'}, + {"database", 1, 0, 'l'}, + {"authorize-everyone", 0, 0, 'a'}, + {"pcap", 1, 0, 'p'}, + {"timestamp", 0, 0, 'T'}, + {"version", 0, 0, 'V' }, + {"rtp-proxy", 0, 0, 'P'}, + {"log-level", 1, 0, 'e'}, + {"mncc-sock", 0, 0, 'm'}, + {"mncc-sock-path", 1, 0, 'M'}, + {"no-dbcounter", 0, 0, 'C'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "hd:Dsl:ap:TPVc:e:mCM:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_usage(); + print_help(); + exit(0); + case 's': + log_set_use_color(osmo_stderr_target, 0); + break; + case 'd': + log_parse_category_mask(osmo_stderr_target, optarg); + break; + case 'D': + msc_cmdline_config.daemonize = 1; + break; + case 'l': + msc_cmdline_config.database_name = optarg; + break; + case 'c': + msc_cmdline_config.config_file = optarg; + break; + case 'p': + create_pcap_file(optarg); + break; + case 'T': + log_set_print_timestamp(osmo_stderr_target, 1); + break; +#if BEFORE_MSCSPLIT + case 'P': + ipacc_rtp_direct = 0; + break; +#endif + case 'e': + log_set_log_level(osmo_stderr_target, atoi(optarg)); + break; + case 'M': + msc_cmdline_config.mncc_sock_path = optarg; + break; + case 'm': + msc_cmdline_config.mncc_sock_path = "/tmp/bsc_mncc"; + break; + case 'C': + msc_cmdline_config.use_db_counter = 0; + break; + case 'V': + print_version(1); + exit(0); + break; + default: + /* catch unknown options *as well as* missing arguments. */ + fprintf(stderr, "Error in command line options. Exiting.\n"); + exit(-1); + } + } +} + +struct gsm_network *msc_network_alloc(void *ctx, + mncc_recv_cb_t mncc_recv) +{ + struct gsm_network *net = gsm_network_init(ctx, 1, 1, mncc_recv); + if (!net) + return NULL; + + net->name_long = talloc_strdup(net, "OsmoMSC"); + net->name_short = talloc_strdup(net, "OsmoMSC"); + + net->gsup_server_addr_str = talloc_strdup(net, + MSC_HLR_REMOTE_IP_DEFAULT); + net->gsup_server_port = MSC_HLR_REMOTE_PORT_DEFAULT; + + mgcpgw_client_conf_init(&net->mgcpgw.conf); + + return net; +} + +void msc_network_shutdown(struct gsm_network *net) +{ + /* nothing here yet */ +} + +static struct gsm_network *msc_network = NULL; + +extern void *tall_vty_ctx; +static void signal_handler(int signal) +{ + fprintf(stdout, "signal %u received\n", signal); + + switch (signal) { + case SIGINT: + msc_network_shutdown(msc_network); + osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); + sleep(3); + exit(0); + break; + case SIGABRT: + osmo_generate_backtrace(); + /* in case of abort, we want to obtain a talloc report + * and then return to the caller, who will abort the process */ + case SIGUSR1: + talloc_report(tall_vty_ctx, stderr); + talloc_report_full(tall_msc_ctx, stderr); + break; + case SIGUSR2: + talloc_report_full(tall_vty_ctx, stderr); + break; + default: + break; + } +} + +/* timer handling */ +static int _db_store_counter(struct osmo_counter *counter, void *data) +{ + return db_store_counter(counter); +} + +static void db_sync_timer_cb(void *data) +{ + /* store counters to database and re-schedule */ + osmo_counters_for_each(_db_store_counter, NULL); + osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); +} + +extern int bsc_vty_go_parent(struct vty *vty); + +static struct vty_app_info msc_vty_info = { + .name = "OsmoMSC", + .version = PACKAGE_VERSION, + .go_parent_cb = bsc_vty_go_parent, + .is_config_node = bsc_vty_is_config_node, +}; + +static int rcvmsg_iu_cs(struct msgb *msg, struct gprs_ra_id *ra_id, /* FIXME gprs_ in CS code */ + uint16_t *sai) +{ + DEBUGP(DIUCS, "got IuCS message" + " %d bytes: %s\n", + msg->len, osmo_hexdump(msg->data, msg->len)); + if (ra_id) { + DEBUGP(DIUCS, "got IuCS message on" + " MNC %d MCC %d LAC %d RAC %d\n", + ra_id->mnc, ra_id->mcc, ra_id->lac, ra_id->rac); + } + + return gsm0408_rcvmsg_iucs(msc_network, msg, ra_id? &ra_id->lac : NULL); +} + +static int rx_iu_event(struct ue_conn_ctx *ctx, enum iu_event_type type, + void *data) +{ + DEBUGP(DIUCS, "got IuCS event %u: %s\n", type, + iu_event_type_str(type)); + + return iucs_rx_ranap_event(msc_network, ctx, type, data); +} + +int main(int argc, char **argv) +{ + int rc; + + msc_vty_info.copyright = osmomsc_copyright; + + tall_msc_ctx = talloc_named_const(NULL, 1, "osmo_msc"); + talloc_ctx_init(tall_msc_ctx); + + osmo_init_logging(&log_info); + osmo_stats_init(tall_msc_ctx); + + /* For --version, vty_init() must be called before handling options */ + vty_init(&msc_vty_info); + + /* Parse options */ + handle_options(argc, argv); + + /* Allocate global gsm_network struct; choose socket/internal MNCC */ + msc_network = msc_network_alloc(tall_msc_ctx, + msc_cmdline_config.mncc_sock_path? + mncc_sock_from_cc + : int_mncc_recv); + if (!msc_network) + return -ENOMEM; + + if (msc_vlr_alloc(msc_network)) { + fprintf(stderr, "Failed to allocate VLR\n"); + exit(1); + } + + ctrl_vty_init(tall_msc_ctx); + logging_vty_add_cmds(&log_info); + msc_vty_init(msc_network); + bsc_vty_init_extra(); + +#ifdef BUILD_SMPP + if (smpp_openbsc_alloc_init(tall_msc_ctx) < 0) + return -1; +#endif + + /* + * For osmo-nitb, skip TCH/F for now, because otherwise dyn TS + * always imply the possibility to have a mix of TCH/F and + * TCH/H channels; if two phones request a TCH/F and a TCH/H, + * respectively, they cannot call each other. If we deny TCH/F, + * they will both fall back to TCH/H, and dynamic channels are + * usable. See OS#1778. + * + * A third-party MSC may well be able to handle a TCH/H TCH/F + * mismatch. Moreover, this option may be overwritten in the + * config file or in VTY. + */ + msc_network->dyn_ts_allow_tch_f = false; + + rc = vty_read_config_file(msc_cmdline_config.config_file, NULL); + if (rc < 0) { + LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", + msc_cmdline_config.config_file); + return 1; + } + + /* Initialize MNCC socket if appropriate */ + if (msc_cmdline_config.mncc_sock_path) { + rc = mncc_sock_init(msc_network, + msc_cmdline_config.mncc_sock_path); + if (rc) { + fprintf(stderr, "MNCC socket initialization failed. exiting.\n"); + exit(1); + } + } else + DEBUGP(DMNCC, "Using internal MNCC handler.\n"); + + /* start telnet after reading config for vty_get_bind_addr() */ + rc = telnet_init_dynif(tall_msc_ctx, &msc_network, + vty_get_bind_addr(), OSMO_VTY_PORT_MSC); + if (rc < 0) + return 2; + + /* BSC stuff is to be split behind an A-interface to be used with + * OsmoBSC, but there is no need to remove it yet. Most of the + * following code until iu_init() is legacy. */ + +#ifdef BUILD_SMPP + smpp_openbsc_start(msc_network); +#endif + +#if 0 + the bsc_ctrl_node_lookup() only returns BSC specific ctrl nodes + + /* start control interface after reading config for + * ctrl_vty_get_bind_addr() */ + msc_network->ctrl = bsc_controlif_setup(msc_network, + ctrl_vty_get_bind_addr(), + OSMO_CTRL_PORT_MSC); + if (!msc_network->ctrl) { + printf("Failed to initialize control interface. Exiting.\n"); + return -1; + } +#endif + +#if 0 +TODO: we probably want some of the _net_ ctrl commands from bsc_base_ctrl_cmds_install(). + if (bsc_base_ctrl_cmds_install() != 0) { + printf("Failed to initialize the BSC control commands.\n"); + return -1; + } +#endif + +#if 0 + if (msc_ctrl_cmds_install(msc_network) != 0) { + printf("Failed to initialize the MSC control commands.\n"); + return -1; + } +#endif + + /* seed the PRNG */ + srand(time(NULL)); + /* TODO: is this used for crypto?? Improve randomness, at least we + * should try to use the nanoseconds part of the current time. */ + + if (db_init(msc_cmdline_config.database_name)) { + printf("DB: Failed to init database: %s\n", + msc_cmdline_config.database_name); + return 4; + } + + osmo_fsm_log_addr(true); + if (msc_vlr_start(msc_network)) { + fprintf(stderr, "Failed to start VLR\n"); + exit(1); + } + + msc_subscr_conn_init(); + + if (db_prepare()) { + printf("DB: Failed to prepare database.\n"); + return 5; + } + + /* setup the timer */ + osmo_timer_setup(&db_sync_timer, db_sync_timer_cb, NULL); + if (msc_cmdline_config.use_db_counter) + osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); + + signal(SIGINT, &signal_handler); + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); + osmo_init_ignore_signals(); + + /* start the SMS queue */ + if (sms_queue_start(msc_network, 20) != 0) + return -1; + + msc_network->mgcpgw.client = mgcpgw_client_init( + msc_network, &msc_network->mgcpgw.conf); + + if (mgcpgw_client_connect(msc_network->mgcpgw.client)) { + printf("MGCPGW connect failed\n"); + return 7; + } + + /* Set up A-Interface */ + /* TODO: implement A-Interface and remove above legacy stuff. */ + + /* Set up IuCS */ + iu_init(tall_msc_ctx, "127.0.0.1", 14001, rcvmsg_iu_cs, rx_iu_event); + + if (msc_cmdline_config.daemonize) { + rc = osmo_daemonize(); + if (rc < 0) { + perror("Error during daemonize"); + return 6; + } + } + + while (1) { + log_reset_context(); + osmo_select_main(0); + } +} diff --git a/openbsc/src/osmo-nitb/Makefile.am b/openbsc/src/osmo-nitb/Makefile.am deleted file mode 100644 index a99334d33..000000000 --- a/openbsc/src/osmo-nitb/Makefile.am +++ /dev/null @@ -1,46 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(COVERAGE_CFLAGS) \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOCTRL_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(LIBSMPP34_CFLAGS) \ - $(LIBCRYPTO_CFLAGS) \ - $(NULL) - -AM_LDFLAGS = \ - $(COVERAGE_LDFLAGS) \ - $(NULL) - -bin_PROGRAMS = \ - osmo-nitb \ - $(NULL) - -osmo_nitb_SOURCES = \ - bsc_hack.c \ - $(NULL) - -osmo_nitb_LDADD = \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libcommon-cs/libcommon-cs.a \ - $(top_builddir)/src/libmsc/libmsc.a \ - $(top_builddir)/src/libvlr/libvlr.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOCTRL_LIBS) \ - $(LIBOSMOABIS_LIBS) \ - $(LIBSMPP34_LIBS) \ - $(LIBCRYPTO_LIBS) \ - -ldbi \ - $(NULL) diff --git a/openbsc/src/osmo-nitb/bsc_hack.c b/openbsc/src/osmo-nitb/bsc_hack.c deleted file mode 100644 index b04a39e55..000000000 --- a/openbsc/src/osmo-nitb/bsc_hack.c +++ /dev/null @@ -1,416 +0,0 @@ -/* A hackish minimal BSC (+MSC +HLR) implementation */ - -/* (C) 2008-2010 by Harald Welte - * (C) 2009-2012 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 . - * - */ - -#include -#include -#include -#include -#include -#include - -#define _GNU_SOURCE -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../bscconfig.h" - -/* MCC and MNC for the Location Area Identifier */ -struct gsm_network *bsc_gsmnet = 0; -static const char *database_name = "sms.db"; -static const char *config_file = "osmo-nitb.cfg"; -static const char *rf_ctrl_path = NULL; -extern const char *openbsc_copyright; -static int daemonize = 0; -static const char *mncc_sock_path = NULL; -static int use_db_counter = 1; - -/* timer to store statistics */ -#define DB_SYNC_INTERVAL 60, 0 -#define EXPIRE_INTERVAL 10, 0 - -static struct osmo_timer_list db_sync_timer; - -static void create_pcap_file(char *file) -{ - mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - int fd = open(file, O_WRONLY|O_TRUNC|O_CREAT, mode); - - if (fd < 0) { - perror("Failed to open file for pcap"); - return; - } - - e1_set_pcap_fd(fd); -} - -static void print_usage() -{ - printf("Usage: osmo-nitb\n"); -} - -static void print_help() -{ - printf(" Some useful help...\n"); - printf(" -h --help This text.\n"); - printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM Enable debugging.\n"); - printf(" -D --daemonize Fork the process into a background daemon.\n"); - printf(" -c --config-file filename The config file to use.\n"); - printf(" -s --disable-color\n"); - printf(" -l --database db-name The database to use.\n"); - printf(" -a --authorize-everyone Authorize every new subscriber. Dangerous!\n"); - printf(" -T --timestamp Prefix every log line with a timestamp.\n"); - printf(" -V --version Print the version of OpenBSC.\n"); - printf(" -P --rtp-proxy Enable the RTP Proxy code inside OpenBSC.\n"); - printf(" -e --log-level number Set a global loglevel.\n"); - printf(" -M --mncc-sock-path PATH Disable built-in MNCC handler and offer socket.\n"); - printf(" -m --mncc-sock Same as `-M /tmp/bsc_mncc' (deprecated).\n"); - printf(" -C --no-dbcounter Disable regular syncing of counters to database.\n"); - printf(" -r --rf-ctl PATH A unix domain socket to listen for cmds.\n"); - printf(" -p --pcap PATH Write abis communication to pcap trace file.\n"); -} - -static void handle_options(int argc, char **argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"debug", 1, 0, 'd'}, - {"daemonize", 0, 0, 'D'}, - {"config-file", 1, 0, 'c'}, - {"disable-color", 0, 0, 's'}, - {"database", 1, 0, 'l'}, - {"authorize-everyone", 0, 0, 'a'}, - {"pcap", 1, 0, 'p'}, - {"timestamp", 0, 0, 'T'}, - {"version", 0, 0, 'V' }, - {"rtp-proxy", 0, 0, 'P'}, - {"log-level", 1, 0, 'e'}, - {"mncc-sock", 0, 0, 'm'}, - {"mncc-sock-path", 1, 0, 'M'}, - {"no-dbcounter", 0, 0, 'C'}, - {"rf-ctl", 1, 0, 'r'}, - {0, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "hd:Dsl:ar:p:TPVc:e:mCr:M:", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - print_usage(); - print_help(); - exit(0); - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'd': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 'D': - daemonize = 1; - break; - case 'l': - database_name = optarg; - break; - case 'c': - config_file = optarg; - break; - case 'p': - create_pcap_file(optarg); - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'P': - ipacc_rtp_direct = 0; - break; - case 'e': - log_set_log_level(osmo_stderr_target, atoi(optarg)); - break; - case 'M': - mncc_sock_path = optarg; - break; - case 'm': - mncc_sock_path = "/tmp/bsc_mncc"; - break; - case 'C': - use_db_counter = 0; - break; - case 'V': - print_version(1); - exit(0); - break; - case 'r': - rf_ctrl_path = optarg; - break; - default: - /* catch unknown options *as well as* missing arguments. */ - fprintf(stderr, "Error in command line options. Exiting.\n"); - exit(-1); - break; - } - } -} - -extern void *tall_vty_ctx; -static void signal_handler(int signal) -{ - fprintf(stdout, "signal %u received\n", signal); - - switch (signal) { - case SIGINT: - bsc_shutdown_net(bsc_gsmnet); - osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); - sleep(3); - exit(0); - break; - case SIGABRT: - osmo_generate_backtrace(); - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - talloc_report(tall_vty_ctx, stderr); - talloc_report_full(tall_bsc_ctx, stderr); - break; - case SIGUSR2: - talloc_report_full(tall_vty_ctx, stderr); - break; - default: - break; - } -} - -/* timer handling */ -static int _db_store_counter(struct osmo_counter *counter, void *data) -{ - return db_store_counter(counter); -} - -static void db_sync_timer_cb(void *data) -{ - /* store counters to database and re-schedule */ - osmo_counters_for_each(_db_store_counter, NULL); - osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); -} - -static void subscr_expire_cb(void *data) -{ - /* TODO expire vlr_subscrs? */ - osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL); -} - -extern int bsc_vty_go_parent(struct vty *vty); - -static struct vty_app_info vty_info = { - .name = "OpenBSC", - .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, -}; - -int main(int argc, char **argv) -{ - int rc; - - vty_info.copyright = openbsc_copyright; - - tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); - talloc_ctx_init(tall_bsc_ctx); -#if 0 - on_dso_load_token(); -#endif - on_dso_load_rrlp(); - on_dso_load_ho_dec(); - - libosmo_abis_init(tall_bsc_ctx); - osmo_init_logging(&log_info); - osmo_stats_init(tall_bsc_ctx); - bts_init(); - vty_init(&vty_info); - - /* Parse options */ - handle_options(argc, argv); - - /* Allocate global gsm_network struct; choose socket/internal MNCC */ - rc = bsc_network_alloc(mncc_sock_path? - mncc_sock_from_cc : int_mncc_recv); - if (rc) { - fprintf(stderr, "Allocation failed. Exiting.\n"); - exit(1); - } - - /* Initialize VTY */ - bsc_vty_init(bsc_gsmnet); - ctrl_vty_init(tall_bsc_ctx); - if (msc_vlr_alloc(bsc_gsmnet)) { - fprintf(stderr, "Failed to allocate VLR\n"); - exit(1); - } - -#ifdef BUILD_SMPP - if (smpp_openbsc_alloc_init(tall_bsc_ctx) < 0) - return -1; -#endif - - /* Initialize MNCC socket if appropriate */ - if (mncc_sock_path) { - rc = mncc_sock_init(bsc_gsmnet, mncc_sock_path); - if (rc) { - fprintf(stderr, "MNCC socket initialization failed. exiting.\n"); - exit(1); - } - } else - DEBUGP(DMNCC, "Using internal MNCC handler.\n"); - - /* - * For osmo-nitb, skip TCH/F for now, because otherwise dyn TS - * always imply the possibility to have a mix of TCH/F and - * TCH/H channels; if two phones request a TCH/F and a TCH/H, - * respectively, they cannot call each other. If we deny TCH/F, - * they will both fall back to TCH/H, and dynamic channels are - * usable. See OS#1778. - * - * A third-party MSC may well be able to handle a TCH/H TCH/F - * mismatch. Moreover, this option may be overwritten in the - * config file or in VTY. - */ - bsc_gsmnet->dyn_ts_allow_tch_f = false; - - /* Read the config */ - rc = bsc_network_configure(config_file); - if (rc < 0) { - fprintf(stderr, "Reading config failed. Exiting.\n"); - exit(1); - } - -#ifdef BUILD_SMPP - smpp_openbsc_start(bsc_gsmnet); -#endif - bsc_api_init(bsc_gsmnet, msc_bsc_api()); - - /* start control interface after reading config for - * ctrl_vty_get_bind_addr() */ - bsc_gsmnet->ctrl = bsc_controlif_setup(bsc_gsmnet, - ctrl_vty_get_bind_addr(), - OSMO_CTRL_PORT_NITB_BSC); - if (!bsc_gsmnet->ctrl) { - printf("Failed to initialize control interface. Exiting.\n"); - return -1; - } - - if (bsc_base_ctrl_cmds_install() != 0) { - printf("Failed to initialize the BSC control commands.\n"); - return -1; - } - - if (msc_ctrl_cmds_install(bsc_gsmnet) != 0) { - printf("Failed to initialize the MSC control commands.\n"); - return -1; - } - - /* seed the PRNG */ - srand(time(NULL)); - - bsc_gsmnet->bsc_data->rf_ctrl = osmo_bsc_rf_create(rf_ctrl_path, bsc_gsmnet); - if (!bsc_gsmnet->bsc_data->rf_ctrl) { - fprintf(stderr, "Failed to create the RF service.\n"); - exit(1); - } - - osmo_fsm_log_addr(true); - if (msc_vlr_start(bsc_gsmnet)) { - fprintf(stderr, "Failed to start VLR\n"); - exit(1); - } - - msc_subscr_conn_init(); - - if (db_init(database_name)) { - printf("DB: Failed to init database. Please check the option settings.\n"); - return -1; - } - printf("DB: Database initialized.\n"); - - if (db_prepare()) { - printf("DB: Failed to prepare database.\n"); - return -1; - } - printf("DB: Database prepared.\n"); - - /* setup the timer */ - osmo_timer_setup(&db_sync_timer, db_sync_timer_cb, NULL); - if (use_db_counter) - osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); - - osmo_timer_setup(&bsc_gsmnet->subscr_expire_timer, subscr_expire_cb, - NULL); - osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL); - - signal(SIGINT, &signal_handler); - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - signal(SIGUSR2, &signal_handler); - osmo_init_ignore_signals(); - - /* start the SMS queue */ - if (sms_queue_start(bsc_gsmnet, 20) != 0) - return -1; - - if (daemonize) { - rc = osmo_daemonize(); - if (rc < 0) { - perror("Error during daemonize"); - exit(1); - } - } - - while (1) { - log_reset_context(); - osmo_select_main(0); - } -} -- cgit v1.2.3