LCS: implement the bulk of Location Services

Depends: I4d7302a4853518916b6b425e710c10568eb2ffe5 (libosmocore)
Change-Id: I28314ba97df86a118497e9b2770e2e6e2484e872
This commit is contained in:
Neels Hofmeyr 2020-09-17 17:54:39 +02:00
parent 1ebc0422d6
commit 4ae338d5b6
30 changed files with 1858 additions and 17 deletions

View File

@ -9,3 +9,4 @@
#library what description / commit summary line
manual needs common chapter cs7-config.adoc, vty_cpu_sched.adoc from osmo-gsm-manuals > 0.3.0
osmo-bsc Mobile Identity Coding OsmoBSC is stricter in rejecting invalid coding of Mobile Identity IEs
libosmocore >1.4.0 (1.4.1?) need BSSMAP-LE, BSSLAP, GAD coding

View File

@ -27,9 +27,12 @@ noinst_HEADERS = \
handover_fsm.h \
handover_vty.h \
ipaccess.h \
lb.h \
lchan_fsm.h \
lchan_rtp_fsm.h \
lchan_select.h \
lcs_loc_req.h \
lcs_ta_req.h \
meas_feed.h \
meas_rep.h \
misdn.h \

View File

@ -68,6 +68,8 @@ enum {
MSC_CTR_BSSMAP_RX_DT1_UNKNOWN,
MSC_CTR_BSSMAP_RX_DT1_DTAP,
MSC_CTR_BSSMAP_RX_DT1_DTAP_ERROR,
MSC_CTR_BSSMAP_RX_DT1_PERFORM_LOCATION_REQUEST,
MSC_CTR_BSSMAP_RX_DT1_PERFORM_LOCATION_ABORT,
/* Tx message counters (per connection type) */
MSC_CTR_BSSMAP_TX_BSS_MANAGEMENT,
@ -97,6 +99,8 @@ enum {
MSC_CTR_BSSMAP_TX_DT1_HANDOVER_COMPLETE,
MSC_CTR_BSSMAP_TX_DT1_HANDOVER_FAILURE,
MSC_CTR_BSSMAP_TX_DT1_DTAP,
MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS,
MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE,
MSC_CTR_MSCPOOL_SUBSCR_NEW,
MSC_CTR_MSCPOOL_SUBSCR_REATTACH,

View File

@ -44,6 +44,8 @@ enum gscon_fsm_event {
GSCON_EV_FORGET_LCHAN,
GSCON_EV_FORGET_MGW_ENDPOINT,
GSCON_EV_LCS_LOC_REQ_END,
};
struct gscon_clear_cmd_data {

View File

@ -27,6 +27,7 @@ enum {
DTS,
DAS,
DCBS,
DLCS,
Debug_LastEntry,
};

View File

@ -52,6 +52,7 @@ struct bsc_subscr;
struct gprs_ra_id;
struct handover;
struct osmo_sccp_instance;
struct smlc_config;
#define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3]
@ -304,6 +305,23 @@ struct gsm_subscriber_connection {
uint8_t ms_power_class:3;
bool rx_clear_command;
/* Location Services handling for this subscriber */
struct {
/* FSM to handle Perform Location Request coming in from the MSC via A interface,
* and receive BSSMAP-LE responses from the SMLC. */
struct lcs_loc_req *loc_req;
/* FSM to handle BSSLAP requests coming in from the SMLC via Lb interface.
* BSSLAP APDU are encapsulated in BSSMAP-LE Connection Oriented Information messages. */
struct lcs_bsslap *bsslap;
/* Lb interface to the SMLC: BSSMAP-LE/SCCP connection associated with this subscriber */
struct {
int conn_id;
enum subscr_sccp_state state;
} lb;
} lcs;
};
@ -1200,6 +1218,8 @@ struct gsm_network {
uint8_t nri_bitlen;
struct osmo_nri_ranges *null_nri_ranges;
struct smlc_config *smlc;
};
struct gsm_audio_support {

60
include/osmocom/bsc/lb.h Normal file
View File

@ -0,0 +1,60 @@
/* Location Services (LCS): low level Lb/SCCP handling in OsmoBSC, API */
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/sigtran/sccp_sap.h>
struct bssap_le_pdu;
struct gsm_subscriber_connection;
enum {
SMLC_CTR_BSSMAP_LE_RX_UNKNOWN_PEER,
SMLC_CTR_BSSMAP_LE_RX_UDT_RESET,
SMLC_CTR_BSSMAP_LE_RX_UDT_RESET_ACK,
SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG,
SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG,
SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS,
SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE,
SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_TA_REQUEST,
SMLC_CTR_BSSMAP_LE_TX_ERR_INVALID_MSG,
SMLC_CTR_BSSMAP_LE_TX_ERR_CONN_NOT_READY,
SMLC_CTR_BSSMAP_LE_TX_ERR_SEND,
SMLC_CTR_BSSMAP_LE_TX_SUCCESS,
SMLC_CTR_BSSMAP_LE_TX_UDT_RESET,
SMLC_CTR_BSSMAP_LE_TX_UDT_RESET_ACK,
SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_REQUEST,
SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_ABORT,
SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_TA_RESPONSE,
SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_REJECT,
SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_RESET,
SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_ABORT,
};
struct smlc_config {
uint32_t cs7_instance;
bool cs7_instance_valid;
struct osmo_sccp_instance *sccp;
struct osmo_sccp_user *sccp_user;
struct osmo_sccp_addr bsc_addr;
char *bsc_addr_name;
struct osmo_sccp_addr smlc_addr;
char *smlc_addr_name;
/*! True after either side has sent a BSSMAP-LE RESET-ACK */
bool ready;
struct rate_ctr_group *ctrs;
};
extern const struct rate_ctr_desc smlc_ctr_description[];
extern const struct rate_ctr_group_desc smlc_ctrg_desc;
int lb_init();
int lb_send(struct gsm_subscriber_connection *conn, const struct bssap_le_pdu *bssap_le);
void lb_close_conn(struct gsm_subscriber_connection *conn);

View File

@ -0,0 +1,48 @@
/* Location Services (LCS): BSSMAP and BSSMAP-LE Perform Location Request handling in OsmoBSC, API */
#pragma once
#include <osmocom/gsm/bssmap_le.h>
#define LOG_LCS_LOC_REQ(LOC_REQ, level, fmt, args...) do { \
if (LOC_REQ) \
LOGPFSML((LOC_REQ)->fi, level, fmt, ## args); \
else \
LOGP(DLCS, level, "LCS Perf Loc Req: " fmt, ## args); \
} while(0)
struct lcs_ta_req;
enum lcs_loc_req_fsm_event {
LCS_LOC_REQ_EV_RX_LB_PERFORM_LOCATION_RESPONSE,
LCS_LOC_REQ_EV_RX_A_PERFORM_LOCATION_ABORT,
LCS_LOC_REQ_EV_TA_REQ_START,
LCS_LOC_REQ_EV_TA_REQ_END,
LCS_LOC_REQ_EV_HANDOVER_PERFORMED,
LCS_LOC_REQ_EV_CONN_CLEAR,
};
struct lcs_loc_req {
struct osmo_fsm_inst *fi;
struct gsm_subscriber_connection *conn;
struct {
struct bssmap_le_location_type location_type;
bool cell_id_present;
struct gsm0808_cell_id cell_id;
struct osmo_mobile_identity imsi;
struct osmo_mobile_identity imei;
} req;
bool resp_present;
struct bssmap_le_perform_loc_resp resp;
struct lcs_cause_ie lcs_cause;
struct lcs_ta_req *ta_req;
};
void lcs_loc_req_start(struct gsm_subscriber_connection *conn, struct msgb *msg);
int lcs_loc_req_rx_bssmap_le(struct gsm_subscriber_connection *conn, struct msgb *msg);
void lcs_loc_req_reset(struct gsm_subscriber_connection *conn);

View File

@ -0,0 +1,29 @@
/* Location Services (LCS): BSSLAP TA Request handling in OsmoBSC, API */
#pragma once
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/core/fsm.h>
#include <osmocom/gsm/bssmap_le.h>
#define LOG_LCS_TA_REQ(TA_REQ, level, fmt, args...) do { \
if (TA_REQ) \
LOGPFSML((TA_REQ)->fi, level, fmt, ## args); \
else \
LOGP(DLCS, level, "LCS TA Req: " fmt, ## args); \
} while(0)
enum lcs_ta_req_fsm_event {
LCS_TA_REQ_EV_GOT_TA,
LCS_TA_REQ_EV_ABORT,
};
struct lcs_ta_req {
struct osmo_fsm_inst *fi;
struct lcs_loc_req *loc_req;
enum lcs_cause failure_cause;
uint8_t failure_diagnostic_val;
};
int lcs_ta_req_start(struct lcs_loc_req *lcs_loc_req);
void lcs_bsslap_rx(struct gsm_subscriber_connection *conn, struct msgb *msg);

View File

@ -94,6 +94,7 @@ int paging_request_bts(const struct bsc_paging_params *params, struct gsm_bts *b
int paging_request_stop(struct bsc_msc_data **msc_p, enum bsc_paging_reason *reasons_p,
struct gsm_bts *bts, struct bsc_subscr *bsub);
int paging_request_cancel(struct bsc_subscr *bsub, enum bsc_paging_reason reasons);
/* update paging load */
void paging_update_buffer_space(struct gsm_bts *bts, uint16_t);

View File

@ -27,6 +27,7 @@ enum bsc_vty_node {
CBC_NODE,
CBC_SERVER_NODE,
CBC_CLIENT_NODE,
SMLC_NODE,
};
struct log_info;

View File

@ -65,9 +65,12 @@ osmo_bsc_SOURCES = \
handover_fsm.c \
handover_logic.c \
handover_vty.c \
lb.c \
lchan_fsm.c \
lchan_rtp_fsm.c \
lchan_select.c \
lcs_loc_req.c \
lcs_ta_req.c \
meas_feed.c \
meas_rep.c \
neighbor_ident.c \

View File

@ -37,6 +37,7 @@
#include <osmocom/bsc/gsm_04_08_rr.h>
#include <osmocom/bsc/neighbor_ident.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/lb.h>
#include <osmocom/bsc/smscb.h>
#include <osmocom/gsm/protocol/gsm_48_049.h>

View File

@ -23,6 +23,7 @@
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/lb.h>
/* We need an unused SCCP conn_id across all SCCP users. */
int bsc_sccp_inst_next_conn_id(struct osmo_sccp_instance *sccp)
@ -47,7 +48,13 @@ int bsc_sccp_inst_next_conn_id(struct osmo_sccp_instance *sccp)
}
}
/* Future for LCS: also check Lb-interface conn IDs here */
if (bsc_gsmnet->smlc->sccp == sccp
&& conn->lcs.lb.state != SUBSCR_SCCP_ST_NONE) {
if (conn_id == conn->lcs.lb.conn_id) {
conn_id_already_used = true;
break;
}
}
}
if (!conn_id_already_used)

View File

@ -46,6 +46,8 @@
#include <osmocom/bsc/codec_pref.h>
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
#include <osmocom/core/byteswap.h>
#include <osmocom/bsc/lb.h>
#include <osmocom/bsc/lcs_loc_req.h>
#define S(x) (1 << (x))
@ -86,6 +88,7 @@ static const struct value_string gscon_fsm_event_names[] = {
{GSCON_EV_LCLS_FAIL, "LCLS_FAIL"},
{GSCON_EV_FORGET_LCHAN, "FORGET_LCHAN"},
{GSCON_EV_FORGET_MGW_ENDPOINT, "FORGET_MGW_ENDPOINT"},
{GSCON_EV_LCS_LOC_REQ_END, "LCS_LOC_REQ_END"},
{}
};
@ -249,22 +252,41 @@ static void handle_bssap_n_connect(struct osmo_fsm_inst *fi, struct osmo_scu_pri
switch (bssmap_type) {
case BSS_MAP_MSG_HANDOVER_RQST:
/* First off, accept the new conn. */
osmo_sccp_tx_conn_resp(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id,
&scu_prim->u.connect.called_addr, NULL, 0);
case BSS_MAP_MSG_PERFORM_LOCATION_RQST:
break;
/* Make sure the conn FSM will osmo_sccp_tx_disconn() on term */
conn->sccp.state = SUBSCR_SCCP_ST_CONNECTED;
default:
LOGPFSML(fi, LOGL_NOTICE, "No support for N-CONNECT: %s: %s\n",
gsm0808_bssap_name(bs->type), gsm0808_bssmap_name(bssmap_type));
goto refuse;
}
/* First off, accept the new conn. */
if (osmo_sccp_tx_conn_resp(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id,
&scu_prim->u.connect.called_addr, NULL, 0)) {
LOGPFSML(fi, LOGL_ERROR, "Cannot send SCCP CONN RESP\n");
goto refuse;
}
/* Make sure the conn FSM will osmo_sccp_tx_disconn() on term */
conn->sccp.state = SUBSCR_SCCP_ST_CONNECTED;
switch (bssmap_type) {
case BSS_MAP_MSG_HANDOVER_RQST:
/* Inter-BSC MT Handover Request, another BSS is handovering to us. */
handover_start_inter_bsc_in(conn, msg);
return;
case BSS_MAP_MSG_PERFORM_LOCATION_RQST:
/* Location Services: MSC asks for location of an IDLE subscriber */
conn_fsm_state_chg(ST_ACTIVE);
lcs_loc_req_start(conn, msg);
return;
default:
break;
OSMO_ASSERT(false);
}
LOGPFSML(fi, LOGL_NOTICE, "No support for N-CONNECT: %s: %s\n",
gsm0808_bssap_name(bs->type), gsm0808_bssmap_name(bssmap_type));
refuse:
osmo_sccp_tx_disconn(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id,
&scu_prim->u.connect.called_addr, 0);
@ -404,6 +426,14 @@ static void gscon_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *dat
case GSCON_EV_TX_SCCP:
gscon_sigtran_send(conn, (struct msgb *)data);
break;
case GSCON_EV_LCS_LOC_REQ_END:
/* On the A-interface, there is nothing to do. If there still is an lchan, the conn should stay open. If
* not, it is up to the MSC to send a Clear Command.
* On the Lb-interface, tear down the SCCP connection. */
lb_close_conn(conn);
break;
default:
OSMO_ASSERT(false);
}
@ -628,7 +658,8 @@ static const struct osmo_fsm_state gscon_fsm_states[] = {
[ST_ACTIVE] = {
.name = "ACTIVE",
.in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_ASSIGNMENT_START) |
S(GSCON_EV_HANDOVER_START),
S(GSCON_EV_HANDOVER_START)
| S(GSCON_EV_LCS_LOC_REQ_END),
.out_state_mask = S(ST_CLEARING) | S(ST_ASSIGNMENT) |
S(ST_HANDOVER),
.action = gscon_fsm_active,
@ -656,6 +687,9 @@ void gscon_change_primary_lchan(struct gsm_subscriber_connection *conn, struct g
/* On release, do not receive release events that look like the primary lchan is gone. */
struct gsm_lchan *old_lchan = conn->lchan;
if (old_lchan == new_lchan)
return;
conn->lchan = new_lchan;
conn->lchan->conn = conn;
@ -738,7 +772,8 @@ void gscon_forget_lchan(struct gsm_subscriber_connection *conn, struct gsm_lchan
if ((conn->fi && conn->fi->state != ST_CLEARING)
&& !conn->lchan
&& !conn->ho.new_lchan
&& !conn->assignment.new_lchan)
&& !conn->assignment.new_lchan
&& !conn->lcs.loc_req)
gscon_bssmap_clear(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
}
@ -777,6 +812,9 @@ static void gscon_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *d
if (conn->ho.fi)
osmo_fsm_inst_dispatch(conn->ho.fi, HO_EV_CONN_RELEASING, NULL);
if (conn->lcs.loc_req)
osmo_fsm_inst_dispatch(conn->lcs.loc_req->fi, LCS_LOC_REQ_EV_CONN_CLEAR, NULL);
OSMO_ASSERT(data);
ccd = data;
if (conn->lchan)
@ -846,6 +884,8 @@ static void gscon_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cau
lchan_forget_conn(conn->assignment.new_lchan);
lchan_forget_conn(conn->ho.new_lchan);
lb_close_conn(conn);
if (conn->sccp.state != SUBSCR_SCCP_ST_NONE) {
LOGPFSML(fi, LOGL_DEBUG, "Disconnecting SCCP\n");
struct bsc_msc_data *msc = conn->sccp.msc;

View File

@ -1027,7 +1027,6 @@ int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id)
* MSC */
dispatch_dtap(lchan->conn, link_id, msg);
} else {
/* fwd via bsc_api to send COMPLETE L3 INFO to MSC */
return bsc_compl_l3(lchan, msg, 0);
}

View File

@ -30,6 +30,9 @@
#include <osmocom/bsc/gsm_04_08_rr.h>
#include <osmocom/bsc/a_reset.h>
#include <osmocom/bsc/lcs_ta_req.h>
#include <osmocom/bsc/lcs_loc_req.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/mncc.h>
@ -495,6 +498,10 @@ int bsc_compl_l3(struct gsm_lchan *lchan, struct msgb *msg, uint16_t chosen_chan
parse_powercap(conn, msg);
/* If a BSSLAP TA Request from the SMLC is waiting for a TA value, we have one now. */
if (conn->lcs.loc_req && conn->lcs.loc_req->ta_req)
osmo_fsm_inst_dispatch(conn->lcs.loc_req->ta_req->fi, LCS_TA_REQ_EV_GOT_TA, NULL);
/* If the Paging was issued only by OsmoBSC for LCS, don't bother to establish Layer 3 to the MSC. */
if (paged_from_msc && !(paging_reasons & BSC_PAGING_FROM_CN)) {
LOG_COMPL_L3(pdisc, mtype, LOGL_DEBUG,

View File

@ -45,6 +45,7 @@
#include <osmocom/bsc/timeslot_fsm.h>
#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/bsc_msc_data.h>
void *tall_bsc_ctx = NULL;

View File

@ -44,6 +44,7 @@
#include <osmocom/bsc/codec_pref.h>
#include <osmocom/bsc/gsm_08_08.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/lcs_loc_req.h>
#define LOG_FMT_BTS "bts %u lac-ci %u-%u arfcn-bsic %d-%d"
#define LOG_ARGS_BTS(bts) \
@ -938,6 +939,10 @@ void handover_end(struct gsm_subscriber_connection *conn, enum handover_result r
if (ho->new_lchan && result == HO_RESULT_OK) {
gscon_change_primary_lchan(conn, conn->ho.new_lchan);
ho->new_lchan = NULL;
/* If a Perform Location Request (LCS) is busy, inform the SMLC that there is a new lchan */
if (conn->lcs.loc_req)
osmo_fsm_inst_dispatch(conn->lcs.loc_req->fi, LCS_LOC_REQ_EV_HANDOVER_PERFORMED, NULL);
}
osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_HANDOVER_END, &result);

662
src/osmo-bsc/lb.c Normal file
View File

@ -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,
};

581
src/osmo-bsc/lcs_loc_req.c Normal file
View File

@ -0,0 +1,581 @@
/* Handle LCS BSSMAP-LE Perform Location Request */
/*
* (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/lcs_loc_req.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#include <osmocom/bsc/bsc_subscriber.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/lb.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/tdef.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/gad.h>
#include <osmocom/gsm/bsslap.h>
#include <osmocom/gsm/bssmap_le.h>
#include <osmocom/gsm/gsm0808_lcs.h>
#include <osmocom/bsc/lcs_ta_req.h>
#include <osmocom/bsc/paging.h>
#include <osmocom/bsc/bts_trx.h>
#include <osmocom/bsc/bts.h>
enum lcs_loc_req_fsm_state {
LCS_LOC_REQ_ST_INIT,
LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE,
LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING,
LCS_LOC_REQ_ST_GOT_LOCATION_RESPONSE,
LCS_LOC_REQ_ST_FAILED,
};
static const struct value_string lcs_loc_req_fsm_event_names[] = {
OSMO_VALUE_STRING(LCS_LOC_REQ_EV_RX_LB_PERFORM_LOCATION_RESPONSE),
OSMO_VALUE_STRING(LCS_LOC_REQ_EV_RX_A_PERFORM_LOCATION_ABORT),
OSMO_VALUE_STRING(LCS_LOC_REQ_EV_TA_REQ_START),
OSMO_VALUE_STRING(LCS_LOC_REQ_EV_TA_REQ_END),
OSMO_VALUE_STRING(LCS_LOC_REQ_EV_HANDOVER_PERFORMED),
OSMO_VALUE_STRING(LCS_LOC_REQ_EV_CONN_CLEAR),
{}
};
static struct osmo_fsm lcs_loc_req_fsm;
static const struct osmo_tdef_state_timeout lcs_loc_req_fsm_timeouts[32] = {
[LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE] = { .T = -11 },
};
/* Transition to a state, using the T timer defined in lcs_loc_req_fsm_timeouts.
* The actual timeout value is in turn obtained from network->T_defs.
* Assumes local variable fi exists. */
#define lcs_loc_req_fsm_state_chg(FI, STATE) \
osmo_tdef_fsm_inst_state_chg(FI, STATE, \
lcs_loc_req_fsm_timeouts, \
(bsc_gsmnet)->T_defs, \
5)
#define lcs_loc_req_fail(cause, fmt, args...) do { \
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR, "Perform Location Request failed in state %s: " fmt "\n", \
lcs_loc_req ? osmo_fsm_inst_state_name(lcs_loc_req->fi) : "NULL", ## args); \
lcs_loc_req->lcs_cause = (struct lcs_cause_ie){ \
.present = true, \
.cause_val = cause, \
}; \
lcs_loc_req_fsm_state_chg(lcs_loc_req->fi, LCS_LOC_REQ_ST_FAILED); \
} while(0)
static struct lcs_loc_req *lcs_loc_req_alloc(struct osmo_fsm_inst *parent_fi, uint32_t parent_event_term)
{
struct lcs_loc_req *lcs_loc_req;
struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc_child(&lcs_loc_req_fsm, parent_fi, parent_event_term);
OSMO_ASSERT(fi);
lcs_loc_req = talloc(fi, struct lcs_loc_req);
OSMO_ASSERT(lcs_loc_req);
fi->priv = lcs_loc_req;
*lcs_loc_req = (struct lcs_loc_req){
.fi = fi,
};
return lcs_loc_req;
}
static bool parse_bssmap_perf_loc_req(struct lcs_loc_req *lcs_loc_req, struct msgb *msg)
{
struct tlv_parsed tp_arr[1];
struct tlv_parsed *tp = &tp_arr[0];
struct tlv_p_entry *e;
int payload_length;
#define PARSE_ERR(ERRMSG) do { \
lcs_loc_req_fail(LCS_CAUSE_PROTOCOL_ERROR, "rx BSSMAP Perform Location Request: " ERRMSG); \
return false; \
} while(0)
payload_length = msg->tail - msg->l4h;
if (tlv_parse2(tp_arr, 1, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0) <= 0)
PARSE_ERR("Failed to parse IEs");
if (!(e = TLVP_GET(tp, GSM0808_IE_LOCATION_TYPE)))
PARSE_ERR("Missing Location Type IE");
if (osmo_bssmap_le_ie_dec_location_type(&lcs_loc_req->req.location_type, -1, -1, NULL, NULL, e->val, e->len))
PARSE_ERR("Failed to parse Location Type IE");
if ((e = TLVP_GET(tp, GSM0808_IE_CELL_IDENTIFIER))) {
if (gsm0808_dec_cell_id(&lcs_loc_req->req.cell_id, e->val, e->len) <= 0)
PARSE_ERR("Failed to parse Cell Identifier IE");
lcs_loc_req->req.cell_id_present = true;
}
if ((e = TLVP_GET(tp, GSM0808_IE_IMSI))) {
if (osmo_mobile_identity_decode(&lcs_loc_req->req.imsi, e->val, e->len, false)
|| lcs_loc_req->req.imsi.type != GSM_MI_TYPE_IMSI)
PARSE_ERR("Failed to parse IMSI IE");
}
if ((e = TLVP_GET(tp, GSM0808_IE_IMEI))) {
if (osmo_mobile_identity_decode(&lcs_loc_req->req.imei, e->val, e->len, false)
|| lcs_loc_req->req.imei.type != GSM_MI_TYPE_IMEI)
PARSE_ERR("Failed to parse IMEI IE");
}
// FIXME LCS QoS IE is mandatory for requesting the location
/* A lot of IEs remain ignored... */
return true;
#undef PARSE_ERR
}
void lcs_loc_req_start(struct gsm_subscriber_connection *conn, struct msgb *loc_req_msg)
{
struct lcs_loc_req *lcs_loc_req;
if (conn->lcs.loc_req) {
LOG_LCS_LOC_REQ(conn, LOGL_ERROR,
"Ignoring Perform Location Request, another request is still pending\n");
return;
}
lcs_loc_req = lcs_loc_req_alloc(conn->fi, GSCON_EV_LCS_LOC_REQ_END);
lcs_loc_req->conn = conn;
conn->lcs.loc_req = lcs_loc_req;
if (!parse_bssmap_perf_loc_req(lcs_loc_req, loc_req_msg))
return;
if (!conn->bsub) {
if (lcs_loc_req->req.imsi.type != GSM_MI_TYPE_IMSI) {
lcs_loc_req_fail(LCS_CAUSE_DATA_MISSING_IN_REQ,
"tx Perform Location Request: Missing identity:"
" No IMSI included in request, and also no active subscriber");
return;
}
conn->bsub = bsc_subscr_find_or_create_by_mi(bsc_gsmnet->bsc_subscribers, &lcs_loc_req->req.imsi,
BSUB_USE_CONN);
if (!conn->bsub) {
lcs_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE,
"tx Perform Location Request: Cannot assign subscriber");
return;
}
}
/* state change to start the timeout */
lcs_loc_req_fsm_state_chg(lcs_loc_req->fi, LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE);
}
static int handle_bssmap_le_conn_oriented_info(struct lcs_loc_req *lcs_loc_req, const struct bssmap_le_pdu *bssmap_le)
{
switch (bssmap_le->conn_oriented_info.apdu.msg_type) {
case BSSLAP_MSGT_TA_REQUEST:
rate_ctr_inc(&bsc_gsmnet->smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_TA_REQUEST]);
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_DEBUG, "rx BSSLAP TA Request\n");
/* The TA Request message contains only the message type. */
return lcs_ta_req_start(lcs_loc_req);
default:
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR, "rx BSSLAP APDU with unsupported message type %d\n",
bssmap_le->conn_oriented_info.apdu.msg_type);
return -ENOTSUP;
};
}
int lcs_loc_req_rx_bssmap_le(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
struct lcs_loc_req *lcs_loc_req = conn->lcs.loc_req;
struct bssap_le_pdu bssap_le;
struct osmo_bssap_le_err *err;
struct rate_ctr *ctr = bsc_gsmnet->smlc->ctrs->ctr;
if (!lcs_loc_req) {
LOGPFSMSL(conn->fi, DLCS, LOGL_ERROR,
"Rx BSSMAP-LE message, but no Location Request is ongoing\n");
return -EINVAL;
}
if (osmo_bssap_le_dec(&bssap_le, &err, msg, msg)) {
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR, "Rx BSSAP-LE message with error: %s\n", err->logmsg);
rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG]);
return -EINVAL;
}
if (bssap_le.discr != BSSAP_LE_MSG_DISCR_BSSMAP_LE) {
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR, "Rx BSSAP-LE: discr %d not implemented\n", bssap_le.discr);
rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG]);
return -ENOTSUP;
}
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_DEBUG, "Rx %s\n", osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le));
switch (bssap_le.bssmap_le.msg_type) {
case BSSMAP_LE_MSGT_PERFORM_LOC_RESP:
if (bssap_le.bssmap_le.perform_loc_resp.location_estimate_present)
rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS]);
else
rate_ctr_inc(&ctr[SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE]);
return osmo_fsm_inst_dispatch(lcs_loc_req->fi, LCS_LOC_REQ_EV_RX_LB_PERFORM_LOCATION_RESPONSE,
&bssap_le.bssmap_le);
case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO:
return handle_bssmap_le_conn_oriented_info(lcs_loc_req, &bssap_le.bssmap_le);
default:
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR, "Rx BSSMAP-LE from SMLC with unsupported message type: %s\n",
osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le));
return -ENOTSUP;
}
}
void lcs_loc_req_reset(struct gsm_subscriber_connection *conn)
{
struct lcs_loc_req *lcs_loc_req = conn->lcs.loc_req;
if (!lcs_loc_req)
return;
lcs_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Aborting Location Request due to RESET on Lb");
}
static int lcs_loc_req_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
struct lcs_loc_req *lcs_loc_req = fi->priv;
lcs_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Timeout");
return 1;
}
static int lcs_loc_req_send(struct lcs_loc_req *lcs_loc_req, const struct bssap_le_pdu *bssap_le)
{
int rc = lb_send(lcs_loc_req->conn, bssap_le);
if (rc)
lcs_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE,
"Failed to send %s", osmo_bssap_le_pdu_to_str_c(OTC_SELECT, bssap_le));
return rc;
}
static void lcs_loc_req_wait_loc_resp_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct lcs_loc_req *lcs_loc_req = fi->priv;
struct bssap_le_pdu plr;
struct gsm_lchan *lchan;
if (prev_state == LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING) {
/* LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING should halt the FSM timeout. As soon as the TA Request is
* served, re-entering LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE, but of course there is then no need to
* send a second BSSMAP-LE Perform Location Request to the SMLC. */
return;
}
if (!lcs_loc_req->req.cell_id_present) {
lcs_loc_req_fail(LCS_CAUSE_PROTOCOL_ERROR,
"Cannot encode BSSMAP-LE Perform Location Request,"
" because mandatory Cell Identity is not known");
return;
}
plr = (struct bssap_le_pdu){
.discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
.bssmap_le = {
.msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_REQ,
.perform_loc_req = {
.location_type = lcs_loc_req->req.location_type,
.cell_id = lcs_loc_req->req.cell_id,
.imsi = lcs_loc_req->req.imsi,
.imei = lcs_loc_req->req.imei,
},
},
};
/* If we already have an active lchan, send the known TA directly to the SMLC */
lchan = lcs_loc_req->conn->lchan;
if (lchan) {
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_DEBUG,
"Active lchan present, including BSSLAP APDU with TA Layer 3\n");
plr.bssmap_le.perform_loc_req.apdu_present = true;
plr.bssmap_le.perform_loc_req.apdu = (struct bsslap_pdu){
.msg_type = BSSLAP_MSGT_TA_LAYER3,
.ta_layer3 = {
.ta = lchan->rqd_ta,
},
};
} else {
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_DEBUG,
"No active lchan, not including BSSLAP APDU\n");
}
/* Establish Lb connection to SMLC and send the BSSMAP-LE Perform Location Request */
lcs_loc_req_send(lcs_loc_req, &plr);
}
static void lcs_loc_req_bssmap_le_abort(struct lcs_loc_req *lcs_loc_req)
{
struct bssap_le_pdu pla = {
.discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
.bssmap_le = {
.msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_ABORT,
.perform_loc_abort = {
.present = true,
.cause_val = LCS_CAUSE_REQUEST_ABORTED,
},
},
};
lcs_loc_req_send(lcs_loc_req, &pla);
}
/* After a handover, send the new lchan information to the SMLC via a BSSLAP Reset message.
* See 3GPP TS 48.071 4.2.6 Reset. */
static void lcs_loc_req_handover_performed(struct lcs_loc_req *lcs_loc_req)
{
struct gsm_lchan *lchan = lcs_loc_req->conn->lchan;
struct bssap_le_pdu bsslap = {
.discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
.bssmap_le = {
.msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,
},
};
struct bsslap_pdu *apdu = &bsslap.bssmap_le.conn_oriented_info.apdu;
if (!lchan) {
/* The handover was out of this BSS. Abort the location procedure. */
*apdu = (struct bsslap_pdu){
.msg_type = BSSLAP_MSGT_ABORT,
.abort = BSSLAP_CAUSE_INTER_BSS_HO,
};
} else {
*apdu = (struct bsslap_pdu){
.msg_type = BSSLAP_MSGT_RESET,
.reset = {
.cell_id = lchan->ts->trx->bts->cell_identity,
.ta = lchan->rqd_ta,
.cause = BSSLAP_CAUSE_INTRA_BSS_HO,
},
};
gsm48_lchan2chan_desc(&apdu->reset.chan_desc, lchan);
}
lcs_loc_req_send(lcs_loc_req, &bsslap);
}
static void lcs_loc_req_wait_loc_resp_and_ta_req_ongoing_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct lcs_loc_req *lcs_loc_req = fi->priv;
const struct bssmap_le_pdu *bssmap_le;
switch (event) {
case LCS_LOC_REQ_EV_RX_LB_PERFORM_LOCATION_RESPONSE:
bssmap_le = data;
OSMO_ASSERT(bssmap_le->msg_type == BSSMAP_LE_MSGT_PERFORM_LOC_RESP);
lcs_loc_req->resp = bssmap_le->perform_loc_resp;
lcs_loc_req->resp_present = true;
lcs_loc_req_fsm_state_chg(fi, LCS_LOC_REQ_ST_GOT_LOCATION_RESPONSE);
break;
case LCS_LOC_REQ_EV_TA_REQ_START:
if (fi->state != LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING)
lcs_loc_req_fsm_state_chg(fi, LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING);
break;
case LCS_LOC_REQ_EV_TA_REQ_END:
if (fi->state != LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE)
lcs_loc_req_fsm_state_chg(fi, LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE);
break;
case LCS_LOC_REQ_EV_HANDOVER_PERFORMED:
lcs_loc_req_handover_performed(lcs_loc_req);
break;
case LCS_LOC_REQ_EV_RX_A_PERFORM_LOCATION_ABORT:
case LCS_LOC_REQ_EV_CONN_CLEAR:
if (lcs_loc_req->ta_req)
osmo_fsm_inst_dispatch(lcs_loc_req->ta_req->fi, LCS_TA_REQ_EV_ABORT, NULL);
lcs_loc_req_bssmap_le_abort(lcs_loc_req);
osmo_fsm_inst_term(lcs_loc_req->fi, OSMO_FSM_TERM_REGULAR, NULL);
break;
default:
OSMO_ASSERT(false);
}
}
static void lcs_loc_req_got_loc_resp_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct lcs_loc_req *lcs_loc_req = fi->priv;
struct msgb *msg;
int rc;
struct gsm0808_perform_location_response plr = {
.location_estimate_present = lcs_loc_req->resp.location_estimate_present,
.location_estimate = lcs_loc_req->resp.location_estimate,
.lcs_cause = lcs_loc_req->resp.lcs_cause,
};
if (plr.location_estimate_present) {
struct osmo_gad gad;
struct osmo_gad_err *err;
if (osmo_gad_dec(&gad, &err, OTC_SELECT, &plr.location_estimate))
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
"Perform Location Response contains Location Estimate with error: %s\n",
err->logmsg);
else
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_INFO,
"Perform Location Response contains Location Estimate: %s\n",
osmo_gad_to_str_c(OTC_SELECT, &gad));
}
if (plr.lcs_cause.present) {
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
"Perform Location Response contains error cause: %d\n",
plr.lcs_cause.cause_val);
}
msg = gsm0808_create_perform_location_response(&plr);
if (!msg) {
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
"Failed to encode BSSMAP Perform Location Response (A-interface)\n");
} else {
rc = gscon_sigtran_send(lcs_loc_req->conn, msg);
if (rc < 0)
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
"Failed to send Perform Location Response (A-interface)\n");
else
rate_ctr_inc(&lcs_loc_req->conn->sccp.msc->msc_ctrs->ctr[
plr.location_estimate_present ? MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS
: MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE]);
}
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
}
static void lcs_loc_req_failed_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct lcs_loc_req *lcs_loc_req = fi->priv;
struct msgb *msg;
int rc;
struct bssap_le_pdu pla = {
.discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
.bssmap_le = {
.msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_ABORT,
.perform_loc_abort = lcs_loc_req->lcs_cause,
},
};
struct gsm0808_perform_location_response plr = {
.lcs_cause = lcs_loc_req->lcs_cause,
};
/* If we're paging this subscriber for LCS, stop paging. */
paging_request_cancel(lcs_loc_req->conn->bsub, BSC_PAGING_FOR_LCS);
/* Send Perform Location Abort to SMLC, only if we got started on the Lb */
if (lcs_loc_req->conn->lcs.lb.state == SUBSCR_SCCP_ST_CONNECTED)
lcs_loc_req_send(lcs_loc_req, &pla);
/* Send Perform Location Result with failure cause to MSC */
msg = gsm0808_create_perform_location_response(&plr);
if (!msg) {
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
"Failed to encode BSSMAP Perform Location Response (A-interface)\n");
} else {
rc = gscon_sigtran_send(lcs_loc_req->conn, msg);
if (rc < 0)
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR,
"Failed to send BSSMAP Perform Location Response (A-interface)\n");
else
rate_ctr_inc(&lcs_loc_req->conn->sccp.msc->msc_ctrs->ctr[MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE]);
}
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
}
void lcs_loc_req_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct lcs_loc_req *lcs_loc_req = fi->priv;
if (lcs_loc_req->conn && lcs_loc_req->conn->lcs.loc_req == lcs_loc_req)
lcs_loc_req->conn->lcs.loc_req = NULL;
/* FSM termination will dispatch GSCON_EV_LCS_LOC_REQ_END to the conn FSM */
}
#define S(x) (1 << (x))
static const struct osmo_fsm_state lcs_loc_req_fsm_states[] = {
[LCS_LOC_REQ_ST_INIT] = {
.name = "INIT",
.out_state_mask = 0
| S(LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE)
| S(LCS_LOC_REQ_ST_FAILED)
,
},
[LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE] = {
.name = "WAIT_LOCATION_RESPONSE",
.in_event_mask = 0
| S(LCS_LOC_REQ_EV_RX_LB_PERFORM_LOCATION_RESPONSE)
| S(LCS_LOC_REQ_EV_RX_A_PERFORM_LOCATION_ABORT)
| S(LCS_LOC_REQ_EV_TA_REQ_START)
| S(LCS_LOC_REQ_EV_TA_REQ_END)
| S(LCS_LOC_REQ_EV_HANDOVER_PERFORMED)
| S(LCS_LOC_REQ_EV_CONN_CLEAR)
,
.out_state_mask = 0
| S(LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING)
| S(LCS_LOC_REQ_ST_GOT_LOCATION_RESPONSE)
| S(LCS_LOC_REQ_ST_FAILED)
,
.onenter = lcs_loc_req_wait_loc_resp_onenter,
.action = lcs_loc_req_wait_loc_resp_and_ta_req_ongoing_action,
},
[LCS_LOC_REQ_ST_BSSLAP_TA_REQ_ONGOING] = {
.name = "BSSLAP_TA_REQ_ONGOING",
.in_event_mask = 0
| S(LCS_LOC_REQ_EV_RX_LB_PERFORM_LOCATION_RESPONSE)
| S(LCS_LOC_REQ_EV_RX_A_PERFORM_LOCATION_ABORT)
| S(LCS_LOC_REQ_EV_TA_REQ_END)
| S(LCS_LOC_REQ_EV_HANDOVER_PERFORMED)
| S(LCS_LOC_REQ_EV_CONN_CLEAR)
,
.out_state_mask = 0
| S(LCS_LOC_REQ_ST_WAIT_LOCATION_RESPONSE)
| S(LCS_LOC_REQ_ST_GOT_LOCATION_RESPONSE)
| S(LCS_LOC_REQ_ST_FAILED)
,
.action = lcs_loc_req_wait_loc_resp_and_ta_req_ongoing_action,
},
[LCS_LOC_REQ_ST_GOT_LOCATION_RESPONSE] = {
.name = "GOT_LOCATION_RESPONSE",
.onenter = lcs_loc_req_got_loc_resp_onenter,
},
[LCS_LOC_REQ_ST_FAILED] = {
.name = "FAILED",
.onenter = lcs_loc_req_failed_onenter,
},
};
static struct osmo_fsm lcs_loc_req_fsm = {
.name = "lcs_loc_req",
.states = lcs_loc_req_fsm_states,
.num_states = ARRAY_SIZE(lcs_loc_req_fsm_states),
.log_subsys = DLCS,
.event_names = lcs_loc_req_fsm_event_names,
.timer_cb = lcs_loc_req_fsm_timer_cb,
.cleanup = lcs_loc_req_fsm_cleanup,
};
static __attribute__((constructor)) void lcs_loc_req_fsm_register(void)
{
OSMO_ASSERT(osmo_fsm_register(&lcs_loc_req_fsm) == 0);
}

305
src/osmo-bsc/lcs_ta_req.c Normal file
View File

@ -0,0 +1,305 @@
/* Handle LCS BSSLAP TA Request */
/*
* (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/lcs_ta_req.h>
#include <osmocom/bsc/lcs_loc_req.h>
#include <osmocom/bsc/lb.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/paging.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/tdef.h>
#include <osmocom/gsm/bsslap.h>
enum lcs_ta_req_fsm_state {
LCS_TA_REQ_ST_INIT,
LCS_TA_REQ_ST_WAIT_TA,
LCS_TA_REQ_ST_GOT_TA,
LCS_TA_REQ_ST_FAILED,
};
static const struct value_string lcs_ta_req_fsm_event_names[] = {
OSMO_VALUE_STRING(LCS_TA_REQ_EV_GOT_TA),
OSMO_VALUE_STRING(LCS_TA_REQ_EV_ABORT),
{}
};
static const struct osmo_tdef_state_timeout lcs_ta_req_fsm_timeouts[32] = {
[LCS_TA_REQ_ST_WAIT_TA] = { .T = -12 },
};
/* Transition to a state, using the T timer defined in lcs_ta_req_fsm_timeouts.
* The actual timeout value is in turn obtained from network->T_defs.
* Assumes local variable fi exists. */
#define lcs_ta_req_fsm_state_chg(FI, STATE) \
osmo_tdef_fsm_inst_state_chg(FI, STATE, \
lcs_ta_req_fsm_timeouts, \
(bsc_gsmnet)->T_defs, \
5)
#define lcs_ta_req_fail(cause, fmt, args...) do { \
LOG_LCS_TA_REQ(lcs_ta_req, LOGL_ERROR, "BSSLAP TA Request failed in state %s: " fmt "\n", \
lcs_ta_req ? osmo_fsm_inst_state_name(lcs_ta_req->fi) : "NULL", ## args); \
lcs_ta_req->failure_cause = cause; \
lcs_ta_req_fsm_state_chg(lcs_ta_req->fi, LCS_TA_REQ_ST_FAILED); \
} while(0)
static struct osmo_fsm lcs_ta_req_fsm;
static struct lcs_ta_req *lcs_ta_req_alloc(struct osmo_fsm_inst *parent_fi, uint32_t parent_event_term)
{
struct lcs_ta_req *lcs_ta_req;
struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc_child(&lcs_ta_req_fsm, parent_fi, parent_event_term);
OSMO_ASSERT(fi);
lcs_ta_req = talloc(fi, struct lcs_ta_req);
OSMO_ASSERT(lcs_ta_req);
fi->priv = lcs_ta_req;
*lcs_ta_req = (struct lcs_ta_req){
.fi = fi,
};
return lcs_ta_req;
}
int lcs_ta_req_start(struct lcs_loc_req *lcs_loc_req)
{
struct lcs_ta_req *lcs_ta_req;
if (lcs_loc_req->ta_req) {
LOG_LCS_TA_REQ(lcs_loc_req->ta_req, LOGL_ERROR,
"Cannot start anoter TA Request FSM, this TA Request is still active\n");
return -ENOTSUP;
}
lcs_ta_req = lcs_ta_req_alloc(lcs_loc_req->fi, LCS_LOC_REQ_EV_TA_REQ_END);
if (!lcs_ta_req) {
LOG_LCS_LOC_REQ(lcs_loc_req, LOGL_ERROR, "Cannot allocate TA Request FSM");
return -ENOSPC;
}
lcs_ta_req->loc_req = lcs_loc_req;
lcs_loc_req->ta_req = lcs_ta_req;
return lcs_ta_req_fsm_state_chg(lcs_ta_req->fi, LCS_TA_REQ_ST_WAIT_TA);
}
static int lcs_ta_req_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
struct lcs_ta_req *lcs_ta_req = fi->priv;
lcs_ta_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Timeout");
return 1;
}
void lcs_ta_req_wait_ta_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct lcs_ta_req *lcs_ta_req = fi->priv;
struct lcs_loc_req *loc_req = lcs_ta_req->loc_req;
struct gsm_lchan *lchan;
struct bsc_paging_params paging;
if (osmo_fsm_inst_dispatch(loc_req->fi, LCS_LOC_REQ_EV_TA_REQ_START, lcs_ta_req)) {
lcs_ta_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Failed to dispatch LCS_LOC_REQ_EV_TA_REQ_START");
return;
}
paging = (struct bsc_paging_params){
.reason = BSC_PAGING_FOR_LCS,
.msc = loc_req->conn->sccp.msc,
.bsub = loc_req->conn->bsub,
.tmsi = GSM_RESERVED_TMSI,
.imsi = loc_req->req.imsi,
.chan_needed = RSL_CHANNEED_ANY,
};
if (paging.bsub)
bsc_subscr_get(paging.bsub, BSUB_USE_PAGING_START);
/* Do we already have an active lchan with knowledge of TA? */
lchan = loc_req->conn->lchan;
if (lchan) {
lcs_ta_req_fsm_state_chg(fi, LCS_TA_REQ_ST_GOT_TA);
return;
}
/* No lchan yet, need to start Paging */
if (loc_req->req.imsi.type != GSM_MI_TYPE_IMSI) {
lcs_ta_req_fail(LCS_CAUSE_PROTOCOL_ERROR,
"No IMSI in BSSMAP Location Request and no active lchan, cannot start Paging");
return;
}
if (!loc_req->req.cell_id_present) {
LOG_LCS_TA_REQ(lcs_ta_req, LOGL_DEBUG,
"No Cell Identity in BSSMAP Location Request, paging entire BSS\n");
paging.cil = (struct gsm0808_cell_id_list2){
.id_discr = CELL_IDENT_BSS,
};
} else {
paging.cil = (struct gsm0808_cell_id_list2){
.id_discr = loc_req->req.cell_id.id_discr,
.id_list = { loc_req->req.cell_id.id },
.id_list_len = 1,
};
}
bsc_paging_start(&paging);
}
static void lcs_ta_req_wait_ta_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case LCS_TA_REQ_EV_GOT_TA:
lcs_ta_req_fsm_state_chg(fi, LCS_TA_REQ_ST_GOT_TA);
break;
case LCS_TA_REQ_EV_ABORT:
lcs_ta_req_fsm_state_chg(fi, LCS_TA_REQ_ST_FAILED);
break;
default:
OSMO_ASSERT(false);
}
}
static int lcs_ta_req_send(struct lcs_ta_req *lcs_ta_req, const struct bssap_le_pdu *bssap_le)
{
int rc = lb_send(lcs_ta_req->loc_req->conn, bssap_le);
if (rc)
lcs_ta_req_fail(LCS_CAUSE_SYSTEM_FAILURE,
"Failed to send %s", osmo_bssap_le_pdu_to_str_c(OTC_SELECT, bssap_le));
return rc;
}
void lcs_ta_req_got_ta_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct lcs_ta_req *lcs_ta_req = fi->priv;
struct bssap_le_pdu bsslap_ta_resp;
struct gsm_lchan *lchan = lcs_ta_req->loc_req->conn->lchan;
if (!lchan) {
lcs_ta_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Internal error: no lchan");
return;
}
bsslap_ta_resp = (struct bssap_le_pdu) {
.discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
.bssmap_le = {
.msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,
.conn_oriented_info = {
.apdu = {
.msg_type = BSSLAP_MSGT_TA_RESPONSE,
.ta_response = {
.cell_id = lchan->ts->trx->bts->cell_identity,
.ta = lchan->rqd_ta,
},
},
},
},
};
lcs_ta_req_send(lcs_ta_req, &bsslap_ta_resp);
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
}
void lcs_ta_req_failed_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct lcs_ta_req *lcs_ta_req = fi->priv;
struct bssap_le_pdu bsslap_abort;
bsslap_abort = (struct bssap_le_pdu) {
.discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
.bssmap_le = {
.msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,
.conn_oriented_info = {
.apdu = {
.msg_type = BSSLAP_MSGT_ABORT,
.abort = BSSLAP_CAUSE_OTHER_RADIO_EVT_FAIL,
},
},
},
};
lcs_ta_req_send(lcs_ta_req, &bsslap_abort);
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
}
void lcs_ta_req_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct lcs_ta_req *lcs_ta_req = fi->priv;
if (lcs_ta_req->loc_req->ta_req == lcs_ta_req)
lcs_ta_req->loc_req->ta_req = NULL;
/* FSM termination will dispatch LCS_LOC_REQ_EV_TA_REQ_END to the lcs_loc_req FSM */
}
#define S(x) (1 << (x))
static const struct osmo_fsm_state lcs_ta_req_fsm_states[] = {
[LCS_TA_REQ_ST_INIT] = {
.name = "init",
.out_state_mask = 0
| S(LCS_TA_REQ_ST_WAIT_TA)
| S(LCS_TA_REQ_ST_GOT_TA)
,
},
[LCS_TA_REQ_ST_WAIT_TA] = {
.name = "wait_ta",
.in_event_mask = 0
| S(LCS_TA_REQ_EV_GOT_TA)
| S(LCS_TA_REQ_EV_ABORT)
,
.out_state_mask = 0
| S(LCS_TA_REQ_ST_GOT_TA)
| S(LCS_TA_REQ_ST_FAILED)
,
.onenter = lcs_ta_req_wait_ta_onenter,
.action = lcs_ta_req_wait_ta_action,
},
[LCS_TA_REQ_ST_GOT_TA] = {
.name = "got_ta",
.in_event_mask = 0
,
.out_state_mask = 0
,
.onenter = lcs_ta_req_got_ta_onenter,
},
[LCS_TA_REQ_ST_FAILED] = {
.name = "failed",
.onenter = lcs_ta_req_failed_onenter,
},
};
static struct osmo_fsm lcs_ta_req_fsm = {
.name = "lcs_ta_req",
.states = lcs_ta_req_fsm_states,
.num_states = ARRAY_SIZE(lcs_ta_req_fsm_states),
.log_subsys = DLCS,
.event_names = lcs_ta_req_fsm_event_names,
.timer_cb = lcs_ta_req_fsm_timer_cb,
.cleanup = lcs_ta_req_fsm_cleanup,
};
static __attribute__((constructor)) void lcs_ta_req_fsm_register(void)
{
OSMO_ASSERT(osmo_fsm_register(&lcs_ta_req_fsm) == 0);
}

View File

@ -52,6 +52,7 @@ static struct osmo_tdef gsm_network_T_defs[] = {
{ .T=-8, .default_val=5, .desc="Timeout for RSL IPA MDCX ACK after sending RSL IPA MDCX" },
{ .T=-9, .default_val=5, .desc="Timeout for availability of MGW endpoint" },
{ .T=-10, .default_val=5, .desc="Timeout for fully configured MGW endpoint" },
{ .T=-11, .default_val=5, .desc="Timeout for Perform Location Response from SMLC" },
{ .T=-3111, .default_val=4, .desc="Wait time after lchan was released in error (should be T3111 + 2s)" },
{ .T=-3210, .default_val=20, .desc="After L3 Complete, wait for MSC to confirm" },
{}

View File

@ -44,6 +44,7 @@
#include <osmocom/core/fsm.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/bsc/lcs_loc_req.h>
#define IP_V4_ADDR_LEN 4
@ -1176,6 +1177,21 @@ static int bssmap_rcvmsg_dt1(struct gsm_subscriber_connection *conn,
rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_COMMON_ID]);
ret = bssmap_handle_common_id(conn, msg, length);
break;
case BSS_MAP_MSG_PERFORM_LOCATION_RQST:
rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_PERFORM_LOCATION_REQUEST]);
lcs_loc_req_start(conn, msg);
ret = 0;
break;
case BSS_MAP_MSG_PERFORM_LOCATION_ABORT:
rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_PERFORM_LOCATION_ABORT]);
if (conn->lcs.loc_req) {
ret = osmo_fsm_inst_dispatch(conn->lcs.loc_req->fi, LCS_LOC_REQ_EV_RX_A_PERFORM_LOCATION_ABORT,
msg);
} else {
LOGP(DMSC, LOGL_ERROR, "Rx BSSMAP Perform Location Abort without ongoing Location Request\n");
ret = 0;
}
break;
default:
rate_ctr_inc(&ctrs[MSC_CTR_BSSMAP_RX_DT1_UNKNOWN]);
LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n",

View File

@ -38,6 +38,7 @@
#include <osmocom/bsc/assignment_fsm.h>
#include <osmocom/bsc/handover_fsm.h>
#include <osmocom/bsc/smscb.h>
#include <osmocom/bsc/lb.h>
#include <osmocom/ctrl/control_cmd.h>
#include <osmocom/ctrl/control_if.h>
@ -788,8 +789,12 @@ static const struct log_info_cat osmo_bsc_categories[] = {
.name = "DCBS",
.description = "Cell Broadcast System",
.enabled = 1, .loglevel = LOGL_NOTICE,
}
},
[DLCS] = {
.name = "DLCS",
.description = "Location Services",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
static int filter_fn(const struct log_context *ctx, struct log_target *tar)
@ -947,6 +952,7 @@ int main(int argc, char **argv)
handover_decision_1_init();
hodec2_init(bsc_gsmnet);
bsc_cbc_link_restart();
lb_init();
signal(SIGINT, &signal_handler);
signal(SIGTERM, &signal_handler);

View File

@ -62,6 +62,8 @@ static const struct rate_ctr_desc msc_ctr_description[] = {
[MSC_CTR_BSSMAP_RX_DT1_UNKNOWN] = {"bssmap:rx:dt1:err_unknown", "Number of received BSSMAP unknown DT1 messages"},
[MSC_CTR_BSSMAP_RX_DT1_DTAP] = {"bssmap:rx:dt1:dtap:good", "Number of received BSSMAP DTAP messages"},
[MSC_CTR_BSSMAP_RX_DT1_DTAP_ERROR] = {"bssmap:rx:dt1:dtap:error", "Number of received BSSMAP DTAP messages with errors"},
[MSC_CTR_BSSMAP_RX_DT1_PERFORM_LOCATION_REQUEST] = {"bssmap:rx:dt1:location:request", "Number of received BSSMAP Perform Location Request messages"},
[MSC_CTR_BSSMAP_RX_DT1_PERFORM_LOCATION_ABORT] = {"bssmap:tx:dt1:location:abort", "Number of received BSSMAP Perform Location Abort messages"},
/* Tx message counters (per message type)
*
@ -102,6 +104,10 @@ static const struct rate_ctr_desc msc_ctr_description[] = {
[MSC_CTR_BSSMAP_TX_DT1_HANDOVER_COMPLETE] = {"bssmap:tx:dt1:handover:complete", "Number of transmitted BSSMAP DT1 HANDOVER COMPLETE messages"},
[MSC_CTR_BSSMAP_TX_DT1_HANDOVER_FAILURE] = {"bssmap:tx:dt1:handover:failure", "Number of transmitted BSSMAP DT1 HANDOVER FAILURE messages"},
[MSC_CTR_BSSMAP_TX_DT1_DTAP] = {"bssmap:tx:dt1:dtap", "Number of transmitted BSSMAP DT1 DTAP messages"},
[MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_SUCCESS] = {"bssmap:tx:dt1:location:response_success",
"Number of transmitted BSSMAP Perform Location Response messages containing a location estimate"},
[MSC_CTR_BSSMAP_TX_DT1_PERFORM_LOCATION_RESPONSE_FAILURE] = {"bssmap:tx:dt1:location:response_failure",
"Number of transmitted BSSMAP Perform Location Response messages containing a failure cause"},
/* Indicators for MSC pool usage */
[MSC_CTR_MSCPOOL_SUBSCR_NEW] = {

View File

@ -79,9 +79,9 @@ static void page_ms(struct gsm_paging_request *request)
log_set_context(LOG_CTX_BSC_SUBSCR, request->bsub);
LOG_BTS(bts, DPAG, LOGL_INFO, "Going to send paging commands: imsi: %s tmsi: "
"0x%08x for ch. type %d (attempt %d)\n", request->bsub->imsi,
request->bsub->tmsi, request->chan_type, request->attempts);
LOG_BTS(bts, DPAG, LOGL_INFO, "Going to send paging commands: %s"
" for ch. type %d (attempt %d)\n", bsc_subscr_name(request->bsub),
request->chan_type, request->attempts);
if (request->bsub->tmsi == GSM_RESERVED_TMSI) {
mi = (struct osmo_mobile_identity){
@ -457,6 +457,30 @@ int paging_request_stop(struct bsc_msc_data **msc_p, enum bsc_paging_reason *rea
return count;
}
/* Remove all paging requests, for specific reasons only. */
int paging_request_cancel(struct bsc_subscr *bsub, enum bsc_paging_reason reasons)
{
struct gsm_bts *bts;
int count = 0;
llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) {
struct gsm_paging_request *req, *req2;
paging_init_if_needed(bts);
llist_for_each_entry_safe(req, req2, &bts->paging.pending_requests, entry) {
if (req->bsub != bsub)
continue;
if (!(req->reason & reasons))
continue;
LOG_BTS(bts, DPAG, LOGL_DEBUG, "Cancel paging %s\n", bsc_subscr_name(bsub));
paging_remove_request(&bts->paging, req);
count++;
}
}
return count;
}
/*! Update the BTS paging buffer slots on given BTS */
void paging_update_buffer_space(struct gsm_bts *bts, uint16_t free_slots)
{

View File

@ -30,6 +30,7 @@
#include <osmocom/bsc/osmo_bsc.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/gsm/gad.h>
#include <osmocom/core/application.h>
#include <osmocom/core/backtrace.h>
#include <osmocom/core/talloc.h>

View File

@ -93,6 +93,10 @@ handover_test_LDADD = \
$(top_builddir)/src/osmo-bsc/smscb.o \
$(top_builddir)/src/osmo-bsc/cbch_scheduler.o \
$(top_builddir)/src/osmo-bsc/cbsp_link.o \
$(top_builddir)/src/osmo-bsc/lcs_loc_req.o \
$(top_builddir)/src/osmo-bsc/lcs_ta_req.o \
$(top_builddir)/src/osmo-bsc/lb.o \
$(top_builddir)/src/osmo-bsc/bsc_sccp.o \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCTRL_LIBS) \

View File

@ -25,6 +25,7 @@ net: X7 = 5 s Timeout for RSL IPA CRCX ACK after sending RSL IPA CRCX (default:
net: X8 = 5 s Timeout for RSL IPA MDCX ACK after sending RSL IPA MDCX (default: 5 s)
net: X9 = 5 s Timeout for availability of MGW endpoint (default: 5 s)
net: X10 = 5 s Timeout for fully configured MGW endpoint (default: 5 s)
net: X11 = 5 s Timeout for Perform Location Response from SMLC (default: 5 s)
net: X3111 = 4 s Wait time after lchan was released in error (should be T3111 + 2s) (default: 4 s)
net: X3210 = 20 s After L3 Complete, wait for MSC to confirm (default: 20 s)
mgw: X2427 = 5 s timeout for MGCP response from MGW (default: 5 s)
@ -70,6 +71,7 @@ net: X7 = 5 s Timeout for RSL IPA CRCX ACK after sending RSL IPA CRCX (default:
net: X8 = 5 s Timeout for RSL IPA MDCX ACK after sending RSL IPA MDCX (default: 5 s)
net: X9 = 5 s Timeout for availability of MGW endpoint (default: 5 s)
net: X10 = 5 s Timeout for fully configured MGW endpoint (default: 5 s)
net: X11 = 5 s Timeout for Perform Location Response from SMLC (default: 5 s)
net: X3111 = 4 s Wait time after lchan was released in error (should be T3111 + 2s) (default: 4 s)
net: X3210 = 20 s After L3 Complete, wait for MSC to confirm (default: 20 s)
mgw: X2427 = 5 s timeout for MGCP response from MGW (default: 5 s)