initial working osmo-smlc implementation
The lower level Lb/SCCP interface conn handling is essentially a copy of OsmoMSC's A/SCCP infrastructure (OsmoMSC also connects to multiple BSCs). The smlc_subscr is mostly a copy of OsmoBSC's bsc_subscr. smlc_loc_req FSM is the SMLC side of OsmoBSC's new lcs_loc_req FSM. cell_locations configures geographic coordinates of individual cells. Change-Id: I917ba8fc51a1f1150be77ae01e12a7b16a853052
This commit is contained in:
parent
97362c57bc
commit
7299215d74
|
@ -212,6 +212,7 @@ AC_OUTPUT(
|
|||
src/osmo-smlc/Makefile
|
||||
tests/Makefile
|
||||
tests/atlocal
|
||||
tests/smlc_subscr/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
doc/manuals/Makefile
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
cells
|
||||
lac-ci 23 42 lat 12.34567 lon 34.56789
|
||||
cgi 262 42 17 5 lat 12.34765 lon 34.56987
|
|
@ -1,4 +1,12 @@
|
|||
noinst_HEADERS = \
|
||||
cell_locations.h \
|
||||
debug.h \
|
||||
lb_conn.h \
|
||||
lb_peer.h \
|
||||
sccp_lb_inst.h \
|
||||
smlc_data.h \
|
||||
smlc_loc_req.h \
|
||||
smlc_sigtran.h \
|
||||
smlc_subscr.h \
|
||||
smlc_vty.h \
|
||||
$(NULL)
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/* OsmoSMLC cell locations configuration */
|
||||
/*
|
||||
* (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Hofmeyr <neels@hofmeyr.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
struct osmo_gad;
|
||||
|
||||
struct cell_location {
|
||||
struct llist_head entry;
|
||||
|
||||
struct gsm0808_cell_id cell_id;
|
||||
|
||||
/*! latitude in micro degrees (degrees * 1e6) */
|
||||
int32_t lat;
|
||||
/*! longitude in micro degrees (degrees * 1e6) */
|
||||
int32_t lon;
|
||||
};
|
||||
|
||||
int cell_location_from_ta(struct osmo_gad *location_estimate,
|
||||
const struct gsm0808_cell_id *cell_id,
|
||||
uint8_t ta);
|
||||
|
||||
int cell_locations_vty_init();
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#define DEBUG
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
/* Debug Areas of the code */
|
||||
enum {
|
||||
DSMLC,
|
||||
DREF,
|
||||
DLB,
|
||||
DLCS,
|
||||
Debug_LastEntry,
|
||||
};
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
/* SMLC Lb connection implementation */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/smlc/smlc_subscr.h>
|
||||
|
||||
struct lb_peer;
|
||||
struct osmo_fsm_inst;
|
||||
struct msgb;
|
||||
struct bssmap_le_pdu;
|
||||
|
||||
#define LOG_LB_CONN_SL(CONN, CAT, LEVEL, file, line, FMT, args...) \
|
||||
LOGPSRC(CAT, LEVEL, file, line, "Lb-%d %s %s: " FMT, (CONN) ? (CONN)->sccp_conn_id : 0, \
|
||||
((CONN) && (CONN)->smlc_subscr) ? smlc_subscr_to_str_c(OTC_SELECT, (CONN)->smlc_subscr) : "no-subscr", \
|
||||
(CONN) ? osmo_use_count_to_str_c(OTC_SELECT, &(CONN)->use_count) : "-", \
|
||||
##args)
|
||||
|
||||
#define LOG_LB_CONN_S(CONN, CAT, LEVEL, FMT, args...) \
|
||||
LOG_LB_CONN_SL(CONN, CAT, LEVEL, NULL, 0, FMT, ##args)
|
||||
|
||||
#define LOG_LB_CONN(CONN, LEVEL, FMT, args...) \
|
||||
LOG_LB_CONN_S(CONN, DLB, LEVEL, FMT, ##args)
|
||||
|
||||
#define SMLC_SUBSCR_USE_LB_CONN "Lb-conn"
|
||||
|
||||
struct lb_conn {
|
||||
struct llist_head entry;
|
||||
struct osmo_use_count use_count;
|
||||
|
||||
struct lb_peer *lb_peer;
|
||||
uint32_t sccp_conn_id;
|
||||
|
||||
bool closing;
|
||||
|
||||
struct smlc_subscr *smlc_subscr;
|
||||
struct smlc_loc_req *smlc_loc_req;
|
||||
};
|
||||
|
||||
#define lb_conn_get(lb_conn, use) \
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&(lb_conn)->use_count, use, 1) == 0)
|
||||
#define lb_conn_put(lb_conn, use) \
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&(lb_conn)->use_count, use, -1) == 0)
|
||||
|
||||
struct lb_conn *lb_conn_create_incoming(struct lb_peer *lb_peer, uint32_t sccp_conn_id, const char *use_token);
|
||||
struct lb_conn *lb_conn_create_outgoing(struct lb_peer *lb_peer, const char *use_token);
|
||||
struct lb_conn *lb_conn_find_by_smlc_subscr(struct smlc_subscr *smlc_subscr, const char *use_token);
|
||||
|
||||
void lb_conn_msc_role_gone(struct lb_conn *lb_conn, struct osmo_fsm_inst *msc_role);
|
||||
void lb_conn_close(struct lb_conn *lb_conn);
|
||||
void lb_conn_discard(struct lb_conn *lb_conn);
|
||||
|
||||
int lb_conn_rx(struct lb_conn *lb_conn, struct msgb *msg, bool initial);
|
||||
int lb_conn_send_bssmap_le(struct lb_conn *lb_conn, const struct bssmap_le_pdu *bssmap_le);
|
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/gsm0808.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
#include <osmocom/smlc/debug.h>
|
||||
#include <osmocom/smlc/lb_conn.h>
|
||||
|
||||
struct vlr_subscr;
|
||||
struct lb_conn;
|
||||
struct neighbor_ident_entry;
|
||||
|
||||
#define LOG_LB_PEER_CAT(LB_PEER, subsys, loglevel, fmt, args ...) \
|
||||
LOGPFSMSL((LB_PEER)? (LB_PEER)->fi : NULL, subsys, loglevel, fmt, ## args)
|
||||
|
||||
#define LOG_LB_PEER(LB_PEER, loglevel, fmt, args ...) \
|
||||
LOG_LB_PEER_CAT(LB_PEER, DLB, loglevel, fmt, ## args)
|
||||
|
||||
struct lb_peer {
|
||||
struct llist_head entry;
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
struct sccp_lb_inst *sli;
|
||||
struct osmo_sccp_addr peer_addr;
|
||||
};
|
||||
|
||||
#define lb_peer_for_each_lb_conn(LB_CONN, LB_PEER) \
|
||||
llist_for_each_entry(LB_CONN, &(LB_PEER)->sli->lb_conns, entry) \
|
||||
if ((LB_CONN)->lb_peer == (LB_PEER))
|
||||
|
||||
#define lb_peer_for_each_lb_conn_safe(LB_CONN, LB_CONN_NEXT, LB_PEER) \
|
||||
llist_for_each_entry_safe(LB_CONN, LB_CONN_NEXT, &(LB_PEER)->sli->lb_conns, entry) \
|
||||
if ((LB_CONN)->lb_peer == (LB_PEER))
|
||||
|
||||
enum lb_peer_state {
|
||||
LB_PEER_ST_WAIT_RX_RESET = 0,
|
||||
LB_PEER_ST_WAIT_RX_RESET_ACK,
|
||||
LB_PEER_ST_READY,
|
||||
LB_PEER_ST_DISCARDING,
|
||||
};
|
||||
|
||||
enum lb_peer_event {
|
||||
LB_PEER_EV_MSG_UP_CL = 0,
|
||||
LB_PEER_EV_MSG_UP_CO_INITIAL,
|
||||
LB_PEER_EV_MSG_UP_CO,
|
||||
LB_PEER_EV_MSG_DOWN_CL,
|
||||
LB_PEER_EV_MSG_DOWN_CO_INITIAL,
|
||||
LB_PEER_EV_MSG_DOWN_CO,
|
||||
LB_PEER_EV_RX_RESET,
|
||||
LB_PEER_EV_RX_RESET_ACK,
|
||||
LB_PEER_EV_CONNECTION_SUCCESS,
|
||||
LB_PEER_EV_CONNECTION_TIMEOUT,
|
||||
};
|
||||
|
||||
struct lb_peer_ev_ctx {
|
||||
uint32_t conn_id;
|
||||
struct lb_conn *lb_conn;
|
||||
struct msgb *msg;
|
||||
};
|
||||
|
||||
struct lb_peer *lb_peer_find_or_create(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *peer_addr);
|
||||
struct lb_peer *lb_peer_find(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *peer_addr);
|
||||
|
||||
int lb_peer_up_l2(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *calling_addr, bool co, uint32_t conn_id,
|
||||
struct msgb *l2);
|
||||
void lb_peer_disconnect(struct sccp_lb_inst *sli, uint32_t conn_id);
|
|
@ -0,0 +1,63 @@
|
|||
/* Lb: BSSAP-LE/SCCP */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/gsm/gsm0808_utils.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
struct msgb;
|
||||
struct sccp_lb_inst;
|
||||
|
||||
#define LOG_SCCP_LB_CO(sli, peer_addr, conn_id, level, fmt, args...) \
|
||||
LOGP(DLB, level, "(Lb-%u%s%s) " fmt, \
|
||||
conn_id, peer_addr ? " from " : "", \
|
||||
peer_addr ? osmo_sccp_inst_addr_name((sli)->sccp, peer_addr) : "", \
|
||||
## args)
|
||||
|
||||
#define LOG_SCCP_LB_CL_CAT(sli, peer_addr, subsys, level, fmt, args...) \
|
||||
LOGP(subsys, level, "(Lb%s%s) " fmt, \
|
||||
peer_addr ? " from " : "", \
|
||||
peer_addr ? osmo_sccp_inst_addr_name((sli)->sccp, peer_addr) : "", \
|
||||
## args)
|
||||
|
||||
#define LOG_SCCP_LB_CL(sli, peer_addr, level, fmt, args...) \
|
||||
LOG_SCCP_LB_CL_CAT(sli, peer_addr, DLB, level, fmt, ##args)
|
||||
|
||||
#define LOG_SCCP_LB_CAT(sli, subsys, level, fmt, args...) \
|
||||
LOG_SCCP_LB_CL_CAT(sli, NULL, subsys, level, fmt, ##args)
|
||||
|
||||
#define LOG_SCCP_LB(sli, level, fmt, args...) \
|
||||
LOG_SCCP_LB_CL(sli, NULL, level, fmt, ##args)
|
||||
|
||||
enum reset_msg_type {
|
||||
SCCP_LB_MSG_NON_RESET = 0,
|
||||
SCCP_LB_MSG_RESET,
|
||||
SCCP_LB_MSG_RESET_ACK,
|
||||
};
|
||||
|
||||
struct sccp_lb_inst {
|
||||
struct osmo_sccp_instance *sccp;
|
||||
struct osmo_sccp_user *scu;
|
||||
struct osmo_sccp_addr local_sccp_addr;
|
||||
|
||||
struct llist_head lb_peers;
|
||||
struct llist_head lb_conns;
|
||||
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
struct sccp_lb_inst *sccp_lb_init(void *talloc_ctx, struct osmo_sccp_instance *sccp, enum osmo_sccp_ssn ssn,
|
||||
const char *sccp_user_name);
|
||||
int sccp_lb_inst_next_conn_id();
|
||||
|
||||
int sccp_lb_down_l2_co_initial(struct sccp_lb_inst *sli,
|
||||
const struct osmo_sccp_addr *called_addr,
|
||||
uint32_t conn_id, struct msgb *l2);
|
||||
int sccp_lb_down_l2_co(struct sccp_lb_inst *sli, uint32_t conn_id, struct msgb *l2);
|
||||
int sccp_lb_down_l2_cl(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *called_addr, struct msgb *l2);
|
||||
|
||||
int sccp_lb_disconnect(struct sccp_lb_inst *sli, uint32_t conn_id, uint32_t cause);
|
|
@ -8,25 +8,26 @@
|
|||
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
struct osmo_sccp_instance;
|
||||
struct sccp_lb_inst;
|
||||
|
||||
struct smlc_state {
|
||||
struct osmo_sccp_user *sccp_user;
|
||||
struct osmo_sccp_instance *sccp_inst;
|
||||
struct sccp_lb_inst *lb;
|
||||
|
||||
struct ctrl_handle *ctrl;
|
||||
|
||||
struct rate_ctr_group *ctrs;
|
||||
struct osmo_stat_item_group *statg;
|
||||
struct osmo_tdef *T_defs;
|
||||
|
||||
struct llist_head subscribers;
|
||||
struct llist_head cell_locations;
|
||||
};
|
||||
|
||||
extern struct smlc_state *g_smlc;
|
||||
struct smlc_state *smlc_state_alloc(void *ctx);
|
||||
|
||||
|
||||
enum {
|
||||
DSMLC,
|
||||
DLB, /* Lb interface */
|
||||
};
|
||||
|
||||
extern struct osmo_tdef g_smlc_tdefs[];
|
||||
|
||||
int smlc_ctrl_node_lookup(void *data, vector vline, int *node_type,
|
||||
void **node_data, int *i);
|
||||
|
@ -35,3 +36,25 @@ enum smlc_ctrl_node {
|
|||
CTRL_NODE_SMLC = _LAST_CTRL_NODE,
|
||||
_LAST_CTRL_NODE_SMLC
|
||||
};
|
||||
|
||||
enum {
|
||||
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_REQUEST,
|
||||
SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_TA_RESPONSE,
|
||||
SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_REJECT,
|
||||
SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_RESET,
|
||||
SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_ABORT,
|
||||
|
||||
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_RESPONSE,
|
||||
SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_TA_REQUEST,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/* 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>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/smlc/debug.h>
|
||||
#include <osmocom/gsm/bssmap_le.h>
|
||||
|
||||
#define LOG_SMLC_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 smlc_ta_req;
|
||||
struct lb_conn;
|
||||
struct msgb;
|
||||
|
||||
#define LB_CONN_USE_SMLC_LOC_REQ "smlc_loc_req"
|
||||
|
||||
enum smlc_loc_req_fsm_event {
|
||||
SMLC_LOC_REQ_EV_RX_TA_RESPONSE,
|
||||
SMLC_LOC_REQ_EV_RX_BSSLAP_RESET,
|
||||
SMLC_LOC_REQ_EV_RX_LE_PERFORM_LOCATION_ABORT,
|
||||
};
|
||||
|
||||
struct smlc_loc_req {
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
struct smlc_subscr *smlc_subscr;
|
||||
struct lb_conn *lb_conn;
|
||||
|
||||
struct bssmap_le_perform_loc_req req;
|
||||
|
||||
bool ta_present;
|
||||
uint8_t ta;
|
||||
|
||||
struct gsm0808_cell_id latest_cell_id;
|
||||
|
||||
struct lcs_cause_ie lcs_cause;
|
||||
};
|
||||
|
||||
int smlc_loc_req_rx_bssap_le(struct lb_conn *conn, const struct bssap_le_pdu *bssap_le);
|
|
@ -1,3 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
int smlc_sigtran_init(void);
|
||||
int smlc_sigtran_send(uint32_t sccp_conn_id, struct msgb *msg);
|
||||
int smlc_sigtran_send_udt(uint32_t sccp_conn_id, struct msgb *msg);
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/use_count.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
#include <osmocom/gsm/gsm0808.h>
|
||||
|
||||
struct smlc_subscr {
|
||||
struct llist_head entry;
|
||||
struct osmo_use_count use_count;
|
||||
|
||||
struct osmo_mobile_identity imsi;
|
||||
struct gsm0808_cell_id cell_id;
|
||||
|
||||
struct osmo_fsm_inst *loc_req;
|
||||
};
|
||||
|
||||
struct smlc_subscr *smlc_subscr_find_or_create(const struct osmo_mobile_identity *imsi, const char *use_token);
|
||||
struct smlc_subscr *smlc_subscr_find(const struct osmo_mobile_identity *imsi, const char *use_token);
|
||||
|
||||
int smlc_subscr_to_str_buf(char *buf, size_t buf_len, const struct smlc_subscr *smlc_subscr);
|
||||
char *smlc_subscr_to_str_c(void *ctx, const struct smlc_subscr *smlc_subscr);
|
||||
|
||||
struct smlc_subscr *smlc_subscr_find_or_create(const struct osmo_mobile_identity *imsi, const char *use_token);
|
||||
|
||||
#define smlc_subscr_get(smlc_subscr, use) \
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&(smlc_subscr)->use_count, use, 1) == 0)
|
||||
#define smlc_subscr_put(smlc_subscr, use) \
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&(smlc_subscr)->use_count, use, -1) == 0)
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <osmocom/vty/command.h>
|
||||
|
||||
enum smlc_vty_node {
|
||||
CELLS_NODE = _LAST_OSMOVTY_NODE + 1,
|
||||
};
|
|
@ -23,9 +23,15 @@ bin_PROGRAMS = \
|
|||
$(NULL)
|
||||
|
||||
osmo_smlc_SOURCES = \
|
||||
cell_locations.c \
|
||||
lb_conn.c \
|
||||
lb_peer.c \
|
||||
sccp_lb_inst.c \
|
||||
smlc_ctrl.c \
|
||||
smlc_data.c \
|
||||
smlc_loc_req.c \
|
||||
smlc_main.c \
|
||||
smlc_sigtran.c \
|
||||
smlc_subscr.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_smlc_LDADD = \
|
||||
|
|
|
@ -0,0 +1,318 @@
|
|||
/* OsmoSMLC cell locations configuration */
|
||||
/*
|
||||
* (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Hofmeyr <neels@hofmeyr.de>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/protocol/gsm_08_08.h>
|
||||
#include <osmocom/gsm/gsm0808_utils.h>
|
||||
#include <osmocom/gsm/gad.h>
|
||||
#include <osmocom/smlc/smlc_data.h>
|
||||
#include <osmocom/smlc/smlc_vty.h>
|
||||
#include <osmocom/smlc/cell_locations.h>
|
||||
|
||||
static uint32_t ta_to_m(uint8_t ta)
|
||||
{
|
||||
return ((uint32_t)ta) * 550;
|
||||
}
|
||||
|
||||
static struct cell_location *cell_location_find(const struct gsm0808_cell_id *cell_id)
|
||||
{
|
||||
struct cell_location *cell_location;
|
||||
llist_for_each_entry(cell_location, &g_smlc->cell_locations, entry) {
|
||||
if (gsm0808_cell_ids_match(&cell_location->cell_id, cell_id, true))
|
||||
return cell_location;
|
||||
}
|
||||
llist_for_each_entry(cell_location, &g_smlc->cell_locations, entry) {
|
||||
if (gsm0808_cell_ids_match(&cell_location->cell_id, cell_id, false))
|
||||
return cell_location;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int cell_location_from_ta(struct osmo_gad *location_estimate,
|
||||
const struct gsm0808_cell_id *cell_id,
|
||||
uint8_t ta)
|
||||
{
|
||||
const struct cell_location *cell;
|
||||
cell = cell_location_find(cell_id);
|
||||
if (!cell)
|
||||
return -ENOENT;
|
||||
|
||||
*location_estimate = (struct osmo_gad){
|
||||
.type = GAD_TYPE_ELL_POINT_UNC_CIRCLE,
|
||||
.ell_point_unc_circle = {
|
||||
.lat = cell->lat,
|
||||
.lon = cell->lon,
|
||||
.unc = osmo_gad_dec_unc(osmo_gad_enc_unc(ta_to_m(ta) * 1000)),
|
||||
},
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cell_location *cell_location_find_or_create(const struct gsm0808_cell_id *cell_id)
|
||||
{
|
||||
struct cell_location *cell_location = cell_location_find(cell_id);
|
||||
if (!cell_location) {
|
||||
cell_location = talloc_zero(g_smlc, struct cell_location);
|
||||
OSMO_ASSERT(cell_location);
|
||||
cell_location->cell_id = *cell_id;
|
||||
llist_add_tail(&cell_location->entry, &g_smlc->cell_locations);
|
||||
}
|
||||
return cell_location;
|
||||
|
||||
}
|
||||
|
||||
static const struct cell_location *cell_location_set(const struct gsm0808_cell_id *cell_id, int32_t lat, int32_t lon)
|
||||
{
|
||||
struct cell_location *cell_location = cell_location_find_or_create(cell_id);
|
||||
cell_location->lat = lat;
|
||||
cell_location->lon = lon;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cell_location_remove(const struct gsm0808_cell_id *cell_id)
|
||||
{
|
||||
struct cell_location *cell_location = cell_location_find(cell_id);
|
||||
if (!cell_location)
|
||||
return -ENOENT;
|
||||
llist_del(&cell_location->entry);
|
||||
talloc_free(cell_location);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define LAC_CI_PARAMS "lac-ci <0-65535> <0-65535>"
|
||||
#define LAC_CI_DOC "Cell location by LAC and CI\n" "LAC\n" "CI\n"
|
||||
|
||||
#define CGI_PARAMS "cgi <0-999> <0-999> <0-65535> <0-65535>"
|
||||
#define CGI_DOC "Cell location by Cell-Global ID\n" "MCC\n" "MNC\n" "LAC\n" "CI\n"
|
||||
|
||||
#define LAT_LON_PARAMS "lat LATITUDE lon LONGITUDE"
|
||||
#define LAT_LON_DOC "Global latitute coordinate\n" "Latitude floating-point number, -90.0 (S) to 90.0 (N)\n" \
|
||||
"Global longitude coordinate\n" "Longitude as floating-point number, -180.0 (W) to 180.0 (E)\n"
|
||||
|
||||
static int vty_parse_lac_ci(struct vty *vty, struct gsm0808_cell_id *dst, const char **argv)
|
||||
{
|
||||
*dst = (struct gsm0808_cell_id){
|
||||
.id_discr = CELL_IDENT_LAC_AND_CI,
|
||||
.id.lac_and_ci = {
|
||||
.lac = atoi(argv[0]),
|
||||
.ci = atoi(argv[1]),
|
||||
},
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vty_parse_cgi(struct vty *vty, struct gsm0808_cell_id *dst, const char **argv)
|
||||
{
|
||||
*dst = (struct gsm0808_cell_id){
|
||||
.id_discr = CELL_IDENT_WHOLE_GLOBAL,
|
||||
};
|
||||
struct osmo_cell_global_id *cgi = &dst->id.global;
|
||||
const char *mcc = argv[0];
|
||||
const char *mnc = argv[1];
|
||||
const char *lac = argv[2];
|
||||
const char *ci = argv[3];
|
||||
|
||||
if (osmo_mcc_from_str(mcc, &cgi->lai.plmn.mcc)) {
|
||||
vty_out(vty, "%% Error decoding MCC: %s%s", mcc, VTY_NEWLINE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (osmo_mnc_from_str(mnc, &cgi->lai.plmn.mnc, &cgi->lai.plmn.mnc_3_digits)) {
|
||||
vty_out(vty, "%% Error decoding MNC: %s%s", mnc, VTY_NEWLINE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cgi->lai.lac = atoi(lac);
|
||||
cgi->cell_identity = atoi(ci);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vty_parse_location(struct vty *vty, const struct gsm0808_cell_id *cell_id, const char **argv)
|
||||
{
|
||||
const char *lat_str = argv[0];
|
||||
const char *lon_str = argv[1];
|
||||
int64_t val;
|
||||
int32_t lat, lon;
|
||||
|
||||
if (osmo_float_str_to_int(&val, lat_str, 6)
|
||||
|| val < -90000000 || val > 90000000) {
|
||||
vty_out(vty, "%% Invalid latitude: '%s'%s", lat_str, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
lat = val;
|
||||
|
||||
if (osmo_float_str_to_int(&val, lon_str, 6)
|
||||
|| val < -180000000 || val > 180000000) {
|
||||
vty_out(vty, "%% Invalid longitude: '%s'%s", lon_str, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
lon = val;
|
||||
|
||||
if (cell_location_set(cell_id, lat, lon)) {
|
||||
vty_out(vty, "%% Failed to add cell location%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_cells, cfg_cells_cmd,
|
||||
"cells",
|
||||
"Configure cell locations\n")
|
||||
{
|
||||
vty->node = CELLS_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_cells_lac_ci, cfg_cells_lac_ci_cmd,
|
||||
LAC_CI_PARAMS " " LAT_LON_PARAMS,
|
||||
LAC_CI_DOC LAT_LON_DOC)
|
||||
{
|
||||
struct gsm0808_cell_id cell_id;
|
||||
|
||||
if (vty_parse_lac_ci(vty, &cell_id, argv))
|
||||
return CMD_WARNING;
|
||||
|
||||
return vty_parse_location(vty, &cell_id, argv + 2);
|
||||
}
|
||||
|
||||
DEFUN(cfg_cells_no_lac_ci, cfg_cells_no_lac_ci_cmd,
|
||||
"no " LAC_CI_PARAMS,
|
||||
NO_STR "Remove " LAC_CI_DOC)
|
||||
{
|
||||
struct gsm0808_cell_id cell_id;
|
||||
|
||||
if (vty_parse_lac_ci(vty, &cell_id, argv))
|
||||
return CMD_WARNING;
|
||||
if (cell_location_remove(&cell_id)) {
|
||||
vty_out(vty, "%% cannot remove, no such entry%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_cells_cgi, cfg_cells_cgi_cmd,
|
||||
CGI_PARAMS " " LAT_LON_PARAMS,
|
||||
CGI_DOC LAT_LON_DOC)
|
||||
{
|
||||
struct gsm0808_cell_id cell_id;
|
||||
|
||||
if (vty_parse_cgi(vty, &cell_id, argv))
|
||||
return CMD_WARNING;
|
||||
|
||||
return vty_parse_location(vty, &cell_id, argv + 4);
|
||||
}
|
||||
|
||||
DEFUN(cfg_cells_no_cgi, cfg_cells_no_cgi_cmd,
|
||||
"no " CGI_PARAMS,
|
||||
NO_STR "Remove " CGI_DOC)
|
||||
{
|
||||
struct gsm0808_cell_id cell_id;
|
||||
|
||||
if (vty_parse_cgi(vty, &cell_id, argv))
|
||||
return CMD_WARNING;
|
||||
if (cell_location_remove(&cell_id)) {
|
||||
vty_out(vty, "%% cannot remove, no such entry%s", VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* The above are omnidirectional cells. If we add configuration sector antennae, it would add arguments to the above,
|
||||
* something like this:
|
||||
* cgi 001 01 23 42 lat 23.23 lon 42.42 arc 270 30
|
||||
*/
|
||||
|
||||
struct cmd_node cells_node = {
|
||||
CELLS_NODE,
|
||||
"%s(config-cells)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
static int config_write_cells(struct vty *vty)
|
||||
{
|
||||
struct cell_location *cell;
|
||||
const struct osmo_cell_global_id *cgi;
|
||||
|
||||
if (llist_empty(&g_smlc->cell_locations))
|
||||
return 0;
|
||||
|
||||
vty_out(vty, "cells%s", VTY_NEWLINE);
|
||||
|
||||
llist_for_each_entry(cell, &g_smlc->cell_locations, entry) {
|
||||
switch (cell->cell_id.id_discr) {
|
||||
case CELL_IDENT_LAC_AND_CI:
|
||||
vty_out(vty, " lac-ci %u %u", cell->cell_id.id.lac_and_ci.lac, cell->cell_id.id.lac_and_ci.ci);
|
||||
break;
|
||||
case CELL_IDENT_WHOLE_GLOBAL:
|
||||
cgi = &cell->cell_id.id.global;
|
||||
vty_out(vty, " cgi %s %s %u %u",
|
||||
osmo_mcc_name(cgi->lai.plmn.mcc),
|
||||
osmo_mnc_name(cgi->lai.plmn.mnc, cgi->lai.plmn.mnc_3_digits),
|
||||
cgi->lai.lac, cgi->cell_identity);
|
||||
break;
|
||||
default:
|
||||
vty_out(vty, " %% [unsupported cell id type: %d]",
|
||||
cell->cell_id.id_discr);
|
||||
break;
|
||||
}
|
||||
|
||||
vty_out(vty, " lat %s lon %s%s",
|
||||
osmo_int_to_float_str_c(OTC_SELECT, cell->lat, 6),
|
||||
osmo_int_to_float_str_c(OTC_SELECT, cell->lon, 6),
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFUN(ve_show_cells, ve_show_cells_cmd,
|
||||
"show cells",
|
||||
SHOW_STR "Show configured cell locations\n")
|
||||
{
|
||||
if (llist_empty(&g_smlc->cell_locations)) {
|
||||
vty_out(vty, "%% No cell locations are configured%s", VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
config_write_cells(vty);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int cell_locations_vty_init()
|
||||
{
|
||||
install_element(CONFIG_NODE, &cfg_cells_cmd);
|
||||
install_node(&cells_node, config_write_cells);
|
||||
install_element(CELLS_NODE, &cfg_cells_lac_ci_cmd);
|
||||
install_element(CELLS_NODE, &cfg_cells_no_lac_ci_cmd);
|
||||
install_element(CELLS_NODE, &cfg_cells_cgi_cmd);
|
||||
install_element(CELLS_NODE, &cfg_cells_no_cgi_cmd);
|
||||
install_element_ve(&ve_show_cells_cmd);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/* SMLC Lb connection implementation */
|
||||
|
||||
/*
|
||||
* (C) 2020 by sysmocom s.m.f.c. <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Hofmeyr
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/signal.h>
|
||||
#include <osmocom/gsm/bssmap_le.h>
|
||||
|
||||
#include <osmocom/smlc/debug.h>
|
||||
#include <osmocom/smlc/smlc_data.h>
|
||||
#include <osmocom/smlc/sccp_lb_inst.h>
|
||||
#include <osmocom/smlc/lb_peer.h>
|
||||
#include <osmocom/smlc/lb_conn.h>
|
||||
#include <osmocom/smlc/smlc_loc_req.h>
|
||||
|
||||
static int lb_conn_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
|
||||
{
|
||||
struct lb_conn *lb_conn = e->use_count->talloc_object;
|
||||
int32_t total;
|
||||
int level;
|
||||
|
||||
if (!e->use)
|
||||
return -EINVAL;
|
||||
|
||||
total = osmo_use_count_total(&lb_conn->use_count);
|
||||
|
||||
if (total == 0
|
||||
|| (total == 1 && old_use_count == 0 && e->count == 1))
|
||||
level = LOGL_INFO;
|
||||
else
|
||||
level = LOGL_DEBUG;
|
||||
|
||||
LOG_LB_CONN_SL(lb_conn, DREF, level, file, line, "%s %s: now used by %s\n",
|
||||
(e->count - old_use_count) > 0? "+" : "-", e->use,
|
||||
osmo_use_count_to_str_c(OTC_SELECT, &lb_conn->use_count));
|
||||
|
||||
if (e->count < 0)
|
||||
return -ERANGE;
|
||||
|
||||
if (total == 0)
|
||||
lb_conn_close(lb_conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct lb_conn *lb_conn_alloc(struct lb_peer *lb_peer, uint32_t sccp_conn_id, const char *use_token)
|
||||
{
|
||||
struct lb_conn *lb_conn;
|
||||
|
||||
lb_conn = talloc(lb_peer, struct lb_conn);
|
||||
OSMO_ASSERT(lb_conn);
|
||||
|
||||
*lb_conn = (struct lb_conn){
|
||||
.lb_peer = lb_peer,
|
||||
.sccp_conn_id = sccp_conn_id,
|
||||
.use_count = {
|
||||
.talloc_object = lb_conn,
|
||||
.use_cb = lb_conn_use_cb,
|
||||
},
|
||||
};
|
||||
|
||||
llist_add(&lb_conn->entry, &lb_peer->sli->lb_conns);
|
||||
lb_conn_get(lb_conn, use_token);
|
||||
return lb_conn;
|
||||
}
|
||||
|
||||
struct lb_conn *lb_conn_create_incoming(struct lb_peer *lb_peer, uint32_t sccp_conn_id, const char *use_token)
|
||||
{
|
||||
LOG_LB_PEER(lb_peer, LOGL_DEBUG, "Incoming lb_conn id: %u\n", sccp_conn_id);
|
||||
return lb_conn_alloc(lb_peer, sccp_conn_id, use_token);
|
||||
}
|
||||
|
||||
struct lb_conn *lb_conn_create_outgoing(struct lb_peer *lb_peer, const char *use_token)
|
||||
{
|
||||
int new_conn_id = sccp_lb_inst_next_conn_id();
|
||||
if (new_conn_id < 0)
|
||||
return NULL;
|
||||
LOG_LB_PEER(lb_peer, LOGL_DEBUG, "Outgoing lb_conn id: %u\n", new_conn_id);
|
||||
return lb_conn_alloc(lb_peer, new_conn_id, use_token);
|
||||
}
|
||||
|
||||
struct lb_conn *lb_conn_find_by_smlc_subscr(struct smlc_subscr *smlc_subscr, const char *use_token)
|
||||
{
|
||||
struct lb_conn *lb_conn;
|
||||
llist_for_each_entry(lb_conn, &g_smlc->lb->lb_conns, entry) {
|
||||
if (lb_conn->smlc_subscr == smlc_subscr) {
|
||||
lb_conn_get(lb_conn, use_token);
|
||||
return lb_conn;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int lb_conn_down_l2_co(struct lb_conn *lb_conn, struct msgb *l3, bool initial)
|
||||
{
|
||||
struct lb_peer_ev_ctx co = {
|
||||
.conn_id = lb_conn->sccp_conn_id,
|
||||
.lb_conn = lb_conn,
|
||||
.msg = l3,
|
||||
};
|
||||
if (!lb_conn->lb_peer)
|
||||
return -EIO;
|
||||
return osmo_fsm_inst_dispatch(lb_conn->lb_peer->fi,
|
||||
initial ? LB_PEER_EV_MSG_DOWN_CO_INITIAL : LB_PEER_EV_MSG_DOWN_CO,
|
||||
&co);
|
||||
}
|
||||
|
||||
int lb_conn_rx(struct lb_conn *lb_conn, struct msgb *msg, bool initial)
|
||||
{
|
||||
struct bssap_le_pdu bssap_le;
|
||||
struct osmo_bssap_le_err *err;
|
||||
if (osmo_bssap_le_dec(&bssap_le, &err, msg, msg)) {
|
||||
LOG_LB_CONN(lb_conn, LOGL_ERROR, "Rx BSSAP-LE with error: %s\n", err->logmsg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return smlc_loc_req_rx_bssap_le(lb_conn, &bssap_le);
|
||||
}
|
||||
|
||||
int lb_conn_send_bssmap_le(struct lb_conn *lb_conn, const struct bssmap_le_pdu *bssmap_le)
|
||||
{
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
struct bssap_le_pdu bssap_le = {
|
||||
.discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
|
||||
.bssmap_le = *bssmap_le,
|
||||
};
|
||||
|
||||
msg = osmo_bssap_le_enc(&bssap_le);
|
||||
if (!msg) {
|
||||
LOG_LB_CONN(lb_conn, LOGL_ERROR, "Unable to encode %s\n",
|
||||
osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le));
|
||||
return -EINVAL;
|
||||
}
|
||||
rc = lb_conn_down_l2_co(lb_conn, msg, false);
|
||||
msgb_free(msg);
|
||||
if (rc)
|
||||
LOG_LB_CONN(lb_conn, LOGL_ERROR, "Unable to send %s\n",
|
||||
osmo_bssap_le_pdu_to_str_c(OTC_SELECT, &bssap_le));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Regularly close the lb_conn */
|
||||
void lb_conn_close(struct lb_conn *lb_conn)
|
||||
{
|
||||
if (!lb_conn)
|
||||
return;
|
||||
if (lb_conn->closing)
|
||||
return;
|
||||
lb_conn->closing = true;
|
||||
LOG_LB_PEER(lb_conn->lb_peer, LOGL_DEBUG, "Closing lb_conn\n");
|
||||
|
||||
if (lb_conn->lb_peer) {
|
||||
/* Todo: pass a useful SCCP cause? */
|
||||
sccp_lb_disconnect(lb_conn->lb_peer->sli, lb_conn->sccp_conn_id, 0);
|
||||
lb_conn->lb_peer = NULL;
|
||||
}
|
||||
|
||||
if (lb_conn->smlc_loc_req)
|
||||
osmo_fsm_inst_term(lb_conn->smlc_loc_req->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
|
||||
if (lb_conn->smlc_subscr)
|
||||
smlc_subscr_put(lb_conn->smlc_subscr, SMLC_SUBSCR_USE_LB_CONN);
|
||||
|
||||
llist_del(&lb_conn->entry);
|
||||
talloc_free(lb_conn);
|
||||
}
|
||||
|
||||
/* Same as lb_conn_close() but without sending any SCCP messages (e.g. after RESET) */
|
||||
void lb_conn_discard(struct lb_conn *lb_conn)
|
||||
{
|
||||
if (!lb_conn)
|
||||
return;
|
||||
/* Make sure to drop dead and don't dispatch things like DISCONNECT requests on SCCP. */
|
||||
lb_conn->lb_peer = NULL;
|
||||
lb_conn_close(lb_conn);
|
||||
}
|
|
@ -0,0 +1,495 @@
|
|||
/*
|
||||
* (C) 2019 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* Author: Neels Hofmeyr
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/gsm/bssmap_le.h>
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#include <osmocom/smlc/smlc_data.h>
|
||||
#include <osmocom/smlc/sccp_lb_inst.h>
|
||||
#include <osmocom/smlc/lb_peer.h>
|
||||
|
||||
static struct osmo_fsm lb_peer_fsm;
|
||||
|
||||
static __attribute__((constructor)) void lb_peer_init()
|
||||
{
|
||||
OSMO_ASSERT( osmo_fsm_register(&lb_peer_fsm) == 0);
|
||||
}
|
||||
|
||||
static struct lb_peer *lb_peer_alloc(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *peer_addr)
|
||||
{
|
||||
struct lb_peer *lbp;
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
fi = osmo_fsm_inst_alloc(&lb_peer_fsm, sli, NULL, LOGL_DEBUG, NULL);
|
||||
OSMO_ASSERT(fi);
|
||||
|
||||
osmo_fsm_inst_update_id(fi, osmo_sccp_addr_to_id_c(OTC_SELECT, osmo_sccp_get_ss7(sli->sccp), peer_addr));
|
||||
|
||||
lbp = talloc_zero(fi, struct lb_peer);
|
||||
OSMO_ASSERT(lbp);
|
||||
*lbp = (struct lb_peer){
|
||||
.fi = fi,
|
||||
.sli = sli,
|
||||
.peer_addr = *peer_addr,
|
||||
};
|
||||
fi->priv = lbp;
|
||||
|
||||
llist_add(&lbp->entry, &sli->lb_peers);
|
||||
|
||||
return lbp;
|
||||
}
|
||||
|
||||
struct lb_peer *lb_peer_find_or_create(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *peer_addr)
|
||||
{
|
||||
struct lb_peer *lbp = lb_peer_find(sli, peer_addr);
|
||||
if (lbp)
|
||||
return lbp;
|
||||
return lb_peer_alloc(sli, peer_addr);
|
||||
}
|
||||
|
||||
struct lb_peer *lb_peer_find(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *peer_addr)
|
||||
{
|
||||
struct lb_peer *lbp;
|
||||
llist_for_each_entry(lbp, &sli->lb_peers, entry) {
|
||||
if (osmo_sccp_addr_ri_cmp(peer_addr, &lbp->peer_addr))
|
||||
continue;
|
||||
return lbp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct osmo_tdef_state_timeout lb_peer_fsm_timeouts[32] = {
|
||||
[LB_PEER_ST_WAIT_RX_RESET_ACK] = { .T = -13 },
|
||||
[LB_PEER_ST_DISCARDING] = { .T = -14 },
|
||||
};
|
||||
|
||||
#define lb_peer_state_chg(LB_PEER, NEXT_STATE) \
|
||||
osmo_tdef_fsm_inst_state_chg((LB_PEER)->fi, NEXT_STATE, lb_peer_fsm_timeouts, g_smlc_tdefs, 5)
|
||||
|
||||
void lb_peer_discard_all_conns(struct lb_peer *lbp)
|
||||
{
|
||||
struct lb_conn *lb_conn, *next;
|
||||
|
||||
lb_peer_for_each_lb_conn_safe(lb_conn, next, lbp) {
|
||||
lb_conn_discard(lb_conn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Drop all SCCP connections for this lb_peer, respond with RESET ACKNOWLEDGE and move to READY state. */
|
||||
static void lb_peer_rx_reset(struct lb_peer *lbp, struct msgb *msg)
|
||||
{
|
||||
struct msgb *resp;
|
||||
struct bssap_le_pdu reset_ack = {
|
||||
.discr = BSSAP_LE_MSG_DISCR_BSSMAP_LE,
|
||||
.bssmap_le = {
|
||||
.msg_type = BSSMAP_LE_MSGT_RESET_ACK,
|
||||
},
|
||||
};
|
||||
|
||||
lb_peer_discard_all_conns(lbp);
|
||||
|
||||
resp = osmo_bssap_le_enc(&reset_ack);
|
||||
if (!resp) {
|
||||
LOG_LB_PEER(lbp, LOGL_ERROR, "Failed to compose RESET ACKNOWLEDGE message\n");
|
||||
lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sccp_lb_down_l2_cl(lbp->sli, &lbp->peer_addr, resp)) {
|
||||
LOG_LB_PEER(lbp, LOGL_ERROR, "Failed to send RESET ACKNOWLEDGE message\n");
|
||||
lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_LB_PEER(lbp, LOGL_INFO, "Sent RESET ACKNOWLEDGE\n");
|
||||
|
||||
/* sccp_lb_down_l2_cl() doesn't free msgb */
|
||||
msgb_free(resp);
|
||||
|
||||
lb_peer_state_chg(lbp, LB_PEER_ST_READY);
|
||||
}
|
||||
|
||||
static void lb_peer_rx_reset_ack(struct lb_peer *lbp, struct msgb* msg)
|
||||
{
|
||||
lb_peer_state_chg(lbp, LB_PEER_ST_READY);
|
||||
}
|
||||
|
||||
void lb_peer_reset(struct lb_peer *lbp)
|
||||
{
|
||||
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,
|
||||
},
|
||||
};
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET_ACK);
|
||||
lb_peer_discard_all_conns(lbp);
|
||||
|
||||
msg = osmo_bssap_le_enc(&reset);
|
||||
if (!msg) {
|
||||
LOG_LB_PEER(lbp, LOGL_ERROR, "Failed to compose RESET message\n");
|
||||
lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = sccp_lb_down_l2_cl(lbp->sli, &lbp->peer_addr, msg);
|
||||
msgb_free(msg);
|
||||
if (rc) {
|
||||
LOG_LB_PEER(lbp, LOGL_ERROR, "Failed to send RESET message\n");
|
||||
lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);
|
||||
}
|
||||
}
|
||||
|
||||
void lb_peer_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct lb_peer *lbp = fi->priv;
|
||||
struct lb_peer_ev_ctx *ctx = data;
|
||||
struct msgb *msg = ctx->msg;
|
||||
enum bssmap_le_msgt msg_type;
|
||||
|
||||
switch (event) {
|
||||
case LB_PEER_EV_MSG_UP_CL:
|
||||
msg_type = osmo_bssmap_le_msgt(msgb_l2(msg), msgb_l2len(msg));
|
||||
switch (msg_type) {
|
||||
case BSSMAP_LE_MSGT_RESET:
|
||||
osmo_fsm_inst_dispatch(fi, LB_PEER_EV_RX_RESET, msg);
|
||||
return;
|
||||
case BSSMAP_LE_MSGT_RESET_ACK:
|
||||
osmo_fsm_inst_dispatch(fi, LB_PEER_EV_RX_RESET_ACK, msg);
|
||||
return;
|
||||
default:
|
||||
LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled ConnectionLess message received: %s\n",
|
||||
osmo_bssmap_le_msgt_name(msg_type));
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&lb_peer_fsm, event));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void lb_peer_st_wait_rx_reset(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct lb_peer *lbp = fi->priv;
|
||||
struct lb_peer_ev_ctx *ctx;
|
||||
struct msgb *msg;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case LB_PEER_EV_MSG_UP_CO:
|
||||
case LB_PEER_EV_MSG_UP_CO_INITIAL:
|
||||
ctx = data;
|
||||
OSMO_ASSERT(ctx);
|
||||
LOG_LB_PEER(lbp, LOGL_ERROR, "Receiving CO message on Lb peer that has not done a proper RESET yet."
|
||||
" Disconnecting on incoming message, sending RESET to Lb peer.\n");
|
||||
/* No valid RESET procedure has happened here yet. Usually, we're expecting the Lb peer (BSC,
|
||||
* RNC) to first send a RESET message before sending Connection Oriented messages. So if we're
|
||||
* getting a CO message, likely we've just restarted or something. Send a RESET to the peer. */
|
||||
|
||||
lb_peer_disconnect(lbp->sli, ctx->conn_id);
|
||||
|
||||
lb_peer_reset(lbp);
|
||||
return;
|
||||
|
||||
case LB_PEER_EV_RX_RESET:
|
||||
msg = (struct msgb*)data;
|
||||
lb_peer_rx_reset(lbp, msg);
|
||||
return;
|
||||
|
||||
default:
|
||||
LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&lb_peer_fsm, event));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void lb_peer_st_wait_rx_reset_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct lb_peer *lbp = fi->priv;
|
||||
struct lb_peer_ev_ctx *ctx;
|
||||
struct msgb *msg;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case LB_PEER_EV_RX_RESET_ACK:
|
||||
msg = (struct msgb*)data;
|
||||
lb_peer_rx_reset_ack(lbp, msg);
|
||||
return;
|
||||
|
||||
case LB_PEER_EV_MSG_UP_CO:
|
||||
case LB_PEER_EV_MSG_UP_CO_INITIAL:
|
||||
ctx = data;
|
||||
OSMO_ASSERT(ctx);
|
||||
LOG_LB_PEER(lbp, LOGL_ERROR, "Receiving CO message on Lb peer that has not done a proper RESET yet."
|
||||
" Disconnecting on incoming message, sending RESET to Lb peer.\n");
|
||||
sccp_lb_disconnect(lbp->sli, ctx->conn_id, 0);
|
||||
/* No valid RESET procedure has happened here yet. */
|
||||
lb_peer_reset(lbp);
|
||||
return;
|
||||
|
||||
case LB_PEER_EV_RX_RESET:
|
||||
msg = (struct msgb*)data;
|
||||
lb_peer_rx_reset(lbp, msg);
|
||||
return;
|
||||
|
||||
default:
|
||||
LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&lb_peer_fsm, event));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void lb_peer_st_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct lb_peer *lbp = fi->priv;
|
||||
struct lb_peer_ev_ctx *ctx;
|
||||
struct lb_conn *lb_conn;
|
||||
struct msgb *msg;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case LB_PEER_EV_MSG_UP_CO_INITIAL:
|
||||
ctx = data;
|
||||
OSMO_ASSERT(ctx);
|
||||
OSMO_ASSERT(!ctx->lb_conn);
|
||||
OSMO_ASSERT(ctx->msg);
|
||||
|
||||
lb_conn = lb_conn_create_incoming(lbp, ctx->conn_id, __func__);
|
||||
if (!lb_conn) {
|
||||
LOG_LB_PEER(lbp, LOGL_ERROR, "Cannot allocate lb_conn\n");
|
||||
return;
|
||||
}
|
||||
|
||||
lb_conn_rx(lb_conn, ctx->msg, true);
|
||||
lb_conn_put(lb_conn, __func__);
|
||||
return;
|
||||
|
||||
case LB_PEER_EV_MSG_UP_CO:
|
||||
ctx = data;
|
||||
OSMO_ASSERT(ctx);
|
||||
OSMO_ASSERT(ctx->lb_conn);
|
||||
OSMO_ASSERT(ctx->msg);
|
||||
|
||||
lb_conn_rx(ctx->lb_conn, ctx->msg, false);
|
||||
return;
|
||||
|
||||
case LB_PEER_EV_MSG_DOWN_CO_INITIAL:
|
||||
ctx = data;
|
||||
OSMO_ASSERT(ctx);
|
||||
OSMO_ASSERT(ctx->msg);
|
||||
sccp_lb_down_l2_co_initial(lbp->sli, &lbp->peer_addr, ctx->conn_id, ctx->msg);
|
||||
return;
|
||||
|
||||
case LB_PEER_EV_MSG_DOWN_CO:
|
||||
ctx = data;
|
||||
OSMO_ASSERT(ctx);
|
||||
OSMO_ASSERT(ctx->msg);
|
||||
sccp_lb_down_l2_co(lbp->sli, ctx->conn_id, ctx->msg);
|
||||
return;
|
||||
|
||||
case LB_PEER_EV_MSG_DOWN_CL:
|
||||
OSMO_ASSERT(data);
|
||||
sccp_lb_down_l2_cl(lbp->sli, &lbp->peer_addr, (struct msgb*)data);
|
||||
return;
|
||||
|
||||
case LB_PEER_EV_RX_RESET:
|
||||
msg = (struct msgb*)data;
|
||||
lb_peer_rx_reset(lbp, msg);
|
||||
return;
|
||||
|
||||
default:
|
||||
LOG_LB_PEER(lbp, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&lb_peer_fsm, event));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int lb_peer_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct lb_peer *lbp = fi->priv;
|
||||
lb_peer_state_chg(lbp, LB_PEER_ST_WAIT_RX_RESET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lb_peer_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
struct lb_peer *lbp = fi->priv;
|
||||
lb_peer_discard_all_conns(lbp);
|
||||
llist_del(&lbp->entry);
|
||||
}
|
||||
|
||||
static const struct value_string lb_peer_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(LB_PEER_EV_MSG_UP_CL),
|
||||
OSMO_VALUE_STRING(LB_PEER_EV_MSG_UP_CO_INITIAL),
|
||||
OSMO_VALUE_STRING(LB_PEER_EV_MSG_UP_CO),
|
||||
OSMO_VALUE_STRING(LB_PEER_EV_MSG_DOWN_CL),
|
||||
OSMO_VALUE_STRING(LB_PEER_EV_MSG_DOWN_CO_INITIAL),
|
||||
OSMO_VALUE_STRING(LB_PEER_EV_MSG_DOWN_CO),
|
||||
OSMO_VALUE_STRING(LB_PEER_EV_RX_RESET),
|
||||
OSMO_VALUE_STRING(LB_PEER_EV_RX_RESET_ACK),
|
||||
OSMO_VALUE_STRING(LB_PEER_EV_CONNECTION_SUCCESS),
|
||||
OSMO_VALUE_STRING(LB_PEER_EV_CONNECTION_TIMEOUT),
|
||||
{}
|
||||
};
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
static const struct osmo_fsm_state lb_peer_fsm_states[] = {
|
||||
[LB_PEER_ST_WAIT_RX_RESET] = {
|
||||
.name = "WAIT_RX_RESET",
|
||||
.action = lb_peer_st_wait_rx_reset,
|
||||
.in_event_mask = 0
|
||||
| S(LB_PEER_EV_RX_RESET)
|
||||
| S(LB_PEER_EV_MSG_UP_CO_INITIAL)
|
||||
| S(LB_PEER_EV_MSG_UP_CO)
|
||||
| S(LB_PEER_EV_CONNECTION_TIMEOUT)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(LB_PEER_ST_WAIT_RX_RESET)
|
||||
| S(LB_PEER_ST_WAIT_RX_RESET_ACK)
|
||||
| S(LB_PEER_ST_READY)
|
||||
| S(LB_PEER_ST_DISCARDING)
|
||||
,
|
||||
},
|
||||
[LB_PEER_ST_WAIT_RX_RESET_ACK] = {
|
||||
.name = "WAIT_RX_RESET_ACK",
|
||||
.action = lb_peer_st_wait_rx_reset_ack,
|
||||
.in_event_mask = 0
|
||||
| S(LB_PEER_EV_RX_RESET)
|
||||
| S(LB_PEER_EV_RX_RESET_ACK)
|
||||
| S(LB_PEER_EV_MSG_UP_CO_INITIAL)
|
||||
| S(LB_PEER_EV_MSG_UP_CO)
|
||||
| S(LB_PEER_EV_CONNECTION_TIMEOUT)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(LB_PEER_ST_WAIT_RX_RESET)
|
||||
| S(LB_PEER_ST_WAIT_RX_RESET_ACK)
|
||||
| S(LB_PEER_ST_READY)
|
||||
| S(LB_PEER_ST_DISCARDING)
|
||||
,
|
||||
},
|
||||
[LB_PEER_ST_READY] = {
|
||||
.name = "READY",
|
||||
.action = lb_peer_st_ready,
|
||||
.in_event_mask = 0
|
||||
| S(LB_PEER_EV_RX_RESET)
|
||||
| S(LB_PEER_EV_MSG_UP_CO_INITIAL)
|
||||
| S(LB_PEER_EV_MSG_UP_CO)
|
||||
| S(LB_PEER_EV_MSG_DOWN_CO_INITIAL)
|
||||
| S(LB_PEER_EV_MSG_DOWN_CO)
|
||||
| S(LB_PEER_EV_MSG_DOWN_CL)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(LB_PEER_ST_WAIT_RX_RESET)
|
||||
| S(LB_PEER_ST_WAIT_RX_RESET_ACK)
|
||||
| S(LB_PEER_ST_READY)
|
||||
| S(LB_PEER_ST_DISCARDING)
|
||||
,
|
||||
},
|
||||
[LB_PEER_ST_DISCARDING] = {
|
||||
.name = "DISCARDING",
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm lb_peer_fsm = {
|
||||
.name = "lb_peer",
|
||||
.states = lb_peer_fsm_states,
|
||||
.num_states = ARRAY_SIZE(lb_peer_fsm_states),
|
||||
.log_subsys = DLB,
|
||||
.event_names = lb_peer_fsm_event_names,
|
||||
.timer_cb = lb_peer_fsm_timer_cb,
|
||||
.cleanup = lb_peer_fsm_cleanup,
|
||||
.allstate_action = lb_peer_allstate_action,
|
||||
.allstate_event_mask = 0
|
||||
| S(LB_PEER_EV_MSG_UP_CL)
|
||||
,
|
||||
};
|
||||
|
||||
int lb_peer_up_l2(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *calling_addr, bool co, uint32_t conn_id,
|
||||
struct msgb *l2)
|
||||
{
|
||||
struct lb_peer *lb_peer = NULL;
|
||||
uint32_t event;
|
||||
struct lb_peer_ev_ctx ctx = {
|
||||
.conn_id = conn_id,
|
||||
.msg = l2,
|
||||
};
|
||||
|
||||
if (co) {
|
||||
struct lb_conn *lb_conn;
|
||||
llist_for_each_entry(lb_conn, &sli->lb_conns, entry) {
|
||||
if (lb_conn->sccp_conn_id == conn_id) {
|
||||
lb_peer = lb_conn->lb_peer;
|
||||
ctx.lb_conn = lb_conn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lb_peer && calling_addr) {
|
||||
LOG_SCCP_LB_CO(sli, calling_addr, conn_id, LOGL_ERROR,
|
||||
"Connection-Oriented Initial message for already existing conn_id."
|
||||
" Dropping message.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!lb_peer && !calling_addr) {
|
||||
LOG_SCCP_LB_CO(sli, calling_addr, conn_id, LOGL_ERROR,
|
||||
"Connection-Oriented non-Initial message for unknown conn_id %u."
|
||||
" Dropping message.\n", conn_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (calling_addr) {
|
||||
lb_peer = lb_peer_find_or_create(sli, calling_addr);
|
||||
if (!lb_peer) {
|
||||
LOG_SCCP_LB_CL(sli, calling_addr, LOGL_ERROR, "Cannot register Lb peer\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
OSMO_ASSERT(lb_peer && lb_peer->fi);
|
||||
|
||||
if (co)
|
||||
event = calling_addr ? LB_PEER_EV_MSG_UP_CO_INITIAL : LB_PEER_EV_MSG_UP_CO;
|
||||
else
|
||||
event = LB_PEER_EV_MSG_UP_CL;
|
||||
|
||||
return osmo_fsm_inst_dispatch(lb_peer->fi, event, &ctx);
|
||||
}
|
||||
|
||||
void lb_peer_disconnect(struct sccp_lb_inst *sli, uint32_t conn_id)
|
||||
{
|
||||
struct lb_conn *lb_conn;
|
||||
llist_for_each_entry(lb_conn, &sli->lb_conns, entry) {
|
||||
if (lb_conn->sccp_conn_id == conn_id) {
|
||||
lb_conn_discard(lb_conn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
* (C) 2020 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* Author: Neels Hofmeyr
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#include <osmocom/smlc/debug.h>
|
||||
#include <osmocom/smlc/smlc_data.h>
|
||||
#include <osmocom/smlc/sccp_lb_inst.h>
|
||||
#include <osmocom/smlc/lb_peer.h>
|
||||
|
||||
/* We need an unused SCCP conn_id across all SCCP users. */
|
||||
int sccp_lb_inst_next_conn_id()
|
||||
{
|
||||
static uint32_t next_id = 1;
|
||||
int i;
|
||||
|
||||
/* This looks really suboptimal, but in most cases the static next_id should indicate exactly the next unused
|
||||
* conn_id, and we only iterate all conns once to make super sure that it is not already in use. */
|
||||
|
||||
for (i = 0; i < 0xFFFFFF; i++) {
|
||||
struct lb_peer *lb_peer;
|
||||
uint32_t conn_id = next_id;
|
||||
bool conn_id_already_used = false;
|
||||
next_id = (next_id + 1) & 0xffffff;
|
||||
|
||||
llist_for_each_entry(lb_peer, &g_smlc->lb->lb_peers, entry) {
|
||||
struct lb_conn *conn;
|
||||
lb_peer_for_each_lb_conn(conn, lb_peer) {
|
||||
if (conn_id == conn->sccp_conn_id) {
|
||||
conn_id_already_used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (conn_id_already_used)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!conn_id_already_used)
|
||||
return conn_id;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int sccp_lb_sap_up(struct osmo_prim_hdr *oph, void *_scu);
|
||||
|
||||
struct sccp_lb_inst *sccp_lb_init(void *talloc_ctx, struct osmo_sccp_instance *sccp, enum osmo_sccp_ssn ssn,
|
||||
const char *sccp_user_name)
|
||||
{
|
||||
struct sccp_lb_inst *sli = talloc(talloc_ctx, struct sccp_lb_inst);
|
||||
OSMO_ASSERT(sli);
|
||||
*sli = (struct sccp_lb_inst){
|
||||
.sccp = sccp,
|
||||
};
|
||||
|
||||
INIT_LLIST_HEAD(&sli->lb_peers);
|
||||
INIT_LLIST_HEAD(&sli->lb_conns);
|
||||
|
||||
osmo_sccp_local_addr_by_instance(&sli->local_sccp_addr, sccp, ssn);
|
||||
sli->scu = osmo_sccp_user_bind(sccp, sccp_user_name, sccp_lb_sap_up, ssn);
|
||||
osmo_sccp_user_set_priv(sli->scu, sli);
|
||||
|
||||
return sli;
|
||||
}
|
||||
|
||||
static int sccp_lb_sap_up(struct osmo_prim_hdr *oph, void *_scu)
|
||||
{
|
||||
struct osmo_sccp_user *scu = _scu;
|
||||
struct sccp_lb_inst *sli = osmo_sccp_user_get_priv(scu);
|
||||
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
|
||||
struct osmo_sccp_addr *my_addr;
|
||||
struct osmo_sccp_addr *peer_addr;
|
||||
uint32_t conn_id;
|
||||
int rc;
|
||||
|
||||
switch (OSMO_PRIM_HDR(oph)) {
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):
|
||||
/* indication of new inbound connection request */
|
||||
conn_id = prim->u.connect.conn_id;
|
||||
my_addr = &prim->u.connect.called_addr;
|
||||
peer_addr = &prim->u.connect.calling_addr;
|
||||
LOG_SCCP_LB_CO(sli, peer_addr, conn_id, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
|
||||
|
||||
if (!msgb_l2(oph->msg) || msgb_l2len(oph->msg) == 0) {
|
||||
LOG_SCCP_LB_CO(sli, peer_addr, conn_id, LOGL_NOTICE, "Received invalid N-CONNECT.ind\n");
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (osmo_sccp_addr_ri_cmp(&sli->local_sccp_addr, my_addr))
|
||||
LOG_SCCP_LB_CO(sli, peer_addr, conn_id, LOGL_ERROR,
|
||||
"Rx N-CONNECT: Called address is %s != local address %s\n",
|
||||
osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sli->sccp, my_addr),
|
||||
osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sli->sccp, &sli->local_sccp_addr));
|
||||
|
||||
/* ensure the local SCCP socket is ACTIVE */
|
||||
osmo_sccp_tx_conn_resp(scu, conn_id, my_addr, NULL, 0);
|
||||
|
||||
rc = lb_peer_up_l2(sli, peer_addr, true, conn_id, oph->msg);
|
||||
if (rc)
|
||||
osmo_sccp_tx_disconn(scu, conn_id, my_addr, SCCP_RETURN_CAUSE_UNQUALIFIED);
|
||||
break;
|
||||
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
|
||||
/* connection-oriented data received */
|
||||
conn_id = prim->u.data.conn_id;
|
||||
LOG_SCCP_LB_CO(sli, NULL, conn_id, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
|
||||
|
||||
rc = lb_peer_up_l2(sli, NULL, true, conn_id, oph->msg);
|
||||
break;
|
||||
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
|
||||
/* indication of disconnect */
|
||||
conn_id = prim->u.disconnect.conn_id;
|
||||
LOG_SCCP_LB_CO(sli, NULL, conn_id, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
|
||||
|
||||
/* If there is no L2 payload in the N-DISCONNECT, no need to dispatch up_l2(). */
|
||||
if (msgb_l2len(oph->msg))
|
||||
rc = lb_peer_up_l2(sli, NULL, true, conn_id, oph->msg);
|
||||
else
|
||||
rc = 0;
|
||||
|
||||
/* Make sure the lb_conn is dropped. It might seem more optimal to combine the disconnect() into
|
||||
* up_l2(), but since an up_l2() dispatch might already cause the lb_conn to be discarded for other
|
||||
* reasons, a separate disconnect() with a separate conn_id lookup is actually necessary. */
|
||||
sccp_lb_disconnect(sli, conn_id, 0);
|
||||
break;
|
||||
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
|
||||
/* connection-less data received */
|
||||
my_addr = &prim->u.unitdata.called_addr;
|
||||
peer_addr = &prim->u.unitdata.calling_addr;
|
||||
LOG_SCCP_LB_CL(sli, peer_addr, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_scu_prim_name(oph));
|
||||
|
||||
if (osmo_sccp_addr_ri_cmp(&sli->local_sccp_addr, my_addr))
|
||||
LOG_SCCP_LB_CL(sli, peer_addr, LOGL_ERROR,
|
||||
"Rx N-UNITDATA: Called address is %s != local address %s\n",
|
||||
osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sli->sccp, my_addr),
|
||||
osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sli->sccp, &sli->local_sccp_addr));
|
||||
|
||||
rc = lb_peer_up_l2(sli, peer_addr, false, 0, oph->msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_SCCP_LB_CL(sli, NULL, LOGL_ERROR, "%s(%s) unsupported\n", __func__, osmo_scu_prim_name(oph));
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
msgb_free(oph->msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Push some padding if necessary to reach a multiple-of-eight offset to be msgb_push() an osmo_scu_prim that will then
|
||||
* be 8-byte aligned. */
|
||||
static void msgb_pad_mod8(struct msgb *msg)
|
||||
{
|
||||
uint8_t mod8 = (intptr_t)(msg->data) % 8;
|
||||
if (mod8)
|
||||
msgb_push(msg, mod8);
|
||||
}
|
||||
|
||||
static int sccp_lb_sap_down(struct sccp_lb_inst *sli, struct osmo_prim_hdr *oph)
|
||||
{
|
||||
int rc;
|
||||
if (!sli->scu) {
|
||||
rate_ctr_inc(&g_smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_ERR_CONN_NOT_READY]);
|
||||
return -EIO;
|
||||
}
|
||||
rc = osmo_sccp_user_sap_down_nofree(sli->scu, oph);
|
||||
if (rc >= 0)
|
||||
rate_ctr_inc(&g_smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_SUCCESS]);
|
||||
else
|
||||
rate_ctr_inc(&g_smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_TX_ERR_SEND]);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sccp_lb_down_l2_co_initial(struct sccp_lb_inst *sli,
|
||||
const struct osmo_sccp_addr *called_addr,
|
||||
uint32_t conn_id, struct msgb *l2)
|
||||
{
|
||||
struct osmo_scu_prim *prim;
|
||||
|
||||
l2->l2h = l2->data;
|
||||
|
||||
msgb_pad_mod8(l2);
|
||||
prim = (struct osmo_scu_prim *) msgb_push(l2, sizeof(*prim));
|
||||
prim->u.connect = (struct osmo_scu_connect_param){
|
||||
.called_addr = *called_addr,
|
||||
.calling_addr = sli->local_sccp_addr,
|
||||
.sccp_class = 2,
|
||||
//.importance = ?,
|
||||
.conn_id = conn_id,
|
||||
};
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_REQUEST, l2);
|
||||
return sccp_lb_sap_down(sli, &prim->oph);
|
||||
}
|
||||
|
||||
int sccp_lb_down_l2_co(struct sccp_lb_inst *sli, uint32_t conn_id, struct msgb *l2)
|
||||
{
|
||||
struct osmo_scu_prim *prim;
|
||||
|
||||
l2->l2h = l2->data;
|
||||
|
||||
msgb_pad_mod8(l2);
|
||||
prim = (struct osmo_scu_prim *) msgb_push(l2, sizeof(*prim));
|
||||
prim->u.data.conn_id = conn_id;
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, l2);
|
||||
return sccp_lb_sap_down(sli, &prim->oph);
|
||||
}
|
||||
|
||||
int sccp_lb_down_l2_cl(struct sccp_lb_inst *sli, const struct osmo_sccp_addr *called_addr, struct msgb *l2)
|
||||
{
|
||||
struct osmo_scu_prim *prim;
|
||||
|
||||
l2->l2h = l2->data;
|
||||
|
||||
msgb_pad_mod8(l2);
|
||||
prim = (struct osmo_scu_prim *) msgb_push(l2, sizeof(*prim));
|
||||
prim->u.unitdata = (struct osmo_scu_unitdata_param){
|
||||
.called_addr = *called_addr,
|
||||
.calling_addr = sli->local_sccp_addr,
|
||||
};
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_REQUEST, l2);
|
||||
return sccp_lb_sap_down(sli, &prim->oph);
|
||||
}
|
||||
|
||||
int sccp_lb_disconnect(struct sccp_lb_inst *sli, uint32_t conn_id, uint32_t cause)
|
||||
{
|
||||
return osmo_sccp_tx_disconn(sli->scu, conn_id, NULL, cause);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* (C) 2020 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/smlc/smlc_data.h>
|
||||
|
||||
struct osmo_tdef g_smlc_tdefs[] = {
|
||||
{ .T=-12, .default_val=5, .desc="Timeout for BSSLAP TA Response from BSC" },
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc smlc_ctr_description[] = {
|
||||
[SMLC_CTR_BSSMAP_LE_RX_UDT_RESET] = { "bssmap_le:rx_udt_reset", "Rx BSSMAP-LE Reset" },
|
||||
[SMLC_CTR_BSSMAP_LE_RX_UDT_RESET_ACK] = { "bssmap_le:rx_udt_reset_ack", "Rx BSSMAP-LE Reset Acknowledge" },
|
||||
[SMLC_CTR_BSSMAP_LE_RX_UDT_ERR_INVALID_MSG] = { "bssmap_le:rx_udt_err_invalid_msg", "Receive invalid UnitData message" },
|
||||
[SMLC_CTR_BSSMAP_LE_RX_DT1_ERR_INVALID_MSG] = { "bssmap_le:rx_dt1_err_invalid_msg", "Receive invalid DirectTransfer1 message" },
|
||||
[SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_REQUEST] = { "bssmap_le:rx_dt1_perform_location_request", "Receive Perform Location Request from BSC" },
|
||||
[SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_TA_RESPONSE] = { "bssmap_le:rx_dt1_bsslap_ta_response", "Receive BSSLAP TA Response from BSC" },
|
||||
[SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_REJECT] = { "bssmap_le:rx_dt1_bsslap_reject", "Rx BSSLAP Reject from BSC" },
|
||||
[SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_RESET] = { "bssmap_le:rx_dt1_bsslap_reset", "Rx BSSLAP Reset (handover) from BSC" },
|
||||
[SMLC_CTR_BSSMAP_LE_RX_DT1_BSSLAP_ABORT] = { "bssmap_le:rx_dt1_bsslap_abort", "Rx BSSLAP Abort from BSC" },
|
||||
|
||||
[SMLC_CTR_BSSMAP_LE_TX_ERR_INVALID_MSG] = { "bssmap_le:tx_err_invalid_msg", "BSSMAP-LE send error: invalid message" },
|
||||
[SMLC_CTR_BSSMAP_LE_TX_ERR_CONN_NOT_READY] = { "bssmap_le:tx_err_conn_not_ready", "BSSMAP-LE send error: conn not ready" },
|
||||
[SMLC_CTR_BSSMAP_LE_TX_ERR_SEND] = { "bssmap_le:tx_err_send", "BSSMAP-LE send error" },
|
||||
[SMLC_CTR_BSSMAP_LE_TX_SUCCESS] = { "bssmap_le:tx_success", "BSSMAP-LE send success" },
|
||||
|
||||
[SMLC_CTR_BSSMAP_LE_TX_UDT_RESET] = { "bssmap_le:tx_udt_reset", "Transmit UnitData Reset" },
|
||||
[SMLC_CTR_BSSMAP_LE_TX_UDT_RESET_ACK] = { "bssmap_le:tx_udt_reset_ack", "Transmit UnitData Reset Acknowledge" },
|
||||
[SMLC_CTR_BSSMAP_LE_TX_DT1_PERFORM_LOCATION_RESPONSE] = { "bssmap_le:tx_dt1_perform_location_response", "Tx Perform Location Response to BSC" },
|
||||
[SMLC_CTR_BSSMAP_LE_TX_DT1_BSSLAP_TA_REQUEST] = { "bssmap_le:tx_dt1_bsslap_ta_request", "Tx BSSLAP TA Request to BSC" },
|
||||
};
|
||||
|
||||
static const struct rate_ctr_group_desc smlc_ctrg_desc = {
|
||||
"smlc",
|
||||
"serving mobile location center",
|
||||
OSMO_STATS_CLASS_GLOBAL,
|
||||
ARRAY_SIZE(smlc_ctr_description),
|
||||
smlc_ctr_description,
|
||||
};
|
||||
|
||||
struct smlc_state *smlc_state_alloc(void *ctx)
|
||||
{
|
||||
struct smlc_state *smlc = talloc_zero(ctx, struct smlc_state);
|
||||
OSMO_ASSERT(smlc);
|
||||
INIT_LLIST_HEAD(&smlc->subscribers);
|
||||
INIT_LLIST_HEAD(&smlc->cell_locations);
|
||||
smlc->ctrs = rate_ctr_group_alloc(smlc, &smlc_ctrg_desc, 0);
|
||||
return smlc;
|
||||
}
|
|
@ -0,0 +1,445 @@
|
|||
/* 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>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/smlc/smlc_data.h>
|
||||
#include <osmocom/smlc/smlc_loc_req.h>
|
||||
#include <osmocom/smlc/smlc_subscr.h>
|
||||
#include <osmocom/smlc/lb_conn.h>
|
||||
#include <osmocom/smlc/cell_locations.h>
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/bsslap.h>
|
||||
#include <osmocom/gsm/bssmap_le.h>
|
||||
#include <osmocom/gsm/gad.h>
|
||||
|
||||
enum smlc_loc_req_fsm_state {
|
||||
SMLC_LOC_REQ_ST_INIT,
|
||||
SMLC_LOC_REQ_ST_WAIT_TA,
|
||||
SMLC_LOC_REQ_ST_GOT_TA,
|
||||
SMLC_LOC_REQ_ST_FAILED,
|
||||
};
|
||||
|
||||
static const struct value_string smlc_loc_req_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(SMLC_LOC_REQ_EV_RX_TA_RESPONSE),
|
||||
OSMO_VALUE_STRING(SMLC_LOC_REQ_EV_RX_BSSLAP_RESET),
|
||||
OSMO_VALUE_STRING(SMLC_LOC_REQ_EV_RX_LE_PERFORM_LOCATION_ABORT),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct osmo_fsm smlc_loc_req_fsm;
|
||||
|
||||
static const struct osmo_tdef_state_timeout smlc_loc_req_fsm_timeouts[32] = {
|
||||
[SMLC_LOC_REQ_ST_WAIT_TA] = { .T = -12 },
|
||||
};
|
||||
|
||||
/* Transition to a state, using the T timer defined in smlc_loc_req_fsm_timeouts.
|
||||
* The actual timeout value is in turn obtained from network->T_defs.
|
||||
* Assumes local variable fi exists. */
|
||||
#define smlc_loc_req_fsm_state_chg(FI, STATE) \
|
||||
osmo_tdef_fsm_inst_state_chg(FI, STATE, \
|
||||
smlc_loc_req_fsm_timeouts, \
|
||||
g_smlc_tdefs, \
|
||||
5)
|
||||
|
||||
#define smlc_loc_req_fail(cause, fmt, args...) do { \
|
||||
LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_ERROR, "Perform Location Request failed in state %s: " fmt "\n", \
|
||||
smlc_loc_req ? osmo_fsm_inst_state_name(smlc_loc_req->fi) : "NULL", ## args); \
|
||||
smlc_loc_req->lcs_cause = (struct lcs_cause_ie){ \
|
||||
.present = true, \
|
||||
.cause_val = cause, \
|
||||
}; \
|
||||
smlc_loc_req_fsm_state_chg(smlc_loc_req->fi, SMLC_LOC_REQ_ST_FAILED); \
|
||||
} while(0)
|
||||
|
||||
static struct smlc_loc_req *smlc_loc_req_alloc(void *ctx)
|
||||
{
|
||||
struct smlc_loc_req *smlc_loc_req;
|
||||
|
||||
struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc(&smlc_loc_req_fsm, ctx, NULL, LOGL_DEBUG, "no-id");
|
||||
OSMO_ASSERT(fi);
|
||||
|
||||
smlc_loc_req = talloc(fi, struct smlc_loc_req);
|
||||
OSMO_ASSERT(smlc_loc_req);
|
||||
fi->priv = smlc_loc_req;
|
||||
*smlc_loc_req = (struct smlc_loc_req){
|
||||
.fi = fi,
|
||||
};
|
||||
|
||||
return smlc_loc_req;
|
||||
}
|
||||
|
||||
static int smlc_loc_req_start(struct lb_conn *lb_conn, const struct bssmap_le_perform_loc_req *loc_req_pdu)
|
||||
{
|
||||
struct smlc_loc_req *smlc_loc_req;
|
||||
|
||||
rate_ctr_inc(&g_smlc->ctrs->ctr[SMLC_CTR_BSSMAP_LE_RX_DT1_PERFORM_LOCATION_REQUEST]);
|
||||
|
||||
if (lb_conn->smlc_loc_req) {
|
||||
/* Another request is already pending. If we send Perform Location Abort, the peer doesn't know which
|
||||
* request we would mean. Just drop this on the floor. */
|
||||
LOG_SMLC_LOC_REQ(lb_conn->smlc_loc_req, LOGL_ERROR,
|
||||
"Ignoring Perform Location Request, another request is still pending\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (loc_req_pdu->imsi.type == GSM_MI_TYPE_IMSI
|
||||
&& (!lb_conn->smlc_subscr
|
||||
|| osmo_mobile_identity_cmp(&loc_req_pdu->imsi, &lb_conn->smlc_subscr->imsi))) {
|
||||
|
||||
struct smlc_subscr *smlc_subscr;
|
||||
struct lb_conn *other_conn;
|
||||
smlc_subscr = smlc_subscr_find_or_create(&loc_req_pdu->imsi, __func__);
|
||||
OSMO_ASSERT(smlc_subscr);
|
||||
|
||||
if (lb_conn->smlc_subscr && lb_conn->smlc_subscr != smlc_subscr) {
|
||||
LOG_LB_CONN(lb_conn, LOGL_ERROR,
|
||||
"IMSI mismatch: lb_conn has %s, Rx Perform Location Request has %s\n",
|
||||
smlc_subscr_to_str_c(OTC_SELECT, lb_conn->smlc_subscr),
|
||||
smlc_subscr_to_str_c(OTC_SELECT, smlc_subscr));
|
||||
smlc_subscr_put(smlc_subscr, __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Find another conn before setting this conn's subscriber */
|
||||
other_conn = lb_conn_find_by_smlc_subscr(lb_conn->smlc_subscr, __func__);
|
||||
|
||||
/* Set the subscriber before logging about it, so that it shows as log context */
|
||||
if (!lb_conn->smlc_subscr) {
|
||||
lb_conn->smlc_subscr = smlc_subscr;
|
||||
smlc_subscr_get(lb_conn->smlc_subscr, SMLC_SUBSCR_USE_LB_CONN);
|
||||
}
|
||||
|
||||
if (other_conn && other_conn != lb_conn) {
|
||||
LOG_LB_CONN(lb_conn, LOGL_ERROR, "Another conn already active for this subscriber\n");
|
||||
LOG_LB_CONN(other_conn, LOGL_ERROR, "Another conn opened for this subscriber, discarding\n");
|
||||
lb_conn_close(other_conn);
|
||||
}
|
||||
|
||||
smlc_subscr_put(smlc_subscr, __func__);
|
||||
if (other_conn)
|
||||
lb_conn_put(other_conn, __func__);
|
||||
}
|
||||
|
||||
/* smlc_loc_req has a use count on lb_conn, so its talloc ctx must not be a child of lb_conn. (Otherwise an
|
||||
* lb_conn_put() from smlc_loc_req could cause a free of smlc_loc_req's parent ctx, causing a use after free on
|
||||
* FSM termination.) */
|
||||
smlc_loc_req = smlc_loc_req_alloc(lb_conn->lb_peer);
|
||||
|
||||
*smlc_loc_req = (struct smlc_loc_req){
|
||||
.fi = smlc_loc_req->fi,
|
||||
.lb_conn = lb_conn,
|
||||
.req = *loc_req_pdu,
|
||||
};
|
||||
smlc_loc_req->latest_cell_id = loc_req_pdu->cell_id;
|
||||
lb_conn->smlc_loc_req = smlc_loc_req;
|
||||
lb_conn_get(smlc_loc_req->lb_conn, LB_CONN_USE_SMLC_LOC_REQ);
|
||||
|
||||
LOG_LB_CONN(lb_conn, LOGL_INFO, "Rx Perform Location Request (BSSLAP APDU %s), cell id is %s\n",
|
||||
loc_req_pdu->apdu_present ?
|
||||
osmo_bsslap_msgt_name(loc_req_pdu->apdu.msg_type) : "omitted",
|
||||
gsm0808_cell_id_name_c(OTC_SELECT, &smlc_loc_req->latest_cell_id));
|
||||
|
||||
/* state change to start the timeout */
|
||||
smlc_loc_req_fsm_state_chg(smlc_loc_req->fi, SMLC_LOC_REQ_ST_WAIT_TA);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_bssmap_le_conn_oriented_info(struct smlc_loc_req *smlc_loc_req,
|
||||
const struct bssmap_le_conn_oriented_info *coi)
|
||||
{
|
||||
switch (coi->apdu.msg_type) {
|
||||
|
||||
case BSSLAP_MSGT_TA_RESPONSE:
|
||||
return osmo_fsm_inst_dispatch(smlc_loc_req->fi, SMLC_LOC_REQ_EV_RX_TA_RESPONSE,
|
||||
(void*)&coi->apdu.ta_response);
|
||||
|
||||
case BSSLAP_MSGT_RESET:
|
||||
return osmo_fsm_inst_dispatch(smlc_loc_req->fi, SMLC_LOC_REQ_EV_RX_BSSLAP_RESET,
|
||||
(void*)&coi->apdu.reset);
|
||||
|
||||
case BSSLAP_MSGT_ABORT:
|
||||
smlc_loc_req_fail(LCS_CAUSE_REQUEST_ABORTED, "Aborting Location Request due to BSSLAP Abort");
|
||||
return 0;
|
||||
|
||||
case BSSLAP_MSGT_REJECT:
|
||||
smlc_loc_req_fail(LCS_CAUSE_REQUEST_ABORTED, "Aborting Location Request due to BSSLAP Reject");
|
||||
return 0;
|
||||
|
||||
default:
|
||||
LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_ERROR, "rx BSSLAP APDU with unsupported message type %s\n",
|
||||
osmo_bsslap_msgt_name(coi->apdu.msg_type));
|
||||
return -ENOTSUP;
|
||||
};
|
||||
}
|
||||
|
||||
int smlc_loc_req_rx_bssap_le(struct lb_conn *lb_conn, const struct bssap_le_pdu *bssap_le)
|
||||
{
|
||||
struct smlc_loc_req *smlc_loc_req = lb_conn->smlc_loc_req;
|
||||
const struct bssmap_le_pdu *bssmap_le = &bssap_le->bssmap_le;
|
||||
|
||||
LOG_LB_CONN(lb_conn, LOGL_DEBUG, "Rx %s\n", osmo_bssap_le_pdu_to_str_c(OTC_SELECT, bssap_le));
|
||||
|
||||
if (bssap_le->discr != BSSAP_LE_MSG_DISCR_BSSMAP_LE) {
|
||||
LOG_LB_CONN(lb_conn, LOGL_ERROR, "BSSAP-LE discr %d not implemented\n", bssap_le->discr);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
switch (bssmap_le->msg_type) {
|
||||
|
||||
case BSSMAP_LE_MSGT_PERFORM_LOC_REQ:
|
||||
return smlc_loc_req_start(lb_conn, &bssmap_le->perform_loc_req);
|
||||
|
||||
case BSSMAP_LE_MSGT_PERFORM_LOC_ABORT:
|
||||
return osmo_fsm_inst_dispatch(smlc_loc_req->fi, SMLC_LOC_REQ_EV_RX_LE_PERFORM_LOCATION_ABORT,
|
||||
(void*)&bssmap_le->perform_loc_abort);
|
||||
|
||||
case BSSMAP_LE_MSGT_CONN_ORIENTED_INFO:
|
||||
return handle_bssmap_le_conn_oriented_info(smlc_loc_req, &bssmap_le->conn_oriented_info);
|
||||
|
||||
default:
|
||||
LOG_SMLC_LOC_REQ(smlc_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 smlc_loc_req_reset(struct lb_conn *lb_conn)
|
||||
{
|
||||
struct smlc_loc_req *smlc_loc_req = lb_conn->smlc_loc_req;
|
||||
if (!smlc_loc_req)
|
||||
return;
|
||||
smlc_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Aborting Location Request due to RESET on Lb");
|
||||
}
|
||||
|
||||
static int smlc_loc_req_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct smlc_loc_req *smlc_loc_req = fi->priv;
|
||||
smlc_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE, "Timeout");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void smlc_loc_req_wait_ta_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct smlc_loc_req *smlc_loc_req = fi->priv;
|
||||
struct bssmap_le_pdu bssmap_le;
|
||||
|
||||
/* Did the original request contain a TA already? */
|
||||
if (smlc_loc_req->req.apdu_present && smlc_loc_req->req.apdu.msg_type == BSSLAP_MSGT_TA_LAYER3) {
|
||||
smlc_loc_req->ta_present = true;
|
||||
smlc_loc_req->ta = smlc_loc_req->req.apdu.ta_layer3.ta;
|
||||
LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_INFO, "TA = %u\n", smlc_loc_req->ta);
|
||||
smlc_loc_req_fsm_state_chg(smlc_loc_req->fi, SMLC_LOC_REQ_ST_GOT_TA);
|
||||
return;
|
||||
}
|
||||
|
||||
/* No TA known yet, ask via BSSLAP */
|
||||
bssmap_le = (struct bssmap_le_pdu){
|
||||
.msg_type = BSSMAP_LE_MSGT_CONN_ORIENTED_INFO,
|
||||
.conn_oriented_info = {
|
||||
.apdu = {
|
||||
.msg_type = BSSLAP_MSGT_TA_REQUEST,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
lb_conn_send_bssmap_le(smlc_loc_req->lb_conn, &bssmap_le);
|
||||
}
|
||||
|
||||
static void update_ci(struct gsm0808_cell_id *cell_id, int16_t new_ci)
|
||||
{
|
||||
struct osmo_cell_global_id cgi = {};
|
||||
struct gsm0808_cell_id ci = {
|
||||
.id_discr = CELL_IDENT_CI,
|
||||
.id.ci = new_ci,
|
||||
};
|
||||
/* Set all values from the cell_id to the cgi */
|
||||
gsm0808_cell_id_to_cgi(&cgi, cell_id);
|
||||
/* Overwrite the CI part */
|
||||
gsm0808_cell_id_to_cgi(&cgi, &ci);
|
||||
/* write back to cell_id, without changing its type */
|
||||
gsm0808_cell_id_from_cgi(cell_id, cell_id->id_discr, &cgi);
|
||||
}
|
||||
|
||||
static void smlc_loc_req_wait_ta_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct smlc_loc_req *smlc_loc_req = fi->priv;
|
||||
const struct bsslap_ta_response *ta_response;
|
||||
const struct bsslap_reset *reset;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case SMLC_LOC_REQ_EV_RX_TA_RESPONSE:
|
||||
ta_response = data;
|
||||
smlc_loc_req->ta_present = true;
|
||||
smlc_loc_req->ta = ta_response->ta;
|
||||
update_ci(&smlc_loc_req->latest_cell_id, ta_response->cell_id);
|
||||
LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_INFO, "Rx BSSLAP TA Response: cell id is now %s\n",
|
||||
gsm0808_cell_id_name_c(OTC_SELECT, &smlc_loc_req->latest_cell_id));
|
||||
smlc_loc_req_fsm_state_chg(smlc_loc_req->fi, SMLC_LOC_REQ_ST_GOT_TA);
|
||||
return;
|
||||
|
||||
case SMLC_LOC_REQ_EV_RX_BSSLAP_RESET:
|
||||
reset = data;
|
||||
smlc_loc_req->ta_present = true;
|
||||
smlc_loc_req->ta = reset->ta;
|
||||
update_ci(&smlc_loc_req->latest_cell_id, reset->cell_id);
|
||||
LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_INFO, "Rx BSSLAP Reset: cell id is now %s\n",
|
||||
gsm0808_cell_id_name_c(OTC_SELECT, &smlc_loc_req->latest_cell_id));
|
||||
smlc_loc_req_fsm_state_chg(smlc_loc_req->fi, SMLC_LOC_REQ_ST_GOT_TA);
|
||||
return;
|
||||
|
||||
case SMLC_LOC_REQ_EV_RX_LE_PERFORM_LOCATION_ABORT:
|
||||
LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_INFO, "Rx Perform Location Abort, stopping this request dead\n");
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, NULL);
|
||||
return;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void smlc_loc_req_got_ta_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct smlc_loc_req *smlc_loc_req = fi->priv;
|
||||
struct bssmap_le_pdu bssmap_le;
|
||||
struct osmo_gad location;
|
||||
int rc;
|
||||
|
||||
if (!smlc_loc_req->ta_present) {
|
||||
smlc_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE,
|
||||
"Internal error: GOT_TA event, but no TA present");
|
||||
return;
|
||||
}
|
||||
|
||||
bssmap_le = (struct bssmap_le_pdu){
|
||||
.msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_RESP,
|
||||
.perform_loc_resp = {
|
||||
.location_estimate_present = true,
|
||||
},
|
||||
};
|
||||
|
||||
rc = cell_location_from_ta(&location, &smlc_loc_req->latest_cell_id, smlc_loc_req->ta);
|
||||
if (rc) {
|
||||
smlc_loc_req_fail(LCS_CAUSE_FACILITY_NOTSUPP, "Unable to compose Location Estimate for %s: %s",
|
||||
gsm0808_cell_id_name_c(OTC_SELECT, &smlc_loc_req->latest_cell_id),
|
||||
rc == -ENOENT ? "No location information for this cell" : "unknown error");
|
||||
return;
|
||||
}
|
||||
|
||||
rc = osmo_gad_enc(&bssmap_le.perform_loc_resp.location_estimate, &location);
|
||||
if (rc <= 0) {
|
||||
smlc_loc_req_fail(LCS_CAUSE_FACILITY_NOTSUPP, "Unable to encode Location Estimate for %s (rc=%d)",
|
||||
gsm0808_cell_id_name_c(OTC_SELECT, &smlc_loc_req->latest_cell_id), rc);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_SMLC_LOC_REQ(smlc_loc_req, LOGL_INFO, "Returning location estimate to BSC: %s TA=%u --> %s\n",
|
||||
gsm0808_cell_id_name_c(OTC_SELECT, &smlc_loc_req->latest_cell_id),
|
||||
smlc_loc_req->ta, osmo_gad_to_str_c(OTC_SELECT, &location));
|
||||
|
||||
if (lb_conn_send_bssmap_le(smlc_loc_req->lb_conn, &bssmap_le)) {
|
||||
smlc_loc_req_fail(LCS_CAUSE_SYSTEM_FAILURE,
|
||||
"Unable to encode/send BSSMAP-LE Perform Location Response");
|
||||
return;
|
||||
}
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
|
||||
static void smlc_loc_req_failed_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct smlc_loc_req *smlc_loc_req = fi->priv;
|
||||
struct bssmap_le_pdu bssmap_le = {
|
||||
.msg_type = BSSMAP_LE_MSGT_PERFORM_LOC_RESP,
|
||||
.perform_loc_resp = {
|
||||
.lcs_cause = smlc_loc_req->lcs_cause,
|
||||
},
|
||||
};
|
||||
int rc;
|
||||
rc = lb_conn_send_bssmap_le(smlc_loc_req->lb_conn, &bssmap_le);
|
||||
osmo_fsm_inst_term(fi, rc ? OSMO_FSM_TERM_ERROR : OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
|
||||
void smlc_loc_req_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
struct smlc_loc_req *smlc_loc_req = fi->priv;
|
||||
if (smlc_loc_req->lb_conn && smlc_loc_req->lb_conn->smlc_loc_req == smlc_loc_req) {
|
||||
smlc_loc_req->lb_conn->smlc_loc_req = NULL;
|
||||
lb_conn_put(smlc_loc_req->lb_conn, LB_CONN_USE_SMLC_LOC_REQ);
|
||||
}
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
static const struct osmo_fsm_state smlc_loc_req_fsm_states[] = {
|
||||
[SMLC_LOC_REQ_ST_INIT] = {
|
||||
.name = "INIT",
|
||||
.out_state_mask = 0
|
||||
| S(SMLC_LOC_REQ_ST_WAIT_TA)
|
||||
| S(SMLC_LOC_REQ_ST_FAILED)
|
||||
,
|
||||
},
|
||||
[SMLC_LOC_REQ_ST_WAIT_TA] = {
|
||||
.name = "WAIT_TA",
|
||||
.in_event_mask = 0
|
||||
| S(SMLC_LOC_REQ_EV_RX_TA_RESPONSE)
|
||||
| S(SMLC_LOC_REQ_EV_RX_BSSLAP_RESET)
|
||||
| S(SMLC_LOC_REQ_EV_RX_LE_PERFORM_LOCATION_ABORT)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(SMLC_LOC_REQ_ST_GOT_TA)
|
||||
| S(SMLC_LOC_REQ_ST_FAILED)
|
||||
,
|
||||
.onenter = smlc_loc_req_wait_ta_onenter,
|
||||
.action = smlc_loc_req_wait_ta_action,
|
||||
},
|
||||
[SMLC_LOC_REQ_ST_GOT_TA] = {
|
||||
.name = "GOT_TA",
|
||||
.out_state_mask = 0
|
||||
| S(SMLC_LOC_REQ_ST_FAILED)
|
||||
,
|
||||
.onenter = smlc_loc_req_got_ta_onenter,
|
||||
},
|
||||
[SMLC_LOC_REQ_ST_FAILED] = {
|
||||
.name = "FAILED",
|
||||
.onenter = smlc_loc_req_failed_onenter,
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm smlc_loc_req_fsm = {
|
||||
.name = "smlc_loc_req",
|
||||
.states = smlc_loc_req_fsm_states,
|
||||
.num_states = ARRAY_SIZE(smlc_loc_req_fsm_states),
|
||||
.log_subsys = DLCS,
|
||||
.event_names = smlc_loc_req_fsm_event_names,
|
||||
.timer_cb = smlc_loc_req_fsm_timer_cb,
|
||||
.cleanup = smlc_loc_req_fsm_cleanup,
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void smlc_loc_req_fsm_register(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&smlc_loc_req_fsm) == 0);
|
||||
}
|
|
@ -31,12 +31,15 @@
|
|||
#include <osmocom/vty/ports.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
|
||||
#include <osmocom/sigtran/xua_msg.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
#include <osmocom/smlc/debug.h>
|
||||
#include <osmocom/smlc/smlc_data.h>
|
||||
#include <osmocom/smlc/smlc_sigtran.h>
|
||||
#include <osmocom/smlc/sccp_lb_inst.h>
|
||||
#include <osmocom/smlc/cell_locations.h>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
@ -47,9 +50,12 @@
|
|||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#include "../../config.h"
|
||||
|
||||
#define DEFAULT_M3UA_LOCAL_IP "localhost"
|
||||
#define DEFAULT_M3UA_REMOTE_IP "localhost"
|
||||
#define SMLC_DEFAULT_PC "0.23.6"
|
||||
|
||||
static const char *config_file = "osmo-smlc.cfg";
|
||||
static int daemonize = 0;
|
||||
static void *tall_smlc_ctx;
|
||||
|
@ -167,6 +173,26 @@ static void signal_handler(int signal)
|
|||
}
|
||||
|
||||
static const struct log_info_cat smlc_categories[] = {
|
||||
[DSMLC] = {
|
||||
.name = "DSMLC",
|
||||
.description = "Serving Mobile Location Center",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DREF] = {
|
||||
.name = "DREF",
|
||||
.description = "Reference Counting",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DLB] = {
|
||||
.name = "DLB",
|
||||
.description = "Lb interface",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DLCS] = {
|
||||
.name = "DLCS",
|
||||
.description = "Location Services",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info log_info = {
|
||||
|
@ -177,6 +203,7 @@ const struct log_info log_info = {
|
|||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc;
|
||||
int default_pc;
|
||||
|
||||
tall_smlc_ctx = talloc_named_const(NULL, 1, "osmo-smlc");
|
||||
msgb_talloc_ctx_init(tall_smlc_ctx, 0);
|
||||
|
@ -190,13 +217,14 @@ int main(int argc, char **argv)
|
|||
|
||||
osmo_fsm_set_dealloc_ctx(OTC_SELECT);
|
||||
|
||||
g_smlc = talloc_zero(tall_smlc_ctx, struct smlc_state);
|
||||
OSMO_ASSERT(g_smlc);
|
||||
g_smlc = smlc_state_alloc(tall_smlc_ctx);
|
||||
|
||||
/* This needs to precede handle_options() */
|
||||
vty_init(&vty_info);
|
||||
//smlc_vty_init(g_smlc);
|
||||
logging_vty_add_cmds();
|
||||
osmo_talloc_vty_add_cmds();
|
||||
ctrl_vty_init(tall_smlc_ctx);
|
||||
cell_locations_vty_init();
|
||||
|
||||
/* Initialize SS7 */
|
||||
OSMO_ASSERT(osmo_ss7_init() == 0);
|
||||
|
@ -235,9 +263,20 @@ int main(int argc, char **argv)
|
|||
}
|
||||
*/
|
||||
|
||||
if (smlc_sigtran_init() != 0) {
|
||||
LOGP(DLB, LOGL_ERROR, "Failed to initialize sigtran backhaul.\n");
|
||||
exit(1);
|
||||
default_pc = osmo_ss7_pointcode_parse(NULL, SMLC_DEFAULT_PC);
|
||||
OSMO_ASSERT(default_pc);
|
||||
|
||||
g_smlc->sccp_inst = osmo_sccp_simple_client_on_ss7_id(g_smlc, 0, "Lb", default_pc, OSMO_SS7_ASP_PROT_M3UA,
|
||||
0, DEFAULT_M3UA_LOCAL_IP, 0, DEFAULT_M3UA_REMOTE_IP);
|
||||
if (!g_smlc->sccp_inst) {
|
||||
fprintf(stderr, "Setting up SCCP failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_smlc->lb = sccp_lb_init(g_smlc, g_smlc->sccp_inst, OSMO_SCCP_SSN_SMLC_BSSAP_LE, "OsmoSMLC-Lb");
|
||||
if (!g_smlc->lb) {
|
||||
fprintf(stderr, "Setting up Lb receiver failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
signal(SIGINT, &signal_handler);
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
/* (C) 2020 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/gsm/gsm0808.h>
|
||||
|
||||
#include <osmocom/smlc/smlc_data.h>
|
||||
#include <osmocom/smlc/smlc_sigtran.h>
|
||||
|
||||
|
||||
#define DEFAULT_M3UA_REMOTE_IP "localhost"
|
||||
#define DEFAULT_PC "0.23.6"
|
||||
|
||||
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;
|
||||
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(DLB, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
|
||||
//rc = handle_unitdata_from_bsc(&scu_prim->u.unitdata.calling_addr, oph->msg, scu);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):
|
||||
/* Handle inbound connections */
|
||||
DEBUGP(DLB, "N-CONNECT.ind(X->%u)\n", scu_prim->u.connect.conn_id);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
|
||||
/* Handle outbound connection confirmation */
|
||||
DEBUGP(DLB, "N-CONNECT.cnf(%u, %s)\n", scu_prim->u.connect.conn_id,
|
||||
osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
|
||||
/* Handle incoming connection oriented data */
|
||||
DEBUGP(DLB, "N-DATA.ind(%u, %s)\n", scu_prim->u.data.conn_id,
|
||||
osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
|
||||
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
|
||||
DEBUGP(DLB, "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);
|
||||
break;
|
||||
default:
|
||||
LOGP(DLB, 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;
|
||||
}
|
||||
|
||||
int smlc_sigtran_init(void)
|
||||
{
|
||||
struct osmo_sccp_instance *sccp;
|
||||
int default_pc = osmo_ss7_pointcode_parse(NULL, DEFAULT_PC);
|
||||
|
||||
OSMO_ASSERT(default_pc);
|
||||
|
||||
sccp = osmo_sccp_simple_client_on_ss7_id(g_smlc, 0, "Lb", default_pc, OSMO_SS7_ASP_PROT_M3UA,
|
||||
0, NULL, 0, DEFAULT_M3UA_REMOTE_IP);
|
||||
|
||||
|
||||
g_smlc->sccp_user = osmo_sccp_user_bind(sccp, "SMLC", sccp_sap_up, OSMO_SCCP_SSN_SMLC_BSSAP);
|
||||
if (!g_smlc->sccp_user)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/* GSM subscriber details for use in SMLC */
|
||||
/*
|
||||
* (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* Author: Neels Hofmeyr <neels@hofmeyr.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/smlc/debug.h>
|
||||
#include <osmocom/smlc/smlc_data.h>
|
||||
#include <osmocom/smlc/smlc_subscr.h>
|
||||
|
||||
static void smlc_subscr_free(struct smlc_subscr *smlc_subscr)
|
||||
{
|
||||
llist_del(&smlc_subscr->entry);
|
||||
talloc_free(smlc_subscr);
|
||||
}
|
||||
|
||||
static int smlc_subscr_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
|
||||
{
|
||||
struct smlc_subscr *smlc_subscr = e->use_count->talloc_object;
|
||||
int32_t total;
|
||||
int level;
|
||||
|
||||
if (!e->use)
|
||||
return -EINVAL;
|
||||
|
||||
total = osmo_use_count_total(&smlc_subscr->use_count);
|
||||
|
||||
if (total == 0
|
||||
|| (total == 1 && old_use_count == 0 && e->count == 1))
|
||||
level = LOGL_INFO;
|
||||
else
|
||||
level = LOGL_DEBUG;
|
||||
|
||||
LOGPSRC(DREF, level, file, line, "%s: %s %s\n",
|
||||
smlc_subscr_to_str_c(OTC_SELECT, smlc_subscr),
|
||||
(e->count - old_use_count) > 0? "+" : "-", e->use);
|
||||
|
||||
if (e->count < 0)
|
||||
return -ERANGE;
|
||||
|
||||
if (total == 0)
|
||||
smlc_subscr_free(smlc_subscr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct smlc_subscr *smlc_subscr_alloc()
|
||||
{
|
||||
struct smlc_subscr *smlc_subscr;
|
||||
|
||||
smlc_subscr = talloc_zero(g_smlc, struct smlc_subscr);
|
||||
if (!smlc_subscr)
|
||||
return NULL;
|
||||
|
||||
smlc_subscr->use_count = (struct osmo_use_count){
|
||||
.talloc_object = smlc_subscr,
|
||||
.use_cb = smlc_subscr_use_cb,
|
||||
};
|
||||
|
||||
llist_add_tail(&smlc_subscr->entry, &g_smlc->subscribers);
|
||||
|
||||
return smlc_subscr;
|
||||
}
|
||||
|
||||
struct smlc_subscr *smlc_subscr_find(const struct osmo_mobile_identity *imsi, const char *use_token)
|
||||
{
|
||||
struct smlc_subscr *smlc_subscr;
|
||||
if (!imsi)
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(smlc_subscr, &g_smlc->subscribers, entry) {
|
||||
if (!osmo_mobile_identity_cmp(&smlc_subscr->imsi, imsi)) {
|
||||
smlc_subscr_get(smlc_subscr, use_token);
|
||||
return smlc_subscr;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct smlc_subscr *smlc_subscr_find_or_create(const struct osmo_mobile_identity *imsi, const char *use_token)
|
||||
{
|
||||
struct smlc_subscr *smlc_subscr;
|
||||
if (!imsi)
|
||||
return NULL;
|
||||
smlc_subscr = smlc_subscr_find(imsi, use_token);
|
||||
if (smlc_subscr)
|
||||
return smlc_subscr;
|
||||
smlc_subscr = smlc_subscr_alloc();
|
||||
if (!smlc_subscr)
|
||||
return NULL;
|
||||
smlc_subscr->imsi = *imsi;
|
||||
smlc_subscr_get(smlc_subscr, use_token);
|
||||
return smlc_subscr;
|
||||
}
|
||||
|
||||
int smlc_subscr_to_str_buf(char *buf, size_t buf_len, const struct smlc_subscr *smlc_subscr)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buf_len };
|
||||
OSMO_STRBUF_APPEND(sb, osmo_mobile_identity_to_str_buf, &smlc_subscr->imsi);
|
||||
OSMO_STRBUF_PRINTF(sb, "[");
|
||||
OSMO_STRBUF_APPEND(sb, osmo_use_count_to_str_buf, &smlc_subscr->use_count);
|
||||
OSMO_STRBUF_PRINTF(sb, "]");
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *smlc_subscr_to_str_c(void *ctx, const struct smlc_subscr *smlc_subscr)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", smlc_subscr_to_str_buf, smlc_subscr)
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
SUBDIRS = \
|
||||
smlc_subscr \
|
||||
$(NULL)
|
||||
|
||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||
|
@ -25,6 +26,8 @@ EXTRA_DIST = \
|
|||
$(TESTSUITE) \
|
||||
test_nodes.vty \
|
||||
test_nodes.ctrl \
|
||||
cell_locations.vty \
|
||||
osmo-smlc.cfg \
|
||||
$(NULL)
|
||||
|
||||
TESTSUITE = $(srcdir)/testsuite
|
||||
|
@ -51,7 +54,7 @@ VTY_TEST ?= *.vty
|
|||
vty-test:
|
||||
osmo_verify_transcript_vty.py -v \
|
||||
-n OsmoSMLC -p 4271 \
|
||||
-r "$(top_builddir)/src/osmo-smlc/osmo-smlc -c $(top_srcdir)/doc/examples/osmo-smlc/osmo-smlc.cfg" \
|
||||
-r "$(top_builddir)/src/osmo-smlc/osmo-smlc -c $(top_srcdir)/tests/osmo-smlc.cfg" \
|
||||
$(U) $(srcdir)/$(VTY_TEST)
|
||||
|
||||
# To update the CTRL script from current application behavior,
|
||||
|
@ -61,7 +64,7 @@ ctrl-test:
|
|||
-rm -f $(CTRL_TEST_DB)
|
||||
osmo_verify_transcript_ctrl.py -v \
|
||||
-p 4272 \
|
||||
-r "$(top_builddir)/src/osmo-smlc/osmo-smlc -c $(top_srcdir)/doc/examples/osmo-smlc/osmo-smlc.cfg" \
|
||||
-r "$(top_builddir)/src/osmo-smlc/osmo-smlc -c $(top_srcdir)/tests/osmo-smlc.cfg" \
|
||||
$(U) $(srcdir)/*.ctrl
|
||||
-rm -f $(CTRL_TEST_DB)
|
||||
-rm $(CTRL_TEST_DB)-*
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
OsmoSMLC> enable
|
||||
|
||||
OsmoSMLC# show cells
|
||||
% No cell locations are configured
|
||||
|
||||
OsmoSMLC# configure terminal
|
||||
|
||||
OsmoSMLC(config)# cells?
|
||||
cells Configure cell locations
|
||||
|
||||
OsmoSMLC(config)# cells
|
||||
OsmoSMLC(config-cells)# list
|
||||
...
|
||||
lac-ci <0-65535> <0-65535> lat LATITUDE lon LONGITUDE
|
||||
no lac-ci <0-65535> <0-65535>
|
||||
cgi <0-999> <0-999> <0-65535> <0-65535> lat LATITUDE lon LONGITUDE
|
||||
no cgi <0-999> <0-999> <0-65535> <0-65535>
|
||||
|
||||
OsmoSMLC(config-cells)# lac-ci?
|
||||
lac-ci Cell location by LAC and CI
|
||||
OsmoSMLC(config-cells)# lac-ci ?
|
||||
<0-65535> LAC
|
||||
OsmoSMLC(config-cells)# lac-ci 23 ?
|
||||
<0-65535> CI
|
||||
OsmoSMLC(config-cells)# lac-ci 23 42 ?
|
||||
lat Global latitute coordinate
|
||||
OsmoSMLC(config-cells)# lac-ci 23 42 lat ?
|
||||
LATITUDE Latitude floating-point number, -90.0 (S) to 90.0 (N)
|
||||
OsmoSMLC(config-cells)# lac-ci 23 42 lat 23.23 ?
|
||||
lon Global longitude coordinate
|
||||
OsmoSMLC(config-cells)# lac-ci 23 42 lat 23.23 lon ?
|
||||
LONGITUDE Longitude as floating-point number, -180.0 (W) to 180.0 (E)
|
||||
OsmoSMLC(config-cells)# lac-ci 23 42 lat 23.23 lon 42.42 ?
|
||||
<cr>
|
||||
|
||||
OsmoSMLC(config-cells)# cgi?
|
||||
cgi Cell location by Cell-Global ID
|
||||
OsmoSMLC(config-cells)# cgi ?
|
||||
<0-999> MCC
|
||||
OsmoSMLC(config-cells)# cgi 001 ?
|
||||
<0-999> MNC
|
||||
OsmoSMLC(config-cells)# cgi 001 02 ?
|
||||
<0-65535> LAC
|
||||
OsmoSMLC(config-cells)# cgi 001 02 3 ?
|
||||
<0-65535> CI
|
||||
OsmoSMLC(config-cells)# cgi 001 02 3 4 ?
|
||||
lat Global latitute coordinate
|
||||
OsmoSMLC(config-cells)# cgi 001 02 3 4 lat ?
|
||||
LATITUDE Latitude floating-point number, -90.0 (S) to 90.0 (N)
|
||||
OsmoSMLC(config-cells)# cgi 001 02 3 4 lat 1.1 ?
|
||||
lon Global longitude coordinate
|
||||
OsmoSMLC(config-cells)# cgi 001 02 3 4 lat 1.1 lon ?
|
||||
LONGITUDE Longitude as floating-point number, -180.0 (W) to 180.0 (E)
|
||||
OsmoSMLC(config-cells)# cgi 001 02 3 4 lat 1.1 lon 2.2 ?
|
||||
<cr>
|
||||
|
||||
OsmoSMLC(config-cells)# lac-ci 23 42 lat 23.23 lon 42.42
|
||||
OsmoSMLC(config-cells)# cgi 001 02 3 4 lat 1.1 lon 2.2
|
||||
|
||||
OsmoSMLC(config-cells)# do show cells
|
||||
cells
|
||||
lac-ci 23 42 lat 23.23 lon 42.42
|
||||
cgi 001 02 3 4 lat 1.1 lon 2.2
|
||||
|
||||
OsmoSMLC(config-cells)# show running-config
|
||||
...
|
||||
cells
|
||||
lac-ci 23 42 lat 23.23 lon 42.42
|
||||
cgi 001 02 3 4 lat 1.1 lon 2.2
|
||||
...
|
||||
|
||||
OsmoSMLC(config-cells)# no lac-ci 99 99
|
||||
% cannot remove, no such entry
|
||||
OsmoSMLC(config-cells)# no cgi 009 08 7 6
|
||||
% cannot remove, no such entry
|
||||
|
||||
OsmoSMLC(config-cells)# do show cells
|
||||
cells
|
||||
lac-ci 23 42 lat 23.23 lon 42.42
|
||||
cgi 001 02 3 4 lat 1.1 lon 2.2
|
||||
|
||||
OsmoSMLC(config-cells)# lac-ci 23 42 lat 17.17 lon 18.18
|
||||
OsmoSMLC(config-cells)# do show cells
|
||||
cells
|
||||
lac-ci 23 42 lat 17.17 lon 18.18
|
||||
cgi 001 02 3 4 lat 1.1 lon 2.2
|
||||
|
||||
OsmoSMLC(config-cells)# no lac-ci 23 42
|
||||
OsmoSMLC(config-cells)# no cgi 001 02 3 4
|
||||
|
||||
OsmoSMLC(config-cells)# do show cells
|
||||
% No cell locations are configured
|
|
@ -0,0 +1,39 @@
|
|||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
-ggdb3 \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
smlc_subscr_test.ok \
|
||||
smlc_subscr_test.err \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
smlc_subscr_test \
|
||||
$(NULL)
|
||||
|
||||
smlc_subscr_test_SOURCES = \
|
||||
smlc_subscr_test.c \
|
||||
$(NULL)
|
||||
|
||||
smlc_subscr_test_LDADD = \
|
||||
$(top_builddir)/src/osmo-smlc/smlc_data.o \
|
||||
$(top_builddir)/src/osmo-smlc/smlc_subscr.o \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
update_exp:
|
||||
$(builddir)/smlc_subscr_test >$(srcdir)/smlc_subscr_test.ok 2>$(srcdir)/smlc_subscr_test.err
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
*
|
||||
* Author: Neels Hofmeyr <neels@hofmeyr.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/smlc/debug.h>
|
||||
#include <osmocom/smlc/smlc_data.h>
|
||||
#include <osmocom/smlc/smlc_subscr.h>
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
struct smlc_state *g_smlc;
|
||||
|
||||
#define VERBOSE_ASSERT(val, expect_op, fmt) \
|
||||
do { \
|
||||
printf(#val " == " fmt "\n", (val)); \
|
||||
OSMO_ASSERT((val) expect_op); \
|
||||
} while (0);
|
||||
|
||||
#define USE_FOO "foo"
|
||||
#define USE_BAR "bar"
|
||||
|
||||
static void assert_smlc_subscr(const struct smlc_subscr *smlc_subscr, const struct osmo_mobile_identity *imsi)
|
||||
{
|
||||
struct smlc_subscr *sfound;
|
||||
OSMO_ASSERT(smlc_subscr);
|
||||
OSMO_ASSERT(osmo_mobile_identity_cmp(&smlc_subscr->imsi, imsi) == 0);
|
||||
|
||||
sfound = smlc_subscr_find(imsi, __func__);
|
||||
OSMO_ASSERT(sfound == smlc_subscr);
|
||||
|
||||
smlc_subscr_put(sfound, __func__);
|
||||
}
|
||||
|
||||
static void test_smlc_subscr(void)
|
||||
{
|
||||
struct smlc_subscr *s1, *s2, *s3;
|
||||
const struct osmo_mobile_identity imsi1 = { .type = GSM_MI_TYPE_IMSI, .imsi = "1234567890", };
|
||||
const struct osmo_mobile_identity imsi2 = { .type = GSM_MI_TYPE_IMSI, .imsi = "9876543210", };
|
||||
const struct osmo_mobile_identity imsi3 = { .type = GSM_MI_TYPE_IMSI, .imsi = "423423", };
|
||||
|
||||
printf("Test SMLC subscriber allocation and deletion\n");
|
||||
|
||||
/* Check for emptiness */
|
||||
VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 0, "%d");
|
||||
OSMO_ASSERT(smlc_subscr_find(&imsi1, "-") == NULL);
|
||||
OSMO_ASSERT(smlc_subscr_find(&imsi2, "-") == NULL);
|
||||
OSMO_ASSERT(smlc_subscr_find(&imsi3, "-") == NULL);
|
||||
|
||||
/* Allocate entry 1 */
|
||||
s1 = smlc_subscr_find_or_create(&imsi1, USE_FOO);
|
||||
VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 1, "%d");
|
||||
assert_smlc_subscr(s1, &imsi1);
|
||||
VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 1, "%d");
|
||||
OSMO_ASSERT(smlc_subscr_find(&imsi2, "-") == NULL);
|
||||
|
||||
/* Allocate entry 2 */
|
||||
s2 = smlc_subscr_find_or_create(&imsi2, USE_BAR);
|
||||
VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 2, "%d");
|
||||
|
||||
/* Allocate entry 3 */
|
||||
s3 = smlc_subscr_find_or_create(&imsi3, USE_FOO);
|
||||
smlc_subscr_get(s3, USE_BAR);
|
||||
VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 3, "%d");
|
||||
|
||||
/* Check entries */
|
||||
assert_smlc_subscr(s1, &imsi1);
|
||||
assert_smlc_subscr(s2, &imsi2);
|
||||
assert_smlc_subscr(s3, &imsi3);
|
||||
|
||||
/* Free entry 1 */
|
||||
smlc_subscr_put(s1, USE_FOO);
|
||||
s1 = NULL;
|
||||
VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 2, "%d");
|
||||
OSMO_ASSERT(smlc_subscr_find(&imsi1, "-") == NULL);
|
||||
|
||||
assert_smlc_subscr(s2, &imsi2);
|
||||
assert_smlc_subscr(s3, &imsi3);
|
||||
|
||||
/* Free entry 2 */
|
||||
smlc_subscr_put(s2, USE_BAR);
|
||||
s2 = NULL;
|
||||
VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 1, "%d");
|
||||
OSMO_ASSERT(smlc_subscr_find(&imsi1, "-") == NULL);
|
||||
OSMO_ASSERT(smlc_subscr_find(&imsi2, "-") == NULL);
|
||||
assert_smlc_subscr(s3, &imsi3);
|
||||
|
||||
/* Remove one use of entry 3 */
|
||||
smlc_subscr_put(s3, USE_BAR);
|
||||
assert_smlc_subscr(s3, &imsi3);
|
||||
VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 1, "%d");
|
||||
|
||||
/* Free entry 3 */
|
||||
smlc_subscr_put(s3, USE_FOO);
|
||||
s3 = NULL;
|
||||
VERBOSE_ASSERT(llist_count(&g_smlc->subscribers), == 0, "%d");
|
||||
OSMO_ASSERT(smlc_subscr_find(&imsi3, "-") == NULL);
|
||||
|
||||
OSMO_ASSERT(llist_empty(&g_smlc->subscribers));
|
||||
}
|
||||
|
||||
static const struct log_info_cat log_categories[] = {
|
||||
[DREF] = {
|
||||
.name = "DREF",
|
||||
.description = "Reference Counting",
|
||||
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct log_info log_info = {
|
||||
.cat = log_categories,
|
||||
.num_cat = ARRAY_SIZE(log_categories),
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
void *ctx = talloc_named_const(NULL, 0, "smlc_subscr_test");
|
||||
|
||||
osmo_init_logging2(ctx, &log_info);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_print_timestamp(osmo_stderr_target, 0);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_category(osmo_stderr_target, 1);
|
||||
|
||||
g_smlc = smlc_state_alloc(ctx);
|
||||
|
||||
printf("Testing SMLC subscriber code.\n");
|
||||
|
||||
test_smlc_subscr();
|
||||
|
||||
printf("Done\n");
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
DREF IMSI-1234567890[1 (foo)]: + foo
|
||||
DREF IMSI-1234567890[2 (foo,assert_smlc_subscr)]: + assert_smlc_subscr
|
||||
DREF IMSI-1234567890[1 (foo)]: - assert_smlc_subscr
|
||||
DREF IMSI-9876543210[1 (bar)]: + bar
|
||||
DREF IMSI-423423[1 (foo)]: + foo
|
||||
DREF IMSI-423423[2 (foo,bar)]: + bar
|
||||
DREF IMSI-1234567890[2 (foo,assert_smlc_subscr)]: + assert_smlc_subscr
|
||||
DREF IMSI-1234567890[1 (foo)]: - assert_smlc_subscr
|
||||
DREF IMSI-9876543210[2 (bar,assert_smlc_subscr)]: + assert_smlc_subscr
|
||||
DREF IMSI-9876543210[1 (bar)]: - assert_smlc_subscr
|
||||
DREF IMSI-423423[3 (foo,bar,assert_smlc_subscr)]: + assert_smlc_subscr
|
||||
DREF IMSI-423423[2 (foo,bar)]: - assert_smlc_subscr
|
||||
DREF IMSI-1234567890[0 (-)]: - foo
|
||||
DREF IMSI-9876543210[2 (bar,assert_smlc_subscr)]: + assert_smlc_subscr
|
||||
DREF IMSI-9876543210[1 (bar)]: - assert_smlc_subscr
|
||||
DREF IMSI-423423[3 (foo,bar,assert_smlc_subscr)]: + assert_smlc_subscr
|
||||
DREF IMSI-423423[2 (foo,bar)]: - assert_smlc_subscr
|
||||
DREF IMSI-9876543210[0 (-)]: - bar
|
||||
DREF IMSI-423423[3 (foo,bar,assert_smlc_subscr)]: + assert_smlc_subscr
|
||||
DREF IMSI-423423[2 (foo,bar)]: - assert_smlc_subscr
|
||||
DREF IMSI-423423[1 (foo)]: - bar
|
||||
DREF IMSI-423423[2 (foo,assert_smlc_subscr)]: + assert_smlc_subscr
|
||||
DREF IMSI-423423[1 (foo)]: - assert_smlc_subscr
|
||||
DREF IMSI-423423[0 (-)]: - foo
|
|
@ -0,0 +1,12 @@
|
|||
Testing SMLC subscriber code.
|
||||
Test SMLC subscriber allocation and deletion
|
||||
llist_count(&g_smlc->subscribers) == 0
|
||||
llist_count(&g_smlc->subscribers) == 1
|
||||
llist_count(&g_smlc->subscribers) == 1
|
||||
llist_count(&g_smlc->subscribers) == 2
|
||||
llist_count(&g_smlc->subscribers) == 3
|
||||
llist_count(&g_smlc->subscribers) == 2
|
||||
llist_count(&g_smlc->subscribers) == 1
|
||||
llist_count(&g_smlc->subscribers) == 1
|
||||
llist_count(&g_smlc->subscribers) == 0
|
||||
Done
|
|
@ -1,2 +1,9 @@
|
|||
AT_INIT
|
||||
AT_BANNER([Regression tests.])
|
||||
|
||||
AT_SETUP([smlc_subscr])
|
||||
AT_KEYWORDS([smlc_subscr])
|
||||
cat $abs_srcdir/smlc_subscr/smlc_subscr_test.ok > expout
|
||||
cat $abs_srcdir/smlc_subscr/smlc_subscr_test.err > experr
|
||||
AT_CHECK([$abs_top_builddir/tests/smlc_subscr/smlc_subscr_test], [], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
|
Loading…
Reference in New Issue