diff options
author | Neels Hofmeyr <neels@hofmeyr.de> | 2020-09-17 17:54:39 +0200 |
---|---|---|
committer | Neels Hofmeyr <neels@hofmeyr.de> | 2020-10-09 00:26:02 +0200 |
commit | 4ae338d5b6d737fd37826c02e27e2553cf23e2a3 (patch) | |
tree | 4ad03eb36890e9fcf1fce111628b6a4b027a6214 /src/osmo-bsc/lb.c | |
parent | 1ebc0422d66c23be9294d2dc243dcba62f31ecd4 (diff) |
LCS: implement the bulk of Location Services
Depends: I4d7302a4853518916b6b425e710c10568eb2ffe5 (libosmocore)
Change-Id: I28314ba97df86a118497e9b2770e2e6e2484e872
Diffstat (limited to 'src/osmo-bsc/lb.c')
-rw-r--r-- | src/osmo-bsc/lb.c | 662 |
1 files changed, 662 insertions, 0 deletions
diff --git a/src/osmo-bsc/lb.c b/src/osmo-bsc/lb.c new file mode 100644 index 000000000..6ab131f66 --- /dev/null +++ b/src/osmo-bsc/lb.c @@ -0,0 +1,662 @@ +/* Lb interface low level SCCP handling */ +/* + * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * All Rights Reserved + * + * Author: Neels Hofmeyr <neels@hofmeyr.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/bsc/lb.h> + +#include <osmocom/gsm/bssmap_le.h> +#include <osmocom/sigtran/sccp_helpers.h> +#include <osmocom/bsc/gsm_data.h> +#include <osmocom/bsc/vty.h> +#include <osmocom/bsc/debug.h> +#include <osmocom/bsc/osmo_bsc_sigtran.h> +#include <osmocom/bsc/lcs_loc_req.h> + +static struct gsm_subscriber_connection *get_bsc_conn_by_lb_conn_id(int conn_id) +{ + struct gsm_subscriber_connection *conn; + + llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry) { + if (conn->lcs.lb.state != SUBSCR_SCCP_ST_NONE + && conn->lcs.lb.conn_id == conn_id) + return conn; + } + + return NULL; +} + +/* Send reset to SMLC */ +int bssmap_le_tx_reset() + // TODO use this -- patch coming up +{ + struct osmo_ss7_instance *ss7; + struct msgb *msg; + struct bssap_le_pdu reset = { + .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE, + .bssmap_le = { + .msg_type = BSSMAP_LE_MSGT_RESET, + .reset = GSM0808_CAUSE_EQUIPMENT_FAILURE, + }, + }; + + ss7 = osmo_ss7_instance_find(bsc_gsmnet->smlc->cs7_instance); + OSMO_ASSERT(ss7); + LOGP(DLCS, LOGL_NOTICE, "Sending RESET to SMLC: %s\n", osmo_sccp_addr_name(ss7, &bsc_gsmnet->smlc->smlc_addr)); + msg = osmo_bssap_le_enc(&reset); + + rate_ctr_inc(&bsc_gsmnet->smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_UDT_RESET]); + return osmo_sccp_tx_unitdata_msg(bsc_gsmnet->smlc->sccp_user, &bsc_gsmnet->smlc->bsc_addr, + &bsc_gsmnet->smlc->smlc_addr, msg); +} + +/* Send reset-ack to SMLC */ +int bssmap_le_tx_reset_ack() +{ + struct osmo_ss7_instance *ss7; + struct msgb *msg; + struct bssap_le_pdu reset_ack = { + .discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE, + .bssmap_le = { + .msg_type = BSSMAP_LE_MSGT_RESET_ACK, + }, + }; + + ss7 = osmo_ss7_instance_find(bsc_gsmnet->smlc->cs7_instance); + OSMO_ASSERT(ss7); + LOGP(DLCS, LOGL_NOTICE, "Tx RESET ACK to SMLC: %s\n", osmo_sccp_addr_name(ss7, &bsc_gsmnet->smlc->smlc_addr)); + msg = osmo_bssap_le_enc(&reset_ack); + + rate_ctr_inc(&bsc_gsmnet->smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_UDT_RESET_ACK]); + return osmo_sccp_tx_unitdata_msg(bsc_gsmnet->smlc->sccp_user, &bsc_gsmnet->smlc->bsc_addr, + &bsc_gsmnet->smlc->smlc_addr, msg); +} + +static int bssmap_le_handle_reset(const struct bssmap_le_pdu *pdu) +{ + struct gsm_subscriber_connection *conn; + int rc; + + /* Abort all ongoing Location Requests */ + llist_for_each_entry(conn, &bsc_gsmnet->subscr_conns, entry) + lcs_loc_req_reset(conn); + + rc = bssmap_le_tx_reset_ack(); + if (!rc) + bsc_gsmnet->smlc->ready = true; + return rc; +} + +static int bssmap_le_handle_reset_ack() +{ + bsc_gsmnet->smlc->ready = true; + return 0; +} + +static int handle_unitdata_from_smlc(const struct osmo_sccp_addr *smlc_addr, struct msgb *msg, + const struct osmo_sccp_user *scu) +{ + struct osmo_ss7_instance *ss7; + struct bssap_le_pdu bssap_le; + struct osmo_bssap_le_err *err; + struct rate_ctr *ctr = bsc_gsmnet->smlc->ctrs->ctr; + + ss7 = osmo_sccp_get_ss7(osmo_sccp_get_sccp(scu)); + OSMO_ASSERT(ss7); + + if (osmo_sccp_addr_cmp(smlc_addr, &bsc_gsmnet->smlc->smlc_addr, OSMO_SCCP_ADDR_T_MASK)) { + LOGP(DLCS, LOGL_ERROR, "Rx BSSMAP-LE UnitData from unknown remote address: %s\n", + osmo_sccp_addr_name(ss7, smlc_addr)); + rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_UNKNOWN_PEER]); + return -EINVAL; + } + + if (osmo_bssap_le_dec(&bssap_le, &err, msg, msg)) { + LOGP(DLCS, LOGL_ERROR, "Rx BSSAP-LE UnitData with error: %s\n", err->logmsg); + rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG]); + return -EINVAL; + } + + if (bssap_le.discr != BSSAP_LE_MSG_DISCR_BSSMAP_LE) { + LOGP(DLCS, LOGL_ERROR, "Rx BSSAP-LE: discr %d not implemented\n", bssap_le.discr); + return -ENOTSUP; + } + + switch (bssap_le.bssmap_le.msg_type) { + case BSSMAP_LE_MSGT_RESET: + rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_UDT_RESET]); + LOGP(DLCS, LOGL_NOTICE, "RESET from SMLC: %s\n", osmo_sccp_addr_name(ss7, smlc_addr)); + return bssmap_le_handle_reset(&bssap_le.bssmap_le); + case BSSMAP_LE_MSGT_RESET_ACK: + rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_UDT_RESET_ACK]); + LOGP(DLCS, LOGL_NOTICE, "RESET-ACK from SMLC: %s\n", osmo_sccp_addr_name(ss7, smlc_addr)); + return bssmap_le_handle_reset_ack(); + default: + rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG]); + LOGP(DLCS, LOGL_ERROR, "Rx unimplemented UDT message type %s\n", + osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le)); + return -EINVAL; + } +} + +static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) +{ + struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph; + struct osmo_sccp_user *scu = _scu; + struct gsm_subscriber_connection *conn; + int rc = 0; + + switch (OSMO_PRIM_HDR(&scu_prim->oph)) { + case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION): + /* Handle inbound UnitData */ + DEBUGP(DLCS, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + rc = handle_unitdata_from_smlc(&scu_prim->u.unitdata.calling_addr, oph->msg, scu); + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION): + /* Handle inbound connections. A Location Request is always started on the A interface, and OsmoBSC + * forwards this to the SMLC by performing an N-CONNECT from BSC -> SMLC. This is the reverse + * direction: N-CONNECT from SMLC -> BSC, which should never happen. */ + LOGP(DLCS, LOGL_ERROR, "N-CONNECT.ind(X->%u): inbound connect from SMLC is not expected to happen\n", + scu_prim->u.connect.conn_id); + rc = osmo_sccp_tx_disconn(scu, scu_prim->u.connect.conn_id, &scu_prim->u.connect.called_addr, 0); + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM): + /* Handle inbound confirmation of outbound connection */ + DEBUGP(DLCS, "N-CONNECT.cnf(%u)\n", scu_prim->u.connect.conn_id); + conn = get_bsc_conn_by_lb_conn_id(scu_prim->u.connect.conn_id); + if (conn) { + conn->lcs.lb.state = SUBSCR_SCCP_ST_CONNECTED; + if (msgb_l2len(oph->msg) > 0) { + rc = lcs_loc_req_rx_bssmap_le(conn, oph->msg); + } + } else { + LOGP(DLCS, LOGL_ERROR, "N-CONNECT.cfm(%u) for unknown conn\n", scu_prim->u.connect.conn_id); + rc = -EINVAL; + } + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION): + /* Handle incoming connection oriented data */ + DEBUGP(DLCS, "N-DATA.ind(%u)\n", scu_prim->u.data.conn_id); + + conn = get_bsc_conn_by_lb_conn_id(scu_prim->u.data.conn_id); + if (!conn) { + LOGP(DLCS, LOGL_ERROR, "N-DATA.ind(%u) for unknown conn_id\n", scu_prim->u.data.conn_id); + rc = -EINVAL; + } else if (conn->lcs.lb.state != SUBSCR_SCCP_ST_CONNECTED) { + LOGP(DLCS, LOGL_ERROR, "N-DATA.ind(%u) for conn that is not confirmed\n", + scu_prim->u.data.conn_id); + rc = -EINVAL; + } else { + rc = lcs_loc_req_rx_bssmap_le(conn, oph->msg); + } + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION): + DEBUGP(DLCS, "N-DISCONNECT.ind(%u, %s, cause=%i)\n", scu_prim->u.disconnect.conn_id, + osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)), + scu_prim->u.disconnect.cause); + /* indication of disconnect */ + conn = get_bsc_conn_by_lb_conn_id(scu_prim->u.disconnect.conn_id); + if (!conn) { + LOGP(DLCS, LOGL_ERROR, "N-DISCONNECT.ind for unknown conn_id %u\n", + scu_prim->u.disconnect.conn_id); + rc = -EINVAL; + } else { + conn->lcs.lb.state = SUBSCR_SCCP_ST_NONE; + if (msgb_l2len(oph->msg) > 0) { + rc = lcs_loc_req_rx_bssmap_le(conn, oph->msg); + } + } + break; + + default: + LOGP(DLCS, LOGL_ERROR, "Unhandled SIGTRAN operation %s on primitive %u\n", + get_value_string(osmo_prim_op_names, oph->operation), oph->primitive); + break; + } + + msgb_free(oph->msg); + return rc; +} + +static int lb_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct osmo_ss7_instance *ss7; + int conn_id; + int rc; + + OSMO_ASSERT(conn); + OSMO_ASSERT(msg); + + if (conn->lcs.lb.state != SUBSCR_SCCP_ST_NONE) { + LOGPFSMSL(conn->fi, DLCS, LOGL_ERROR, + "Cannot open BSSMAP-LE conn to SMLC, another conn is still active for this subscriber\n"); + return -EINVAL; + } + + conn_id = bsc_sccp_inst_next_conn_id(bsc_gsmnet->smlc->sccp); + if (conn_id < 0) { + LOGPFSMSL(conn->fi, DLCS, LOGL_ERROR, "Unable to allocate SCCP Connection ID for BSSMAP-LE to SMLC\n"); + return -ENOSPC; + } + conn->lcs.lb.conn_id = conn_id; + ss7 = osmo_ss7_instance_find(bsc_gsmnet->smlc->cs7_instance); + OSMO_ASSERT(ss7); + LOGPFSMSL(conn->fi, DLCS, LOGL_INFO, "Opening new SCCP connection (id=%i) to SMLC: %s\n", conn_id, + osmo_sccp_addr_name(ss7, &bsc_gsmnet->smlc->smlc_addr)); + + rc = osmo_sccp_tx_conn_req_msg(bsc_gsmnet->smlc->sccp_user, conn_id, &bsc_gsmnet->smlc->bsc_addr, + &bsc_gsmnet->smlc->smlc_addr, msg); + if (rc >= 0) + rate_ctr_inc(&bsc_gsmnet->smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_SUCCESS]); + else + rate_ctr_inc(&bsc_gsmnet->smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_ERR_SEND]); + if (rc >= 0) + conn->lcs.lb.state = SUBSCR_SCCP_ST_WAIT_CONN_CONF; + + return rc; +} + +void lb_close_conn(struct gsm_subscriber_connection *conn) +{ + if (conn->lcs.lb.state == SUBSCR_SCCP_ST_NONE) + return; + osmo_sccp_tx_disconn(bsc_gsmnet->smlc->sccp_user, conn->lcs.lb.conn_id, &bsc_gsmnet->smlc->bsc_addr, 0); + conn->lcs.lb.state = SUBSCR_SCCP_ST_NONE; +} + +/* Send data to SMLC, take ownership of *msg */ +int lb_send(struct gsm_subscriber_connection *conn, const struct bssap_le_pdu *bssap_le) +{ + int rc; + struct msgb *msg; + + OSMO_ASSERT(conn); + + msg = osmo_bssap_le_enc(bssap_le); + if (!msg) { + LOGPFSMSL(conn->fi, DLCS, LOGL_ERROR, "Failed to encode %s\n", + osmo_bssap_le_pdu_to_str_c(OTC_SELECT, bssap_le)); + return -EINVAL; + } + + if (conn->lcs.lb.state == SUBSCR_SCCP_ST_NONE) { + rc = lb_open_conn(conn, msg); + goto count_tx; + } + + LOGPFSMSL(conn->fi, DLCS, LOGL_DEBUG, "Tx %s\n", osmo_bssap_le_pdu_to_str_c(OTC_SELECT, bssap_le)); + rc = osmo_sccp_tx_data_msg(bsc_gsmnet->smlc->sccp_user, conn->lcs.lb.conn_id, msg); + if (rc >= 0) + rate_ctr_inc(&bsc_gsmnet->smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_SUCCESS]); + else + rate_ctr_inc(&bsc_gsmnet->smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_ERR_SEND]); + +count_tx: + if (rc < 0) + return rc; + + switch (bssap_le->bssmap_le.msg_type) { + case BSSMAP_LE_MSGT_PERFORM_LOC_REQ: + rate_ctr_inc(&bsc_gsmnet->smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_REQUEST]); + break; + case BSSMAP_LE_MSGT_PERFORM_LOC_ABORT: + rate_ctr_inc(&bsc_gsmnet->smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_ABORT]); + break; + case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO: + switch (bssap_le->bssmap_le.conn_oriented_info.apdu.msg_type) { + case BSSLAP_MSGT_TA_RESPONSE: + rate_ctr_inc(&bsc_gsmnet->smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_TA_RESPONSE]); + break; + case BSSLAP_MSGT_REJECT: + rate_ctr_inc(&bsc_gsmnet->smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_REJECT]); + break; + case BSSLAP_MSGT_RESET: + rate_ctr_inc(&bsc_gsmnet->smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_RESET]); + break; + case BSSLAP_MSGT_ABORT: + rate_ctr_inc(&bsc_gsmnet->smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_ABORT]); + break; + default: + break; + } + break; + default: + break; + } + return 0; +} + +/* Default point-code to be used as local address (BSC) */ +#define BSC_DEFAULT_PC "0.23.3" + +/* Default point-code to be used as remote address (SMLC) */ +#define SMLC_DEFAULT_PC "0.23.6" + +#define DEFAULT_ASP_LOCAL_IP "localhost" +#define DEFAULT_ASP_REMOTE_IP "localhost" + +/* Initialize Lb interface to SMLC */ +int lb_init() +{ + uint32_t default_pc; + struct osmo_ss7_instance *cs7_inst = NULL; + struct osmo_sccp_instance *sccp; + enum osmo_ss7_asp_protocol used_proto = OSMO_SS7_ASP_PROT_M3UA; + char inst_name[32]; + const char *smlc_name = "smlc"; + + if (!bsc_gsmnet->smlc) { + bsc_gsmnet->smlc = talloc_zero(bsc_gsmnet, struct smlc_config); + bsc_gsmnet->smlc->ctrs = rate_ctr_group_alloc(bsc_gsmnet, &smlc_ctrg_desc, 0); + } + OSMO_ASSERT(bsc_gsmnet->smlc); + + if (!bsc_gsmnet->smlc->cs7_instance_valid) { + bsc_gsmnet->smlc->cs7_instance = 0; + } + cs7_inst = osmo_ss7_instance_find_or_create(tall_bsc_ctx, bsc_gsmnet->smlc->cs7_instance); + OSMO_ASSERT(cs7_inst); + + /* If unset, use default SCCP address for the SMLC */ + if (!bsc_gsmnet->smlc->smlc_addr.presence) + osmo_sccp_make_addr_pc_ssn(&bsc_gsmnet->smlc->smlc_addr, + osmo_ss7_pointcode_parse(NULL, SMLC_DEFAULT_PC), + OSMO_SCCP_SSN_SMLC_BSSAP_LE); + + /* Set up SCCP user and one ASP+AS */ + snprintf(inst_name, sizeof(inst_name), "Lb-%u-%s", cs7_inst->cfg.id, osmo_ss7_asp_protocol_name(used_proto)); + LOGP(DLCS, LOGL_NOTICE, "Initializing SCCP connection for Lb/%s on cs7 instance %u\n", + osmo_ss7_asp_protocol_name(used_proto), cs7_inst->cfg.id); + + /* SS7 Protocol stack */ + default_pc = osmo_ss7_pointcode_parse(NULL, BSC_DEFAULT_PC); + sccp = osmo_sccp_simple_client_on_ss7_id(tall_bsc_ctx, cs7_inst->cfg.id, inst_name, + default_pc, used_proto, + 0, DEFAULT_ASP_LOCAL_IP, + 0, DEFAULT_ASP_REMOTE_IP); + if (!sccp) + return -EINVAL; + bsc_gsmnet->smlc->sccp = sccp; + + /* If unset, use default local SCCP address */ + if (!bsc_gsmnet->smlc->bsc_addr.presence) + osmo_sccp_local_addr_by_instance(&bsc_gsmnet->smlc->bsc_addr, sccp, + OSMO_SCCP_SSN_BSC_BSSAP_LE); + + if (!osmo_sccp_check_addr(&bsc_gsmnet->smlc->bsc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) { + LOGP(DLCS, LOGL_ERROR, + "%s %s: invalid local (BSC) SCCP address: %s\n", + inst_name, smlc_name, osmo_sccp_inst_addr_name(sccp, &bsc_gsmnet->smlc->bsc_addr)); + return -EINVAL; + } + + if (!osmo_sccp_check_addr(&bsc_gsmnet->smlc->smlc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) { + LOGP(DLCS, LOGL_ERROR, + "%s %s: invalid remote (SMLC) SCCP address: %s\n", + inst_name, smlc_name, osmo_sccp_inst_addr_name(sccp, &bsc_gsmnet->smlc->smlc_addr)); + return -EINVAL; + } + + LOGP(DLCS, LOGL_NOTICE, "Lb: %s %s: local (BSC) SCCP address: %s\n", + inst_name, smlc_name, osmo_sccp_inst_addr_name(sccp, &bsc_gsmnet->smlc->bsc_addr)); + LOGP(DLCS, LOGL_NOTICE, "Lb: %s %s: remote (SMLC) SCCP address: %s\n", + inst_name, smlc_name, osmo_sccp_inst_addr_name(sccp, &bsc_gsmnet->smlc->smlc_addr)); + + /* Bind SCCP user. */ + bsc_gsmnet->smlc->sccp_user = osmo_sccp_user_find(sccp, bsc_gsmnet->smlc->bsc_addr.ssn, bsc_gsmnet->smlc->bsc_addr.pc); + LOGP(DLCS, LOGL_NOTICE, "%s %s: %s\n", inst_name, smlc_name, + bsc_gsmnet->smlc->sccp_user ? "user already bound for this SCCP instance" : "binding SCCP user"); + if (!bsc_gsmnet->smlc->sccp_user) + bsc_gsmnet->smlc->sccp_user = osmo_sccp_user_bind(sccp, smlc_name, sccp_sap_up, bsc_gsmnet->smlc->bsc_addr.ssn); + if (!bsc_gsmnet->smlc->sccp_user) + return -EINVAL; + + return 0; +} + +/********************************************************************************* + * VTY Interface (Configuration + Introspection) + *********************************************************************************/ + +DEFUN(cfg_smlc, cfg_smlc_cmd, + "smlc", "Configure Lb Link to Serving Mobile Location Centre\n") +{ + vty->node = SMLC_NODE; + return CMD_SUCCESS; +} + +static struct cmd_node smlc_node = { + SMLC_NODE, + "%s(config-smlc)# ", + 1, +}; + +static void enforce_ssn(struct vty *vty, struct osmo_sccp_addr *addr, enum osmo_sccp_ssn want_ssn) +{ + if (addr->presence & OSMO_SCCP_ADDR_T_SSN) { + if (addr->ssn != want_ssn) + vty_out(vty, + "setting an SSN (%u) different from the standard (%u) is not allowed, will use standard SSN for address: %s%s", + addr->ssn, want_ssn, osmo_sccp_addr_dump(addr), VTY_NEWLINE); + } + + addr->presence |= OSMO_SCCP_ADDR_T_SSN; + addr->ssn = want_ssn; +} + +DEFUN(cfg_smlc_cs7_bsc_addr, + cfg_smlc_cs7_bsc_addr_cmd, + "bsc-addr NAME", + "Local SCCP address of this BSC towards the SMLC\n" "Name of cs7 addressbook entry\n") +{ + const char *bsc_addr_name = argv[0]; + struct osmo_ss7_instance *ss7; + + ss7 = osmo_sccp_addr_by_name(&bsc_gsmnet->smlc->bsc_addr, bsc_addr_name); + if (!ss7) { + vty_out(vty, "Error: No such SCCP addressbook entry: '%s'%s", bsc_addr_name, VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; + } + + /* Prevent mixing addresses from different CS7 instances */ + if (bsc_gsmnet->smlc->cs7_instance_valid + && bsc_gsmnet->smlc->cs7_instance != ss7->cfg.id) { + vty_out(vty, + "Error: SCCP addressbook entry from mismatching CS7 instance: '%s'%s", + bsc_addr_name, VTY_NEWLINE); + return CMD_WARNING; + } + + bsc_gsmnet->smlc->cs7_instance = ss7->cfg.id; + bsc_gsmnet->smlc->cs7_instance_valid = true; + enforce_ssn(vty, &bsc_gsmnet->smlc->bsc_addr, OSMO_SCCP_SSN_BSC_BSSAP_LE); + bsc_gsmnet->smlc->bsc_addr_name = talloc_strdup(bsc_gsmnet, bsc_addr_name); + return CMD_SUCCESS; +} + +DEFUN(cfg_smlc_cs7_smlc_addr, + cfg_smlc_cs7_smlc_addr_cmd, + "smlc-addr NAME", + "Remote SCCP address of the SMLC\n" "Name of cs7 addressbook entry\n") +{ + const char *smlc_addr_name = argv[0]; + struct osmo_ss7_instance *ss7; + + ss7 = osmo_sccp_addr_by_name(&bsc_gsmnet->smlc->smlc_addr, smlc_addr_name); + if (!ss7) { + vty_out(vty, "Error: No such SCCP addressbook entry: '%s'%s", smlc_addr_name, VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; + } + + /* Prevent mixing addresses from different CS7/SS7 instances */ + if (bsc_gsmnet->smlc->cs7_instance_valid) { + if (bsc_gsmnet->smlc->cs7_instance != ss7->cfg.id) { + vty_out(vty, + "Error: SCCP addressbook entry from mismatching CS7 instance: '%s'%s", + smlc_addr_name, VTY_NEWLINE); + return CMD_ERR_INCOMPLETE; + } + } + + bsc_gsmnet->smlc->cs7_instance = ss7->cfg.id; + bsc_gsmnet->smlc->cs7_instance_valid = true; + enforce_ssn(vty, &bsc_gsmnet->smlc->smlc_addr, OSMO_SCCP_SSN_SMLC_BSSAP_LE); + bsc_gsmnet->smlc->smlc_addr_name = talloc_strdup(bsc_gsmnet, smlc_addr_name); + return CMD_SUCCESS; +} + +static int config_write_smlc(struct vty *vty) +{ + vty_out(vty, "smlc%s", VTY_NEWLINE); + if (bsc_gsmnet->smlc->bsc_addr_name) { + vty_out(vty, " bsc-addr %s%s", + bsc_gsmnet->smlc->bsc_addr_name, VTY_NEWLINE); + } + if (bsc_gsmnet->smlc->smlc_addr_name) { + vty_out(vty, " smlc-addr %s%s", + bsc_gsmnet->smlc->smlc_addr_name, VTY_NEWLINE); + } + + return 0; +} + +DEFUN(show_smlc, show_smlc_cmd, + "show smlc", + SHOW_STR "Display state of SMLC / Lb\n") +{ + vty_out(vty, "not implemented%s", VTY_NEWLINE); + return CMD_SUCCESS; +} + +void smlc_vty_init(void) +{ + install_element_ve(&show_smlc_cmd); + + install_element(CONFIG_NODE, &cfg_smlc_cmd); + install_node(&smlc_node, config_write_smlc); + install_element(SMLC_NODE, &cfg_smlc_cs7_bsc_addr_cmd); + install_element(SMLC_NODE, &cfg_smlc_cs7_smlc_addr_cmd); +} + +const struct rate_ctr_desc smlc_ctr_description[] = { + [SMLC_CTR_BSSMAP_LE_RX_UNKNOWN_PEER] = { + "bssmap_le:rx:unknown_peer", + "Number of received BSSMAP-LE messages from an unknown Calling SCCP address" + }, + [SMLC_CTR_BSSMAP_LE_RX_UDT_RESET] = { + "bssmap_le:rx:udt:reset:request", + "Number of received BSSMAP-LE UDT RESET messages" + }, + [SMLC_CTR_BSSMAP_LE_RX_UDT_RESET_ACK] = { + "bssmap_le:rx:udt:reset:ack", + "Number of received BSSMAP-LE UDT RESET ACKNOWLEDGE messages" + }, + [SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG] = { + "bssmap_le:rx:udt:err:inval", + "Number of received invalid BSSMAP-LE UDT messages" + }, + [SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG] = { + "bssmap_le:rx:dt1:err:inval", + "Number of received invalid BSSMAP-LE" + }, + [SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS] = { + "bssmap_le:rx:dt1:location:response_success", + "Number of received BSSMAP-LE Perform Location Response messages containing a location estimate" + }, + [SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE] = { + "bssmap_le:rx:dt1:location:response_failure", + "Number of received BSSMAP-LE Perform Location Response messages containing a failure cause" + }, + + [SMLC_CTR_BSSMAP_LE_TX_ERR_INVALID_MSG] = { + "bssmap_le:tx:err:inval", + "Number of outgoing BSSMAP-LE messages that are invalid (a bug?)" + }, + [SMLC_CTR_BSSMAP_LE_TX_ERR_CONN_NOT_READY] = { + "bssmap_le:tx:err:conn_not_ready", + "Number of BSSMAP-LE messages we tried to send when the connection was not ready yet" + }, + [SMLC_CTR_BSSMAP_LE_TX_ERR_SEND] = { + "bssmap_le:tx:err:send", + "Number of socket errors while sending BSSMAP-LE messages" + }, + [SMLC_CTR_BSSMAP_LE_TX_SUCCESS] = { + "bssmap_le:tx:success", + "Number of successfully sent BSSMAP-LE messages" + }, + + [SMLC_CTR_BSSMAP_LE_TX_UDT_RESET] = { + "bssmap_le:tx:udt:reset:request", + "Number of transmitted BSSMAP-LE UDT RESET messages" + }, + [SMLC_CTR_BSSMAP_LE_TX_UDT_RESET_ACK] = { + "bssmap_le:tx:udt:reset:ack", + "Number of transmitted BSSMAP-LE UDT RESET ACK messages" + }, + [SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_REQUEST] = { + "bssmap_le:tx:dt1:location:response", + "Number of transmitted BSSMAP-LE DT1 Perform Location Request messages" + }, + [SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_ABORT] = { + "bssmap_le:rx:dt1:location:abort", + "Number of received BSSMAP-LE Perform Location Abort messages" + }, + + [SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_TA_REQUEST] = { + "bssmap_le:rx:dt1:bsslap:ta_request", + "Number of received BSSMAP-LE Connection Oriented Information messages" + " with BSSLAP APDU containing TA Request" + }, + + [SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_TA_RESPONSE] = { + "bssmap_le:tx:dt1:bsslap:ta_response", + "Number of sent BSSMAP-LE Connection Oriented Information messages" + " with BSSLAP APDU containing TA Response" + }, + [SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_REJECT] = { + "bssmap_le:tx:dt1:bsslap:reject", + "Number of sent BSSMAP-LE Connection Oriented Information messages" + " with BSSLAP APDU containing Reject" + }, + [SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_RESET] = { + "bssmap_le:tx:dt1:bsslap:reset", + "Number of sent BSSMAP-LE Connection Oriented Information messages" + " with BSSLAP APDU containing Reset" + }, + [SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_ABORT] = { + "bssmap_le:tx:dt1:bsslap:abort", + "Number of sent BSSMAP-LE Connection Oriented Information messages" + " with BSSLAP APDU containing Abort" + }, + +}; + +const struct rate_ctr_group_desc smlc_ctrg_desc = { + "smlc", + "serving mobile location centre", + OSMO_STATS_CLASS_GLOBAL, + ARRAY_SIZE(smlc_ctr_description), + smlc_ctr_description, +}; |