mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw.git
synced 2025-10-23 08:11:57 +00:00
Compare commits
38 Commits
1dc21cb6b8
...
4da67bb587
Author | SHA1 | Date | |
---|---|---|---|
|
4da67bb587 | ||
|
2c88bdb492 | ||
|
2601d9b3f0 | ||
|
315874927e | ||
|
cb4962f7f0 | ||
|
46a2c0afc2 | ||
|
6679c49133 | ||
|
4db21846e7 | ||
|
883048efd4 | ||
|
33cbc694f5 | ||
|
563f574178 | ||
|
d144d7b497 | ||
|
35a834078f | ||
|
160b372bf9 | ||
|
225a215b09 | ||
|
5eb95cf42a | ||
|
f91e32fc01 | ||
|
37b9a98a10 | ||
|
1f8fb9ae8c | ||
|
6a254f08c7 | ||
|
4e88e2b4ee | ||
|
894a3a455e | ||
|
135a8b079f | ||
|
df7dc3b6f4 | ||
|
8a573665c4 | ||
|
72c96d4792 | ||
|
b561656431 | ||
|
8aef494af5 | ||
|
718d71d3db | ||
|
24c850f8f0 | ||
|
cb03fc80c4 | ||
|
a3cceea45a | ||
|
1386a0da73 | ||
|
5ebcace219 | ||
|
bb3f454c8b | ||
|
ca8fc60231 | ||
|
3ef79eb800 | ||
|
5676dfbb75 |
@@ -7,3 +7,4 @@
|
||||
# If any interfaces have been added since the last public release: c:r:a + 1.
|
||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
|
||||
#library what description / commit summary line
|
||||
libosmo-sigtran >2.1.0 osmo_sccp_addr_{create,update}()
|
||||
|
@@ -1,11 +1,22 @@
|
||||
noinst_HEADERS = \
|
||||
vty.h \
|
||||
context_map.h hnbgw.h hnbgw_cn.h \
|
||||
hnbgw_hnbap.h hnbgw_rua.h hnbgw_ranap.h \
|
||||
kpi.h \
|
||||
nft_kpi.h \
|
||||
ranap_rab_ass.h mgw_fsm.h tdefs.h \
|
||||
cnlink.h \
|
||||
context_map.h \
|
||||
hnb.h \
|
||||
hnb_persistent.h \
|
||||
hnbgw.h \
|
||||
hnbgw_cn.h \
|
||||
hnbgw_hnbap.h \
|
||||
hnbgw_pfcp.h \
|
||||
hnbgw_ranap.h \
|
||||
hnbgw_rua.h \
|
||||
hnbgw_sccp.h \
|
||||
kpi.h \
|
||||
mgw_fsm.h \
|
||||
nft_kpi.h \
|
||||
ps_rab_ass_fsm.h \
|
||||
ps_rab_fsm.h \
|
||||
ranap_rab_ass.h \
|
||||
tdefs.h \
|
||||
umts_cell_id.h \
|
||||
vty.h \
|
||||
$(NULL)
|
||||
|
138
include/osmocom/hnbgw/cnlink.h
Normal file
138
include/osmocom/hnbgw/cnlink.h
Normal file
@@ -0,0 +1,138 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw_sccp.h>
|
||||
|
||||
struct hnbgw_cnpool;
|
||||
|
||||
enum hnbgw_cnlink_ctr {
|
||||
/* TODO: basic counters completely missing
|
||||
* ...
|
||||
*/
|
||||
CNLINK_CTR_RANAP_RX_UDT_RESET,
|
||||
CNLINK_CTR_RANAP_RX_UDT_RESET_ACK,
|
||||
CNLINK_CTR_RANAP_RX_UDT_PAGING,
|
||||
CNLINK_CTR_RANAP_RX_UDT_UNKNOWN,
|
||||
CNLINK_CTR_RANAP_RX_UDT_UNSUPPORTED,
|
||||
CNLINK_CTR_RANAP_RX_UDT_OVERLOAD_IND,
|
||||
CNLINK_CTR_RANAP_RX_UDT_ERROR_IND,
|
||||
|
||||
CNLINK_CTR_RANAP_TX_UDT_RESET,
|
||||
CNLINK_CTR_RANAP_TX_UDT_RESET_ACK,
|
||||
|
||||
/* SCCP Counters: */
|
||||
CNLINK_CTR_SCCP_N_UNITDATA_REQ,
|
||||
CNLINK_CTR_SCCP_N_UNITDATA_IND,
|
||||
CNLINK_CTR_SCCP_N_NOTICE_IND,
|
||||
CNLINK_CTR_SCCP_N_CONNECT_REQ,
|
||||
CNLINK_CTR_SCCP_N_CONNECT_CNF,
|
||||
CNLINK_CTR_SCCP_N_DATA_REQ,
|
||||
CNLINK_CTR_SCCP_N_DATA_IND,
|
||||
CNLINK_CTR_SCCP_N_DISCONNECT_REQ,
|
||||
CNLINK_CTR_SCCP_N_DISCONNECT_IND,
|
||||
CNLINK_CTR_SCCP_N_PCSTATE_IND,
|
||||
CNLINK_CTR_SCCP_RLSD_CN_ORIGIN,
|
||||
|
||||
/* Counters related to link selection from a CN pool. */
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_NEW,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_REATTACH,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_KNOWN,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_PAGED,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_ATTACH_LOST,
|
||||
CNLINK_CTR_CNPOOL_EMERG_FORWARDED,
|
||||
};
|
||||
#define CNLINK_CTR_INC(cnlink, x) rate_ctr_inc2((cnlink)->ctrs, x)
|
||||
|
||||
enum cnlink_stat {
|
||||
CNLINK_STAT_CONNECTED,
|
||||
};
|
||||
#define CNLINK_STAT(cnlink, x) osmo_stat_item_group_get_item((cnlink)->statg, x)
|
||||
#define CNLINK_STAT_SET(cnlink, x, val) osmo_stat_item_set(CNLINK_STAT(cnlink, x), val)
|
||||
|
||||
/* User provided configuration for struct hnbgw_cnlink. */
|
||||
struct hnbgw_cnlink_cfg {
|
||||
/* cs7 address book entry to indicate both the remote point-code of the peer, as well as which cs7 instance to
|
||||
* use. */
|
||||
char *remote_addr_name;
|
||||
|
||||
struct osmo_nri_ranges *nri_ranges;
|
||||
};
|
||||
|
||||
/* A CN peer, like 'msc 0' or 'sgsn 23' */
|
||||
struct hnbgw_cnlink {
|
||||
struct llist_head entry;
|
||||
|
||||
/* backpointer to CS or PS CN pool. */
|
||||
struct hnbgw_cnpool *pool;
|
||||
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
int nr;
|
||||
|
||||
struct hnbgw_cnlink_cfg vty;
|
||||
struct hnbgw_cnlink_cfg use;
|
||||
|
||||
/* To print in logging/VTY */
|
||||
char *name;
|
||||
|
||||
/* Copy of the address book entry use.remote_addr_name. */
|
||||
struct osmo_sccp_addr remote_addr;
|
||||
|
||||
/* The SCCP instance for the cs7 instance indicated by remote_addr_name. (Multiple hnbgw_cnlinks may use the
|
||||
* same hnbgw_sccp_user -- there is exactly one hnbgw_sccp_user per configured cs7 instance.) */
|
||||
struct hnbgw_sccp_user *hnbgw_sccp_user;
|
||||
|
||||
/* linked list of hnbgw_context_map */
|
||||
struct llist_head map_list;
|
||||
|
||||
bool allow_attach;
|
||||
bool allow_emerg;
|
||||
struct llist_head paging;
|
||||
|
||||
struct rate_ctr_group *ctrs;
|
||||
struct osmo_stat_item_group *statg;
|
||||
};
|
||||
|
||||
struct hnbgw_cnlink *hnbgw_cnlink_alloc(struct hnbgw_cnpool *cnpool, int nr);
|
||||
void hnbgw_cnlink_term_and_free(struct hnbgw_cnlink *cnlink);
|
||||
void hnbgw_cnlink_drop_sccp(struct hnbgw_cnlink *cnlink);
|
||||
int hnbgw_cnlink_set_name(struct hnbgw_cnlink *cnlink, const char *name);
|
||||
int hnbgw_cnlink_tx_ranap_reset(struct hnbgw_cnlink *cnlink);
|
||||
int hnbgw_cnlink_tx_ranap_reset_ack(struct hnbgw_cnlink *cnlink);
|
||||
|
||||
int hnbgw_cnlink_start_or_restart(struct hnbgw_cnlink *cnlink);
|
||||
|
||||
char *hnbgw_cnlink_sccp_addr_to_str(struct hnbgw_cnlink *cnlink, const struct osmo_sccp_addr *addr);
|
||||
|
||||
static inline struct osmo_sccp_instance *hnbgw_cnlink_sccp(const struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
if (!cnlink)
|
||||
return NULL;
|
||||
if (!cnlink->hnbgw_sccp_user)
|
||||
return NULL;
|
||||
return hnbgw_sccp_user_get_sccp_instance(cnlink->hnbgw_sccp_user);
|
||||
}
|
||||
|
||||
/* cnlink_fsm.c related: */
|
||||
extern struct osmo_fsm cnlink_fsm;
|
||||
bool cnlink_is_conn_ready(const struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_rx_reset_cmd(struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_rx_reset_ack(struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_resend_reset(struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_set_disconnected(struct hnbgw_cnlink *cnlink);
|
||||
|
||||
/* cnlink_paging.c related: */
|
||||
const char *cnlink_paging_add_ranap(struct hnbgw_cnlink *cnlink, const RANAP_PagingIEs_t *paging_ies);
|
||||
struct hnbgw_cnlink *cnlink_find_by_paging_mi(struct hnbgw_cnpool *cnpool, const struct osmo_mobile_identity *mi);
|
||||
|
||||
#define LOG_CNLINK(CNLINK, SUBSYS, LEVEL, FMT, ARGS...) \
|
||||
LOGP(SUBSYS, LEVEL, "(%s) " FMT, (CNLINK) ? (CNLINK)->name : "null", ##ARGS)
|
@@ -3,6 +3,7 @@
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
|
||||
@@ -54,13 +55,14 @@ enum map_rua_fsm_event {
|
||||
* MAP_SCCP_EV_TX_DATA_REQUEST, MAP_SCCP_EV_RAN_DISC.
|
||||
*/
|
||||
enum map_sccp_fsm_event {
|
||||
/* Receiving an SCCP CC from CN. */
|
||||
/* Receiving an SCCP CC from CN.
|
||||
* Parameter: struct msgb *ranap_msg, may be NULL or empty. */
|
||||
MAP_SCCP_EV_RX_CONNECTION_CONFIRM,
|
||||
/* Receiving some data from CN via SCCP, to forward via RUA to HNB.
|
||||
* Parameter: struct msgb *ranap_msg */
|
||||
* Parameter: struct msgb *ranap_msg, may be NULL or empty. */
|
||||
MAP_SCCP_EV_RX_DATA_INDICATION,
|
||||
/* RUA has received some data from HNB to forward via SCCP to CN.
|
||||
* Parameter: struct msgb *ranap_msg */
|
||||
* Parameter: struct msgb *ranap_msg. */
|
||||
MAP_SCCP_EV_TX_DATA_REQUEST,
|
||||
/* 3GPP TS 25.468 9.1.5: The RAN side received a RUA Disconnect.
|
||||
* - Under normal conditions (cause=Normal) the RUA Disconnect contains a RANAP Iu-ReleaseComplete.
|
||||
@@ -75,15 +77,19 @@ enum map_sccp_fsm_event {
|
||||
* happened under error or normal conditions, as per the above.
|
||||
*/
|
||||
MAP_SCCP_EV_RAN_DISC,
|
||||
/* The RAN released ungracefully. We will directly disconnect the SCCP connection, too. */
|
||||
/* The RAN released ungracefully. We will directly disconnect the SCCP connection, too.
|
||||
* Parameter: no parameter, NULL. */
|
||||
MAP_SCCP_EV_RAN_LINK_LOST,
|
||||
/* Receiving an SCCP RLSD from CN, or libosmo-sigtran tells us about SCCP connection timeout. All done. */
|
||||
/* Receiving an SCCP RLSD from CN, or libosmo-sigtran tells us about SCCP connection timeout. All done.
|
||||
* Parameter: struct msgb *ranap_msg, may be NULL or empty. */
|
||||
MAP_SCCP_EV_RX_RELEASED,
|
||||
/* The human admin asks to drop the current SCCP connection, by telnet VTY 'apply sccp' in presence of SCCP
|
||||
* config changes. */
|
||||
* config changes.
|
||||
* Parameter: no parameter, NULL. */
|
||||
MAP_SCCP_EV_USER_ABORT,
|
||||
/* The CN link can no longer work, for example a RANAP RESET was received from the cnlink that hosts this
|
||||
* context map. */
|
||||
* context map.
|
||||
* Parameter: no parameter, NULL. */
|
||||
MAP_SCCP_EV_CN_LINK_LOST,
|
||||
};
|
||||
|
||||
@@ -163,6 +169,12 @@ struct hnbgw_context_map {
|
||||
uint32_t scu_conn_id;
|
||||
/* FSM handling the SCCP state for scu_conn_id. */
|
||||
struct osmo_fsm_inst *sccp_fi;
|
||||
/* State context related to field sccp_fi above: */
|
||||
struct {
|
||||
/* List of cached packets received from RUA and to be forwarded
|
||||
once SCCP CReq is CC'ed and move to CONNECTED state. */
|
||||
struct llist_head wait_cc_tx_msg_list;
|
||||
} sccp_fi_ctx;
|
||||
|
||||
/* False for CS, true for PS */
|
||||
bool is_ps;
|
||||
@@ -188,15 +200,15 @@ struct hnbgw_context_map {
|
||||
* and Request and Response don't necessarily match the RAB IDs contained. In practice I only ever see a single
|
||||
* RAB matching in Request and Response, but we cannot rely on that to always be true.
|
||||
*
|
||||
* The state of each RAB's PFCP negotiation is kept separately in the list ps_rabs, and as soon as all RABs
|
||||
* The state of each RAB's PFCP negotiation is kept separately in the field ps_rab_list, and as soon as all RABs
|
||||
* appearing in a PS RAB Assignment message have completed their PFCP setup, we can replace the GTP info for the
|
||||
* RAB IDs and forward the RAB Assignment Request to HNB / the RAB Assignment Response to CN.
|
||||
*/
|
||||
struct llist_head ps_rab_ass;
|
||||
struct llist_head ps_rab_ass_list;
|
||||
|
||||
/* All PS RABs and their GTP tunnel mappings. list of struct ps_rab. Each ps_rab FSM handles the PFCP
|
||||
* communication for one particular RAB ID. */
|
||||
struct llist_head ps_rabs;
|
||||
struct llist_head ps_rab_list;
|
||||
|
||||
/* RAB state tracking. As RAB-ID is an 8-bit integer, we need 256 elements in the array */
|
||||
uint8_t rab_state[256];
|
||||
|
76
include/osmocom/hnbgw/hnb.h
Normal file
76
include/osmocom/hnbgw/hnb.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/hashtable.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ranap/RANAP_CN-DomainIndicator.h>
|
||||
|
||||
#define DEBUG
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_pool.h>
|
||||
|
||||
#include <osmocom/hnbgw/umts_cell_id.h>
|
||||
#include <osmocom/hnbgw/nft_kpi.h>
|
||||
#include <osmocom/hnbgw/cnlink.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
|
||||
#define HNB_STORE_RAB_DURATIONS_INTERVAL 1 /* seconds */
|
||||
|
||||
#define LOGHNB(HNB_CTX, ss, lvl, fmt, args ...) \
|
||||
LOGP(ss, lvl, "(%s) " fmt, hnb_context_name(HNB_CTX), ## args)
|
||||
|
||||
enum hnb_ctrl_node {
|
||||
CTRL_NODE_HNB = _LAST_CTRL_NODE,
|
||||
_LAST_CTRL_NODE_HNB
|
||||
};
|
||||
|
||||
/* The lifecycle of the hnb_context object is the same as its conn */
|
||||
struct hnb_context {
|
||||
/*! Entry in HNB-global list of HNB */
|
||||
struct llist_head list;
|
||||
/*! SCTP socket + write queue for Iuh to this specific HNB */
|
||||
struct osmo_stream_srv *conn;
|
||||
/*! copied from HNB-Identity-Info IE */
|
||||
char identity_info[256];
|
||||
/*! copied from Cell Identity IE */
|
||||
struct umts_cell_id id;
|
||||
|
||||
/*! SCTP stream ID for HNBAP */
|
||||
uint16_t hnbap_stream;
|
||||
/*! SCTP stream ID for RUA */
|
||||
uint16_t rua_stream;
|
||||
|
||||
/*! True if a HNB-REGISTER-REQ from this HNB has been accepted. */
|
||||
bool hnb_registered;
|
||||
|
||||
/* linked list of hnbgw_context_map */
|
||||
struct llist_head map_list;
|
||||
|
||||
/*! pointer to the associated hnb persistent state. Always present after HNB-Register */
|
||||
struct hnb_persistent *persistent;
|
||||
};
|
||||
|
||||
|
||||
int hnbgw_rua_accept_cb(struct osmo_stream_srv_link *srv, int fd);
|
||||
int hnb_ctrl_cmds_install(void);
|
||||
int hnb_ctrl_node_lookup(void *data, vector vline, int *node_type, void **node_data, int *i);
|
||||
|
||||
struct hnb_context *hnb_context_by_identity_info(const char *identity_info);
|
||||
const char *hnb_context_name(struct hnb_context *ctx);
|
||||
|
||||
void hnb_context_release(struct hnb_context *ctx);
|
||||
void hnb_context_release_ue_state(struct hnb_context *ctx);
|
||||
|
||||
unsigned long long hnb_get_updowntime(const struct hnb_context *ctx);
|
||||
void hnb_store_rab_durations(struct hnb_context *hnb);
|
193
include/osmocom/hnbgw/hnb_persistent.h
Normal file
193
include/osmocom/hnbgw/hnb_persistent.h
Normal file
@@ -0,0 +1,193 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/hashtable.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ranap/RANAP_CN-DomainIndicator.h>
|
||||
|
||||
#define DEBUG
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_pool.h>
|
||||
|
||||
#include <osmocom/hnbgw/umts_cell_id.h>
|
||||
#include <osmocom/hnbgw/nft_kpi.h>
|
||||
#include <osmocom/hnbgw/cnlink.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
|
||||
#define LOG_HNBP(HNBP, lvl, fmt, args...) \
|
||||
LOGP(DHNB, lvl, "(%s) " fmt, \
|
||||
(HNBP) ? \
|
||||
(((HNBP)->id_str && *(HNBP)->id_str) ? (HNBP)->id_str : "no-cell-id") \
|
||||
: "null", ## args)
|
||||
|
||||
|
||||
enum hnb_rate_ctr {
|
||||
HNB_CTR_IUH_ESTABLISHED,
|
||||
HNB_CTR_RANAP_PS_ERR_IND_UL,
|
||||
HNB_CTR_RANAP_CS_ERR_IND_UL,
|
||||
HNB_CTR_RANAP_PS_RESET_REQ_UL,
|
||||
HNB_CTR_RANAP_CS_RESET_REQ_UL,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_REQ,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_REQ,
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_REQ_UNEXP,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_REQ_UNEXP,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_CNF,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_CNF,
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_CNF_UNEXP,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_CNF_UNEXP,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_FAIL,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_FAIL,
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_FAIL_UNEXP,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_FAIL_UNEXP,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_MOD_REQ,
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_REQ,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_MOD_CNF,
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_CNF,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_MOD_FAIL,
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_FAIL,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_REQ,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ,
|
||||
HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ_ABNORMAL,
|
||||
HNB_CTR_RANAP_PS_RAB_REL_REQ_UNEXP,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ_UNEXP,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_CNF,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_CNF,
|
||||
HNB_CTR_RANAP_PS_RAB_REL_CNF_UNEXP,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_CNF_UNEXP,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_FAIL,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_FAIL,
|
||||
HNB_CTR_RANAP_PS_RAB_REL_FAIL_UNEXP,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_FAIL_UNEXP,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT,
|
||||
HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT_ABNORMAL,
|
||||
|
||||
HNB_CTR_RUA_ERR_IND,
|
||||
|
||||
HNB_CTR_RUA_PS_CONNECT_UL,
|
||||
HNB_CTR_RUA_CS_CONNECT_UL,
|
||||
|
||||
HNB_CTR_RUA_PS_DISCONNECT_UL,
|
||||
HNB_CTR_RUA_CS_DISCONNECT_UL,
|
||||
HNB_CTR_RUA_PS_DISCONNECT_DL,
|
||||
HNB_CTR_RUA_CS_DISCONNECT_DL,
|
||||
|
||||
HNB_CTR_RUA_PS_DT_UL,
|
||||
HNB_CTR_RUA_CS_DT_UL,
|
||||
HNB_CTR_RUA_PS_DT_DL,
|
||||
HNB_CTR_RUA_CS_DT_DL,
|
||||
|
||||
HNB_CTR_RUA_UDT_UL,
|
||||
HNB_CTR_RUA_UDT_DL,
|
||||
|
||||
HNB_CTR_PS_PAGING_ATTEMPTED,
|
||||
HNB_CTR_CS_PAGING_ATTEMPTED,
|
||||
|
||||
HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL,
|
||||
|
||||
HNB_CTR_DTAP_CS_LU_REQ,
|
||||
HNB_CTR_DTAP_CS_LU_ACC,
|
||||
HNB_CTR_DTAP_CS_LU_REJ,
|
||||
|
||||
HNB_CTR_DTAP_PS_ATT_REQ,
|
||||
HNB_CTR_DTAP_PS_ATT_ACK,
|
||||
HNB_CTR_DTAP_PS_ATT_REJ,
|
||||
|
||||
HNB_CTR_DTAP_PS_RAU_REQ,
|
||||
HNB_CTR_DTAP_PS_RAU_ACK,
|
||||
HNB_CTR_DTAP_PS_RAU_REJ,
|
||||
|
||||
HNB_CTR_GTPU_PACKETS_UL,
|
||||
HNB_CTR_GTPU_TOTAL_BYTES_UL,
|
||||
HNB_CTR_GTPU_UE_BYTES_UL,
|
||||
HNB_CTR_GTPU_PACKETS_DL,
|
||||
HNB_CTR_GTPU_TOTAL_BYTES_DL,
|
||||
HNB_CTR_GTPU_UE_BYTES_DL,
|
||||
};
|
||||
|
||||
enum hnb_stat {
|
||||
HNB_STAT_UPTIME_SECONDS,
|
||||
};
|
||||
|
||||
#define HNBP_CTR(hnbp, x) rate_ctr_group_get_ctr((hnbp)->ctrs, x)
|
||||
#define HNBP_CTR_INC(hnbp, x) rate_ctr_inc(HNBP_CTR(hnbp, x))
|
||||
#define HNBP_CTR_ADD(hnbp, x, y) rate_ctr_add2((hnbp)->ctrs, x, y)
|
||||
|
||||
#define HNBP_STAT(hbp, x) osmo_stat_item_group_get_item((hnbp)->statg, x)
|
||||
#define HNBP_STAT_SET(hnbp, x, val) osmo_stat_item_set(HNBP_STAT(hnbp, x), val)
|
||||
|
||||
/* persistent data for one HNB. This continues to exist even as conn / hnb_context is deleted on disconnect */
|
||||
struct hnb_persistent {
|
||||
/*! Entry in HNBGW-global list of hnb_persistent */
|
||||
struct llist_head list;
|
||||
/*! Entry in hash table g_hnbgw->hnb_persistent_by_id. */
|
||||
struct hlist_node node_by_id;
|
||||
/*! back-pointer to hnb_context. Can be NULL if no context at this point */
|
||||
struct hnb_context *ctx;
|
||||
|
||||
/*! unique cell identity; copied from HNB REGISTER REQ */
|
||||
struct umts_cell_id id;
|
||||
/*! stringified version of the cell identiy above (for printing/naming) */
|
||||
const char *id_str;
|
||||
|
||||
/*! copied from HNB-Identity-Info IE */
|
||||
time_t updowntime;
|
||||
|
||||
struct rate_ctr_group *ctrs;
|
||||
struct osmo_stat_item_group *statg;
|
||||
|
||||
/* State that the main thread needs in order to know what was requested from the nft worker threads and what
|
||||
* still needs to be requested. */
|
||||
struct {
|
||||
/* Whether a persistent named counter was added in nftables for this cell id. */
|
||||
bool persistent_counter_added;
|
||||
|
||||
/* The last hNodeB GTP-U address we asked the nft maintenance thread to set up.
|
||||
* osmo_sockaddr_str_is_nonzero(addr_remote) == false when no rules were added yet, and when
|
||||
* we asked the nft maintenance thread to remove the rules for this hNodeB because it has
|
||||
* disconnected. */
|
||||
struct osmo_sockaddr_str addr_remote;
|
||||
|
||||
/* the nft handles needed to clean up the UL and DL rules when the hNodeB disconnects,
|
||||
* and the last seen counter value gotten from nft. */
|
||||
struct {
|
||||
struct nft_kpi_handle h;
|
||||
struct nft_kpi_val v;
|
||||
} ul;
|
||||
struct {
|
||||
struct nft_kpi_handle h;
|
||||
struct nft_kpi_val v;
|
||||
} dl;
|
||||
} nft_kpi;
|
||||
|
||||
struct osmo_timer_list disconnected_timeout;
|
||||
};
|
||||
|
||||
struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id);
|
||||
struct hnb_persistent *hnb_persistent_find_by_id(const struct umts_cell_id *id);
|
||||
void hnb_persistent_registered(struct hnb_persistent *hnbp);
|
||||
void hnb_persistent_deregistered(struct hnb_persistent *hnbp);
|
||||
void hnb_persistent_free(struct hnb_persistent *hnbp);
|
||||
|
||||
unsigned long long hnbp_get_updowntime(const struct hnb_persistent *hnbp);
|
@@ -8,7 +8,6 @@
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ranap/RANAP_CN-DomainIndicator.h>
|
||||
@@ -20,9 +19,10 @@
|
||||
#include <osmocom/mgcp_client/mgcp_client_pool.h>
|
||||
|
||||
#include <osmocom/hnbgw/nft_kpi.h>
|
||||
#include <osmocom/hnbgw/cnlink.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
|
||||
#define STORE_UPTIME_INTERVAL 10 /* seconds */
|
||||
#define HNB_STORE_RAB_DURATIONS_INTERVAL 1 /* seconds */
|
||||
|
||||
enum {
|
||||
DMAIN,
|
||||
@@ -38,15 +38,6 @@ enum {
|
||||
extern const struct log_info hnbgw_log_info;
|
||||
extern struct vty_app_info hnbgw_vty_info;
|
||||
|
||||
#define LOGHNB(HNB_CTX, ss, lvl, fmt, args ...) \
|
||||
LOGP(ss, lvl, "(%s) " fmt, hnb_context_name(HNB_CTX), ## args)
|
||||
|
||||
#define LOG_HNBP(HNBP, lvl, fmt, args...) \
|
||||
LOGP(DHNB, lvl, "(%s) " fmt, \
|
||||
(HNBP) ? \
|
||||
(((HNBP)->id_str && *(HNBP)->id_str) ? (HNBP)->id_str : "no-cell-id") \
|
||||
: "null", ## args)
|
||||
|
||||
#define DOMAIN_CS RANAP_CN_DomainIndicator_cs_domain
|
||||
#define DOMAIN_PS RANAP_CN_DomainIndicator_ps_domain
|
||||
|
||||
@@ -56,11 +47,6 @@ static inline const char *ranap_domain_name(RANAP_CN_DomainIndicator_t domain)
|
||||
return get_value_string(ranap_domain_names, domain);
|
||||
}
|
||||
|
||||
enum hnb_ctrl_node {
|
||||
CTRL_NODE_HNB = _LAST_CTRL_NODE,
|
||||
_LAST_CTRL_NODE_HNB
|
||||
};
|
||||
|
||||
#define HNBGW_LOCAL_IP_DEFAULT "0.0.0.0"
|
||||
/* TODO: CS and PS now both connect to OsmoSTP, i.e. that's always going to be the same address. Drop the
|
||||
* duplicity. */
|
||||
@@ -70,6 +56,8 @@ enum hnb_ctrl_node {
|
||||
#define DEFAULT_PC_HNBGW ((23 << 3) + 5)
|
||||
#define DEFAULT_PC_MSC ((23 << 3) + 1)
|
||||
#define DEFAULT_PC_SGSN ((23 << 3) + 4)
|
||||
#define DEFAULT_ADDR_NAME_MSC "addr-dyn-msc-default"
|
||||
#define DEFAULT_ADDR_NAME_SGSN "addr-dyn-sgsn-default"
|
||||
|
||||
/* 25.467 Section 7.1 */
|
||||
#define IUH_DEFAULT_SCTP_PORT 29169
|
||||
@@ -83,238 +71,8 @@ enum hnb_ctrl_node {
|
||||
|
||||
#define IUH_MSGB_SIZE 2048
|
||||
|
||||
enum hnb_rate_ctr {
|
||||
HNB_CTR_IUH_ESTABLISHED,
|
||||
HNB_CTR_RANAP_PS_ERR_IND_UL,
|
||||
HNB_CTR_RANAP_CS_ERR_IND_UL,
|
||||
HNB_CTR_RANAP_PS_RESET_REQ_UL,
|
||||
HNB_CTR_RANAP_CS_RESET_REQ_UL,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_REQ,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_REQ,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_CNF,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_CNF,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_FAIL,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_FAIL,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_MOD_REQ,
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_REQ,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_MOD_CNF,
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_CNF,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_MOD_FAIL,
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_FAIL,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_REQ,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ,
|
||||
HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ_ABNORMAL,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_CNF,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_CNF,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_FAIL,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_FAIL,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT,
|
||||
HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT_ABNORMAL,
|
||||
|
||||
HNB_CTR_RUA_ERR_IND,
|
||||
|
||||
HNB_CTR_RUA_PS_CONNECT_UL,
|
||||
HNB_CTR_RUA_CS_CONNECT_UL,
|
||||
|
||||
HNB_CTR_RUA_PS_DISCONNECT_UL,
|
||||
HNB_CTR_RUA_CS_DISCONNECT_UL,
|
||||
HNB_CTR_RUA_PS_DISCONNECT_DL,
|
||||
HNB_CTR_RUA_CS_DISCONNECT_DL,
|
||||
|
||||
HNB_CTR_RUA_PS_DT_UL,
|
||||
HNB_CTR_RUA_CS_DT_UL,
|
||||
HNB_CTR_RUA_PS_DT_DL,
|
||||
HNB_CTR_RUA_CS_DT_DL,
|
||||
|
||||
HNB_CTR_RUA_UDT_UL,
|
||||
HNB_CTR_RUA_UDT_DL,
|
||||
|
||||
HNB_CTR_PS_PAGING_ATTEMPTED,
|
||||
HNB_CTR_CS_PAGING_ATTEMPTED,
|
||||
|
||||
HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL,
|
||||
|
||||
HNB_CTR_DTAP_CS_LU_REQ,
|
||||
HNB_CTR_DTAP_CS_LU_ACC,
|
||||
HNB_CTR_DTAP_CS_LU_REJ,
|
||||
|
||||
HNB_CTR_DTAP_PS_ATT_REQ,
|
||||
HNB_CTR_DTAP_PS_ATT_ACK,
|
||||
HNB_CTR_DTAP_PS_ATT_REJ,
|
||||
|
||||
HNB_CTR_DTAP_PS_RAU_REQ,
|
||||
HNB_CTR_DTAP_PS_RAU_ACK,
|
||||
HNB_CTR_DTAP_PS_RAU_REJ,
|
||||
|
||||
HNB_CTR_GTPU_PACKETS_UL,
|
||||
HNB_CTR_GTPU_TOTAL_BYTES_UL,
|
||||
HNB_CTR_GTPU_UE_BYTES_UL,
|
||||
HNB_CTR_GTPU_PACKETS_DL,
|
||||
HNB_CTR_GTPU_TOTAL_BYTES_DL,
|
||||
HNB_CTR_GTPU_UE_BYTES_DL,
|
||||
};
|
||||
|
||||
enum hnb_stat {
|
||||
HNB_STAT_UPTIME_SECONDS,
|
||||
};
|
||||
|
||||
struct umts_cell_id {
|
||||
struct osmo_plmn_id plmn; /*!< Mobile Country Code and Mobile Network Code (000-00 to 999-999) */
|
||||
uint16_t lac; /*!< Locaton Area Code (1-65534) */
|
||||
uint8_t rac; /*!< Routing Area Code (0-255) */
|
||||
uint16_t sac; /*!< Service Area Code */
|
||||
uint32_t cid; /*!< Cell ID */
|
||||
};
|
||||
int umts_cell_id_to_str_buf(char *buf, size_t buflen, const struct umts_cell_id *ucid);
|
||||
char *umts_cell_id_to_str_c(void *ctx, const struct umts_cell_id *ucid);
|
||||
const char *umts_cell_id_to_str(const struct umts_cell_id *ucid);
|
||||
int umts_cell_id_from_str(struct umts_cell_id *ucid, const char *instr);
|
||||
uint32_t umts_cell_id_hash(const struct umts_cell_id *ucid);
|
||||
|
||||
/*! are both given umts_cell_id euqal? */
|
||||
static inline bool umts_cell_id_equal(const struct umts_cell_id *a, const struct umts_cell_id *b)
|
||||
{
|
||||
if (osmo_plmn_cmp(&a->plmn, &b->plmn))
|
||||
return false;
|
||||
if (a->lac != b->lac)
|
||||
return false;
|
||||
if (a->rac != b->rac)
|
||||
return false;
|
||||
if (a->sac != b->sac)
|
||||
return false;
|
||||
if (a->cid != b->cid)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct hnbgw_context_map;
|
||||
|
||||
/* osmo-hnbgw keeps a single hnbgw_sccp_user per osmo_sccp_instance, for the local point-code and SSN == RANAP.
|
||||
* This relates the (opaque) osmo_sccp_user to osmo-hnbgw's per-ss7 state. */
|
||||
struct hnbgw_sccp_user {
|
||||
/* entry in g_hnbgw->sccp.users */
|
||||
struct llist_head entry;
|
||||
|
||||
/* logging context */
|
||||
char *name;
|
||||
|
||||
/* Which 'cs7 instance' is this for? Below sccp_user is registered at the osmo_sccp_instance ss7->sccp. */
|
||||
struct osmo_ss7_instance *ss7;
|
||||
|
||||
/* Local address: cs7 instance's primary PC if present, else the default HNBGW PC; with SSN == RANAP. */
|
||||
struct osmo_sccp_addr local_addr;
|
||||
|
||||
/* osmo_sccp API state for above local address on above ss7 instance. */
|
||||
struct osmo_sccp_user *sccp_user;
|
||||
|
||||
/* Fast access to the hnbgw_context_map responsible for a given SCCP conn_id of the ss7->sccp instance.
|
||||
* hlist_node: hnbgw_context_map->hnbgw_sccp_user_entry. */
|
||||
DECLARE_HASHTABLE(hnbgw_context_map_by_conn_id, 6);
|
||||
};
|
||||
|
||||
#define LOG_HSI(HNBGW_SCCP_INST, SUBSYS, LEVEL, FMT, ARGS...) \
|
||||
LOGP(SUBSYS, LEVEL, "(%s) " FMT, (HNBGW_SCCP_INST) ? (HNBGW_SCCP_INST)->name : "null", ##ARGS)
|
||||
|
||||
/* User provided configuration for struct hnbgw_cnpool. */
|
||||
struct hnbgw_cnpool_cfg {
|
||||
uint8_t nri_bitlen;
|
||||
struct osmo_nri_ranges *null_nri_ranges;
|
||||
};
|
||||
|
||||
/* User provided configuration for struct hnbgw_cnlink. */
|
||||
struct hnbgw_cnlink_cfg {
|
||||
/* cs7 address book entry to indicate both the remote point-code of the peer, as well as which cs7 instance to
|
||||
* use. */
|
||||
char *remote_addr_name;
|
||||
|
||||
struct osmo_nri_ranges *nri_ranges;
|
||||
};
|
||||
|
||||
/* Collection of CN peers to distribute UE connections across. MSCs for DOMAIN_CS, SGSNs for DOMAIN_PS. */
|
||||
struct hnbgw_cnpool {
|
||||
RANAP_CN_DomainIndicator_t domain;
|
||||
|
||||
/* CN pool string used in VTY config and logging, "iucs" or "iups". */
|
||||
const char *pool_name;
|
||||
/* CN peer string used in VTY config and logging, "msc" or "sgsn". */
|
||||
const char *peer_name;
|
||||
/* What we use as the remote MSC/SGSN point-code if the user does not configure any address. */
|
||||
uint32_t default_remote_pc;
|
||||
|
||||
struct hnbgw_cnpool_cfg vty;
|
||||
struct hnbgw_cnpool_cfg use;
|
||||
|
||||
/* List of struct hnbgw_cnlink */
|
||||
struct llist_head cnlinks;
|
||||
|
||||
unsigned int round_robin_next_nr;
|
||||
/* Emergency calls potentially select a different set of MSCs, so to not mess up the normal round-robin
|
||||
* behavior, emergency calls need a separate round-robin counter. */
|
||||
unsigned int round_robin_next_emerg_nr;
|
||||
|
||||
/* rate counter group that child hnbgw_cnlinks should use (points to msc_ctrg_desc or sgsn_ctrg_desc) */
|
||||
const struct rate_ctr_group_desc *cnlink_ctrg_desc;
|
||||
|
||||
/* Running counters for this pool */
|
||||
struct rate_ctr_group *ctrs;
|
||||
};
|
||||
|
||||
#define CNPOOL_CTR_INC(cnpool, x) rate_ctr_inc2((cnpool)->ctrs, x)
|
||||
|
||||
/* A CN peer, like 'msc 0' or 'sgsn 23' */
|
||||
struct hnbgw_cnlink {
|
||||
struct llist_head entry;
|
||||
|
||||
/* backpointer to CS or PS CN pool. */
|
||||
struct hnbgw_cnpool *pool;
|
||||
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
int nr;
|
||||
|
||||
struct hnbgw_cnlink_cfg vty;
|
||||
struct hnbgw_cnlink_cfg use;
|
||||
|
||||
/* To print in logging/VTY */
|
||||
char *name;
|
||||
|
||||
/* Copy of the address book entry use.remote_addr_name. */
|
||||
struct osmo_sccp_addr remote_addr;
|
||||
|
||||
/* The SCCP instance for the cs7 instance indicated by remote_addr_name. (Multiple hnbgw_cnlinks may use the
|
||||
* same hnbgw_sccp_user -- there is exactly one hnbgw_sccp_user per configured cs7 instance.) */
|
||||
struct hnbgw_sccp_user *hnbgw_sccp_user;
|
||||
|
||||
/* linked list of hnbgw_context_map */
|
||||
struct llist_head map_list;
|
||||
|
||||
bool allow_attach;
|
||||
bool allow_emerg;
|
||||
struct llist_head paging;
|
||||
|
||||
struct rate_ctr_group *ctrs;
|
||||
};
|
||||
|
||||
#define LOG_CNLINK(CNLINK, SUBSYS, LEVEL, FMT, ARGS...) \
|
||||
LOGP(SUBSYS, LEVEL, "(%s) " FMT, (CNLINK) ? (CNLINK)->name : "null", ##ARGS)
|
||||
|
||||
#define CNLINK_CTR_INC(cnlink, x) rate_ctr_inc2((cnlink)->ctrs, x)
|
||||
|
||||
struct hnbgw_cnlink *cnlink_get_nr(struct hnbgw_cnpool *cnpool, int nr, bool create_if_missing);
|
||||
|
||||
static inline bool cnlink_is_cs(const struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
return cnlink && cnlink->pool->domain == DOMAIN_CS;
|
||||
@@ -325,97 +83,6 @@ static inline bool cnlink_is_ps(const struct hnbgw_cnlink *cnlink)
|
||||
return cnlink && cnlink->pool->domain == DOMAIN_PS;
|
||||
}
|
||||
|
||||
static inline struct osmo_sccp_instance *cnlink_sccp(const struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
if (!cnlink)
|
||||
return NULL;
|
||||
if (!cnlink->hnbgw_sccp_user)
|
||||
return NULL;
|
||||
if (!cnlink->hnbgw_sccp_user->ss7)
|
||||
return NULL;
|
||||
return osmo_ss7_get_sccp(cnlink->hnbgw_sccp_user->ss7);
|
||||
}
|
||||
|
||||
/* The lifecycle of the hnb_context object is the same as its conn */
|
||||
struct hnb_context {
|
||||
/*! Entry in HNB-global list of HNB */
|
||||
struct llist_head list;
|
||||
/*! SCTP socket + write queue for Iuh to this specific HNB */
|
||||
struct osmo_stream_srv *conn;
|
||||
/*! copied from HNB-Identity-Info IE */
|
||||
char identity_info[256];
|
||||
/*! copied from Cell Identity IE */
|
||||
struct umts_cell_id id;
|
||||
|
||||
/*! SCTP stream ID for HNBAP */
|
||||
uint16_t hnbap_stream;
|
||||
/*! SCTP stream ID for RUA */
|
||||
uint16_t rua_stream;
|
||||
|
||||
/*! True if a HNB-REGISTER-REQ from this HNB has been accepted. */
|
||||
bool hnb_registered;
|
||||
|
||||
/* linked list of hnbgw_context_map */
|
||||
struct llist_head map_list;
|
||||
|
||||
/*! pointer to the associated hnb persistent state. Always present after HNB-Register */
|
||||
struct hnb_persistent *persistent;
|
||||
};
|
||||
|
||||
#define HNBP_CTR(hnbp, x) rate_ctr_group_get_ctr((hnbp)->ctrs, x)
|
||||
#define HNBP_CTR_INC(hnbp, x) rate_ctr_inc(HNBP_CTR(hnbp, x))
|
||||
#define HNBP_CTR_ADD(hnbp, x, y) rate_ctr_add2((hnbp)->ctrs, x, y)
|
||||
|
||||
#define HNBP_STAT(hbp, x) osmo_stat_item_group_get_item((hnbp)->statg, x)
|
||||
#define HNBP_STAT_SET(hnbp, x, val) osmo_stat_item_set(HNBP_STAT(hnbp, x), val)
|
||||
|
||||
/* persistent data for one HNB. This continues to exist even as conn / hnb_context is deleted on disconnect */
|
||||
struct hnb_persistent {
|
||||
/*! Entry in HNBGW-global list of hnb_persistent */
|
||||
struct llist_head list;
|
||||
/*! Entry in hash table g_hnbgw->hnb_persistent_by_id. */
|
||||
struct hlist_node node_by_id;
|
||||
/*! back-pointer to hnb_context. Can be NULL if no context at this point */
|
||||
struct hnb_context *ctx;
|
||||
|
||||
/*! unique cell identity; copied from HNB REGISTER REQ */
|
||||
struct umts_cell_id id;
|
||||
/*! stringified version of the cell identiy above (for printing/naming) */
|
||||
const char *id_str;
|
||||
|
||||
/*! copied from HNB-Identity-Info IE */
|
||||
time_t updowntime;
|
||||
|
||||
struct rate_ctr_group *ctrs;
|
||||
struct osmo_stat_item_group *statg;
|
||||
|
||||
/* State that the main thread needs in order to know what was requested from the nft worker threads and what
|
||||
* still needs to be requested. */
|
||||
struct {
|
||||
/* Whether a persistent named counter was added in nftables for this cell id. */
|
||||
bool persistent_counter_added;
|
||||
|
||||
/* The last hNodeB GTP-U address we asked the nft maintenance thread to set up.
|
||||
* osmo_sockaddr_str_is_nonzero(addr_remote) == false when no rules were added yet, and when
|
||||
* we asked the nft maintenance thread to remove the rules for this hNodeB because it has
|
||||
* disconnected. */
|
||||
struct osmo_sockaddr_str addr_remote;
|
||||
|
||||
/* the nft handles needed to clean up the UL and DL rules when the hNodeB disconnects,
|
||||
* and the last seen counter value gotten from nft. */
|
||||
struct {
|
||||
struct nft_kpi_handle h;
|
||||
struct nft_kpi_val v;
|
||||
} ul;
|
||||
struct {
|
||||
struct nft_kpi_handle h;
|
||||
struct nft_kpi_val v;
|
||||
} dl;
|
||||
} nft_kpi;
|
||||
|
||||
struct osmo_timer_list disconnected_timeout;
|
||||
};
|
||||
|
||||
struct hnbgw {
|
||||
struct {
|
||||
const char *iuh_local_ip;
|
||||
@@ -468,9 +135,9 @@ struct hnbgw {
|
||||
struct llist_head users;
|
||||
|
||||
/* Pool of core network peers: MSCs for IuCS */
|
||||
struct hnbgw_cnpool cnpool_iucs;
|
||||
struct hnbgw_cnpool *cnpool_iucs;
|
||||
/* Pool of core network peers: SGSNs for IuPS */
|
||||
struct hnbgw_cnpool cnpool_iups;
|
||||
struct hnbgw_cnpool *cnpool_iups;
|
||||
} sccp;
|
||||
/* MGW pool, also includes the single MGCP client as fallback if no
|
||||
* pool is configured. */
|
||||
@@ -498,22 +165,8 @@ extern void *talloc_asn1_ctx;
|
||||
void g_hnbgw_alloc(void *ctx);
|
||||
|
||||
int hnbgw_rua_accept_cb(struct osmo_stream_srv_link *srv, int fd);
|
||||
int hnb_ctrl_cmds_install(void);
|
||||
int hnb_ctrl_node_lookup(void *data, vector vline, int *node_type, void **node_data, int *i);
|
||||
int hnbgw_mgw_setup(void);
|
||||
|
||||
struct hnb_context *hnb_context_by_identity_info(const char *identity_info);
|
||||
const char *hnb_context_name(struct hnb_context *ctx);
|
||||
|
||||
void hnb_context_release(struct hnb_context *ctx);
|
||||
void hnb_context_release_ue_state(struct hnb_context *ctx);
|
||||
|
||||
struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id);
|
||||
struct hnb_persistent *hnb_persistent_find_by_id(const struct umts_cell_id *id);
|
||||
void hnb_persistent_registered(struct hnb_persistent *hnbp);
|
||||
void hnb_persistent_deregistered(struct hnb_persistent *hnbp);
|
||||
void hnb_persistent_free(struct hnb_persistent *hnbp);
|
||||
|
||||
void hnbgw_vty_init(void);
|
||||
int hnbgw_vty_go_parent(struct vty *vty);
|
||||
|
||||
@@ -531,6 +184,4 @@ struct msgb *hnbgw_ranap_msg_alloc(const char *name);
|
||||
|
||||
int hnbgw_peek_l3_ul(struct hnbgw_context_map *map, struct msgb *ranap_msg);
|
||||
|
||||
unsigned long long hnb_get_updowntime(const struct hnb_context *ctx);
|
||||
|
||||
uint32_t get_next_ue_ctx_id(void);
|
||||
|
@@ -1,32 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/ranap/RANAP_CN-DomainIndicator.h>
|
||||
|
||||
struct hnbgw_cnlink *cnlink_alloc(struct hnbgw_cnpool *cnpool, int nr);
|
||||
struct hnbgw_cnlink *hnbgw_cnlink_find_by_addr(const struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_sccp_addr *remote_addr);
|
||||
struct hnbgw_cnlink *hnbgw_cnlink_select(struct hnbgw_context_map *map);
|
||||
|
||||
void hnbgw_cnpool_start(struct hnbgw_cnpool *cnpool);
|
||||
void hnbgw_cnpool_apply_cfg(struct hnbgw_cnpool *cnpool);
|
||||
void hnbgw_cnpool_cnlinks_start_or_restart(struct hnbgw_cnpool *cnpool);
|
||||
int hnbgw_cnlink_start_or_restart(struct hnbgw_cnlink *cnlink);
|
||||
|
||||
char *cnlink_sccp_addr_to_str(struct hnbgw_cnlink *cnlink, const struct osmo_sccp_addr *addr);
|
||||
|
||||
bool cnlink_is_conn_ready(const struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_rx_reset_cmd(struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_rx_reset_ack(struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_resend_reset(struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_set_disconnected(struct hnbgw_cnlink *cnlink);
|
||||
|
||||
const char *cnlink_paging_add_ranap(struct hnbgw_cnlink *cnlink, const RANAP_PagingIEs_t *paging_ies);
|
||||
struct hnbgw_cnlink *cnlink_find_by_paging_mi(struct hnbgw_cnpool *cnpool, const struct osmo_mobile_identity *mi);
|
||||
struct hnbgw_context_map;
|
||||
|
||||
enum hnbgw_cnpool_ctr {
|
||||
/* TODO: basic counters completely missing
|
||||
@@ -38,33 +19,46 @@ enum hnbgw_cnpool_ctr {
|
||||
CNPOOL_CTR_EMERG_FORWARDED,
|
||||
CNPOOL_CTR_EMERG_LOST,
|
||||
};
|
||||
#define CNPOOL_CTR_INC(cnpool, x) rate_ctr_inc2((cnpool)->ctrs, x)
|
||||
|
||||
extern const struct rate_ctr_group_desc iucs_ctrg_desc;
|
||||
extern const struct rate_ctr_group_desc iups_ctrg_desc;
|
||||
|
||||
enum hnbgw_cnlink_ctr {
|
||||
/* TODO: basic counters completely missing
|
||||
* ...
|
||||
*/
|
||||
CNLINK_CTR_RANAP_RX_UDT_RESET,
|
||||
CNLINK_CTR_RANAP_RX_UDT_RESET_ACK,
|
||||
CNLINK_CTR_RANAP_RX_UDT_PAGING,
|
||||
CNLINK_CTR_RANAP_RX_UDT_UNKNOWN,
|
||||
CNLINK_CTR_RANAP_RX_UDT_UNSUPPORTED,
|
||||
CNLINK_CTR_RANAP_RX_UDT_OVERLOAD_IND,
|
||||
CNLINK_CTR_RANAP_RX_UDT_ERROR_IND,
|
||||
|
||||
CNLINK_CTR_RANAP_TX_UDT_RESET,
|
||||
CNLINK_CTR_RANAP_TX_UDT_RESET_ACK,
|
||||
|
||||
/* Counters related to link selection from a CN pool. */
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_NEW,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_REATTACH,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_KNOWN,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_PAGED,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_ATTACH_LOST,
|
||||
CNLINK_CTR_CNPOOL_EMERG_FORWARDED,
|
||||
/* User provided configuration for struct hnbgw_cnpool. */
|
||||
struct hnbgw_cnpool_cfg {
|
||||
uint8_t nri_bitlen;
|
||||
struct osmo_nri_ranges *null_nri_ranges;
|
||||
};
|
||||
|
||||
extern const struct rate_ctr_group_desc msc_ctrg_desc;
|
||||
extern const struct rate_ctr_group_desc sgsn_ctrg_desc;
|
||||
/* Collection of CN peers to distribute UE connections across. MSCs for DOMAIN_CS, SGSNs for DOMAIN_PS. */
|
||||
struct hnbgw_cnpool {
|
||||
RANAP_CN_DomainIndicator_t domain;
|
||||
|
||||
/* CN pool string used in VTY config and logging, "iucs" or "iups". */
|
||||
const char *pool_name;
|
||||
/* CN peer string used in VTY config and logging, "msc" or "sgsn". */
|
||||
const char *peer_name;
|
||||
/* What we use as the remote MSC/SGSN point-code if the user does not configure any address. */
|
||||
uint32_t default_remote_pc;
|
||||
const char *default_addr_name;
|
||||
|
||||
struct hnbgw_cnpool_cfg vty;
|
||||
struct hnbgw_cnpool_cfg use;
|
||||
|
||||
/* List of struct hnbgw_cnlink */
|
||||
struct llist_head cnlinks;
|
||||
|
||||
unsigned int round_robin_next_nr;
|
||||
/* Emergency calls potentially select a different set of MSCs, so to not mess up the normal round-robin
|
||||
* behavior, emergency calls need a separate round-robin counter. */
|
||||
unsigned int round_robin_next_emerg_nr;
|
||||
|
||||
/* Running counters for this pool */
|
||||
struct rate_ctr_group *ctrs;
|
||||
};
|
||||
|
||||
struct hnbgw_cnpool *hnbgw_cnpool_alloc(RANAP_CN_DomainIndicator_t domain);
|
||||
|
||||
struct hnbgw_cnlink *hnbgw_cnlink_select(struct hnbgw_context_map *map);
|
||||
|
||||
void hnbgw_cnpool_start(struct hnbgw_cnpool *cnpool);
|
||||
void hnbgw_cnpool_cnlinks_start_or_restart(struct hnbgw_cnpool *cnpool);
|
||||
struct hnbgw_cnlink *cnlink_get_nr(struct hnbgw_cnpool *cnpool, int nr, bool create_if_missing);
|
||||
void hnbgw_cnpool_apply_cfg(struct hnbgw_cnpool *cnpool);
|
||||
|
@@ -3,5 +3,7 @@
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
|
||||
struct hnb_context;
|
||||
|
||||
int hnbgw_hnbap_rx(struct hnb_context *hnb, struct msgb *msg);
|
||||
int hnbgw_hnbap_init(void);
|
||||
|
@@ -4,6 +4,8 @@
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/rua/RUA_Cause.h>
|
||||
|
||||
struct hnb_context;
|
||||
|
||||
int hnbgw_rua_rx(struct hnb_context *hnb, struct msgb *msg);
|
||||
|
||||
int rua_tx_udt(struct hnb_context *hnb, const uint8_t *data, unsigned int len);
|
||||
|
66
include/osmocom/hnbgw/hnbgw_sccp.h
Normal file
66
include/osmocom/hnbgw/hnbgw_sccp.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* SCCP, ITU Q.711 - Q.714 */
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/hashtable.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/prim.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/use_count.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
struct hnbgw_cnlink;
|
||||
|
||||
/* osmo-hnbgw keeps a single hnbgw_sccp_user per osmo_sccp_instance, for the local point-code and SSN == RANAP.
|
||||
* This relates the (opaque) osmo_sccp_user to osmo-hnbgw's per-ss7 state. */
|
||||
struct hnbgw_sccp_user {
|
||||
/* entry in g_hnbgw->sccp.users */
|
||||
struct llist_head entry;
|
||||
|
||||
/* logging context */
|
||||
char *name;
|
||||
|
||||
/* Which 'cs7 instance' is this for? Below sccp_user is registered at the osmo_sccp_instance ss7->sccp. */
|
||||
struct osmo_ss7_instance *ss7;
|
||||
|
||||
/* Local address: cs7 instance's primary PC if present, else the default HNBGW PC; with SSN == RANAP. */
|
||||
struct osmo_sccp_addr local_addr;
|
||||
|
||||
/* osmo_sccp API state for above local address on above ss7 instance. */
|
||||
struct osmo_sccp_user *sccp_user;
|
||||
|
||||
/* Ref count of users of this struct, ie.referencing it in cnlink->hnbgw_sccp_user */
|
||||
struct osmo_use_count use_count;
|
||||
|
||||
/* Fast access to the hnbgw_context_map responsible for a given SCCP conn_id of the ss7->sccp instance.
|
||||
* hlist_node: hnbgw_context_map->hnbgw_sccp_user_entry. */
|
||||
DECLARE_HASHTABLE(hnbgw_context_map_by_conn_id, 6);
|
||||
};
|
||||
|
||||
#define LOG_HSU(HSU, SUBSYS, LEVEL, FMT, ARGS...) \
|
||||
LOGP(SUBSYS, LEVEL, "(%s) " FMT, (HSU) ? (HSU)->name : "null", ##ARGS)
|
||||
|
||||
#define HSU_USE_CNLINK "cnlink"
|
||||
#define hnbgw_sccp_user_get(hsu, use) \
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&(hsu)->use_count, use, 1) == 0)
|
||||
#define hnbgw_sccp_user_put(hsu, use) \
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&(hsu)->use_count, use, -1) == 0)
|
||||
|
||||
struct hnbgw_sccp_user *hnbgw_sccp_user_alloc(int ss7_inst_id);
|
||||
|
||||
int hnbgw_sccp_user_tx_unitdata_req(struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *called_addr,
|
||||
struct msgb *ranap_msg);
|
||||
int hnbgw_sccp_user_tx_connect_req(struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *called_addr,
|
||||
uint32_t scu_conn_id, struct msgb *ranap_msg);
|
||||
int hnbgw_sccp_user_tx_data_req(struct hnbgw_sccp_user *hsu, uint32_t scu_conn_id,
|
||||
struct msgb *ranap_msg);
|
||||
int hnbgw_sccp_user_tx_disconnect_req(struct hnbgw_sccp_user *hsu, uint32_t scu_conn_id);
|
||||
|
||||
static inline struct osmo_sccp_instance *hnbgw_sccp_user_get_sccp_instance(const struct hnbgw_sccp_user *hsu)
|
||||
{
|
||||
if (!hsu->ss7)
|
||||
return NULL;
|
||||
return osmo_ss7_get_sccp(hsu->ss7);
|
||||
}
|
@@ -6,6 +6,7 @@ enum ps_rab_ass_fsm_event {
|
||||
PS_RAB_ASS_EV_LOCAL_F_TEIDS_RX,
|
||||
PS_RAB_ASS_EV_RAB_ASS_RESP,
|
||||
PS_RAB_ASS_EV_RAB_ESTABLISHED,
|
||||
PS_RAB_ASS_EV_RAB_RELEASED,
|
||||
PS_RAB_ASS_EV_RAB_FAIL,
|
||||
};
|
||||
|
||||
|
@@ -85,7 +85,7 @@ struct ps_rab *ps_rab_start(struct hnbgw_context_map *map, uint8_t rab_id,
|
||||
|
||||
struct ps_rab *ps_rab_get(struct hnbgw_context_map *map, uint8_t rab_id);
|
||||
bool ps_rab_is_established(const struct ps_rab *rab);
|
||||
void ps_rab_release(struct ps_rab *rab);
|
||||
void ps_rab_release(struct ps_rab *rab, struct osmo_fsm_inst *notify_fi);
|
||||
|
||||
struct ps_rab_rx_args {
|
||||
struct addr_teid f_teid;
|
||||
|
34
include/osmocom/hnbgw/umts_cell_id.h
Normal file
34
include/osmocom/hnbgw/umts_cell_id.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
|
||||
struct umts_cell_id {
|
||||
struct osmo_plmn_id plmn; /*!< Mobile Country Code and Mobile Network Code (000-00 to 999-999) */
|
||||
uint16_t lac; /*!< Locaton Area Code (1-65534) */
|
||||
uint8_t rac; /*!< Routing Area Code (0-255) */
|
||||
uint16_t sac; /*!< Service Area Code */
|
||||
uint32_t cid; /*!< Cell ID */
|
||||
};
|
||||
int umts_cell_id_to_str_buf(char *buf, size_t buflen, const struct umts_cell_id *ucid);
|
||||
char *umts_cell_id_to_str_c(void *ctx, const struct umts_cell_id *ucid);
|
||||
const char *umts_cell_id_to_str(const struct umts_cell_id *ucid);
|
||||
int umts_cell_id_from_str(struct umts_cell_id *ucid, const char *instr);
|
||||
uint32_t umts_cell_id_hash(const struct umts_cell_id *ucid);
|
||||
|
||||
/*! are both given umts_cell_id euqal? */
|
||||
static inline bool umts_cell_id_equal(const struct umts_cell_id *a, const struct umts_cell_id *b)
|
||||
{
|
||||
if (osmo_plmn_cmp(&a->plmn, &b->plmn))
|
||||
return false;
|
||||
if (a->lac != b->lac)
|
||||
return false;
|
||||
if (a->rac != b->rac)
|
||||
return false;
|
||||
if (a->sac != b->sac)
|
||||
return false;
|
||||
if (a->cid != b->cid)
|
||||
return false;
|
||||
return true;
|
||||
}
|
@@ -32,23 +32,28 @@ noinst_LTLIBRARIES = \
|
||||
$(NULL)
|
||||
|
||||
libhnbgw_la_SOURCES = \
|
||||
hnb.c \
|
||||
hnb_persistent.c \
|
||||
hnbgw.c \
|
||||
hnbgw_hnbap.c \
|
||||
hnbgw_l3.c \
|
||||
hnbgw_rua.c \
|
||||
hnbgw_ranap.c \
|
||||
hnbgw_sccp.c \
|
||||
hnbgw_vty.c \
|
||||
context_map.c \
|
||||
context_map_rua.c \
|
||||
context_map_sccp.c \
|
||||
hnbgw_cn.c \
|
||||
cnlink.c \
|
||||
cnlink_fsm.c \
|
||||
cnlink_paging.c \
|
||||
ranap_rab_ass.c \
|
||||
mgw_fsm.c \
|
||||
kpi_dtap.c \
|
||||
kpi_ranap.c \
|
||||
tdefs.c \
|
||||
umts_cell_id.c \
|
||||
nft_kpi.c \
|
||||
$(NULL)
|
||||
|
||||
|
@@ -20,6 +20,8 @@
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
|
||||
#include <osmocom/gsm/gsm23236.h>
|
||||
|
||||
@@ -34,94 +36,255 @@
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
|
||||
static struct osmo_fsm cnlink_fsm;
|
||||
static const struct rate_ctr_desc cnlink_ctr_description[] = {
|
||||
[CNLINK_CTR_RANAP_RX_UDT_RESET] = {
|
||||
"ranap:rx:udt:reset",
|
||||
"RANAP Unitdata RESET messages received"
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_RESET_ACK] = {
|
||||
"ranap:rx:udt:reset_ack",
|
||||
"RANAP Unitdata RESET ACK messages received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_PAGING] = {
|
||||
"ranap:rx:udt:paging",
|
||||
"RANAP Unitdata PAGING messages received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_UNKNOWN] = {
|
||||
"ranap:rx:udt:unknown",
|
||||
"Unknown RANAP Unitdata messages received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_UNSUPPORTED] = {
|
||||
"ranap:rx:udt:unsupported",
|
||||
"Unsupported RANAP Unitdata messages received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_OVERLOAD_IND] = {
|
||||
"ranap:rx:udt:overload_ind",
|
||||
"RANAP Unitdata Overload Indications received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_ERROR_IND] = {
|
||||
"ranap:rx:udt:error_ind",
|
||||
"RANAP Unitdata Error Indications received",
|
||||
},
|
||||
|
||||
enum cnlink_fsm_state {
|
||||
CNLINK_ST_DISC,
|
||||
CNLINK_ST_CONN,
|
||||
[CNLINK_CTR_RANAP_TX_UDT_RESET] = {
|
||||
"ranap:tx:udt:reset",
|
||||
"RANAP Unitdata RESET messages transmitted",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_TX_UDT_RESET_ACK] = {
|
||||
"ranap:tx:udt:reset_ack",
|
||||
"RANAP Unitdata RESET ACK messages transmitted",
|
||||
},
|
||||
|
||||
/* SCCP Counters: */
|
||||
[CNLINK_CTR_SCCP_N_UNITDATA_REQ] = {
|
||||
"sccp:n_unit_data:req",
|
||||
"Submit SCCP N-UNITDATA.req (UL)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_UNITDATA_IND] = {
|
||||
"sccp:n_unit_data:ind",
|
||||
"Received SCCP N-UNITDATA.ind (DL)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_NOTICE_IND] = {
|
||||
"sccp:n_notice:ind",
|
||||
"Received SCCP N-NOTICE.ind"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_CONNECT_REQ] = {
|
||||
"sccp:n_connect:req",
|
||||
"Submit SCCP N-CONNECT.req (UL SCCP CR)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_CONNECT_CNF] = {
|
||||
"sccp:n_connect:cnf",
|
||||
"Received SCCP N-CONNECT.cnf (DL SCCP CC)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_DATA_REQ] = {
|
||||
"sccp:n_data:req",
|
||||
"SUBMIT SCCP N-DATA.req (UL)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_DATA_IND] = {
|
||||
"sccp:n_data:ind",
|
||||
"Received SCCP N-DATA.ind (DL)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_DISCONNECT_REQ] = {
|
||||
"sccp:n_disconnect:req",
|
||||
"Submit SCCP N-DISCONNECT.req (UL SCCP RLC)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_DISCONNECT_IND] = {
|
||||
"sccp:n_disconnect:ind",
|
||||
"Received SCCP N-DISCONNECT.ind (DL SCCP RLSD)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_PCSTATE_IND] = {
|
||||
"sccp:n_pcstate:ind",
|
||||
"Received SCCP N-PCSTATE.ind"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_RLSD_CN_ORIGIN] = {
|
||||
"sccp:rlsd_cn_origin",
|
||||
"Received unexpected SCCP RSLD originated unilaterally by CN"
|
||||
},
|
||||
|
||||
/* Indicators for CN pool usage */
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_NEW] = {
|
||||
"cnpool:subscr:new",
|
||||
"Complete Layer 3 requests assigned to this CN link by round-robin (no NRI was assigned yet).",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_REATTACH] = {
|
||||
"cnpool:subscr:reattach",
|
||||
"Complete Layer 3 requests assigned to this CN link by round-robin because the subscriber indicates a"
|
||||
" NULL-NRI (previously assigned by another CN link).",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_KNOWN] = {
|
||||
"cnpool:subscr:known",
|
||||
"Complete Layer 3 requests directed to this CN link because the subscriber indicates an NRI of this CN link.",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_PAGED] = {
|
||||
"cnpool:subscr:paged",
|
||||
"Paging Response directed to this CN link because the subscriber was recently paged by this CN link.",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_ATTACH_LOST] = {
|
||||
"cnpool:subscr:attach_lost",
|
||||
"A subscriber indicates an NRI value matching this CN link, but the CN link is not connected:"
|
||||
" a re-attach to another CN link (if available) was forced, with possible service failure.",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_EMERG_FORWARDED] = {
|
||||
"cnpool:emerg:forwarded",
|
||||
"Emergency call requests forwarded to this CN link.",
|
||||
},
|
||||
};
|
||||
|
||||
enum cnlink_fsm_event {
|
||||
CNLINK_EV_RX_RESET,
|
||||
CNLINK_EV_RX_RESET_ACK,
|
||||
static const struct rate_ctr_group_desc msc_ctrg_desc = {
|
||||
"msc",
|
||||
"MSC",
|
||||
OSMO_STATS_CLASS_GLOBAL,
|
||||
ARRAY_SIZE(cnlink_ctr_description),
|
||||
cnlink_ctr_description,
|
||||
};
|
||||
|
||||
static const struct value_string cnlink_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(CNLINK_EV_RX_RESET),
|
||||
OSMO_VALUE_STRING(CNLINK_EV_RX_RESET_ACK),
|
||||
{}
|
||||
static const struct rate_ctr_group_desc sgsn_ctrg_desc = {
|
||||
"sgsn",
|
||||
"SGSN",
|
||||
OSMO_STATS_CLASS_GLOBAL,
|
||||
ARRAY_SIZE(cnlink_ctr_description),
|
||||
cnlink_ctr_description,
|
||||
};
|
||||
|
||||
static const struct osmo_tdef_state_timeout cnlink_timeouts[32] = {
|
||||
[CNLINK_ST_DISC] = { .T = 4 },
|
||||
static const struct osmo_stat_item_desc cnlink_stat_desc[] = {
|
||||
[CNLINK_STAT_CONNECTED] = { "connected", "Connected (1) or disconnected (0)", NULL, 60, 0 },
|
||||
};
|
||||
|
||||
#define cnlink_fsm_state_chg(FI, STATE) \
|
||||
osmo_tdef_fsm_inst_state_chg(FI, STATE, \
|
||||
cnlink_timeouts, \
|
||||
hnbgw_T_defs, \
|
||||
-1)
|
||||
const struct osmo_stat_item_group_desc msc_statg_desc = {
|
||||
.group_name_prefix = "msc",
|
||||
.group_description = "MSC",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_items = ARRAY_SIZE(cnlink_stat_desc),
|
||||
.item_desc = cnlink_stat_desc,
|
||||
};
|
||||
|
||||
struct hnbgw_cnlink *cnlink_alloc(struct hnbgw_cnpool *cnpool, int nr)
|
||||
const struct osmo_stat_item_group_desc sgsn_statg_desc = {
|
||||
.group_name_prefix = "sgsn",
|
||||
.group_description = "SGSN",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_items = ARRAY_SIZE(cnlink_stat_desc),
|
||||
.item_desc = cnlink_stat_desc,
|
||||
};
|
||||
|
||||
struct hnbgw_cnlink *hnbgw_cnlink_alloc(struct hnbgw_cnpool *cnpool, int nr)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
const struct rate_ctr_group_desc *ctrg_desc;
|
||||
const struct osmo_stat_item_group_desc *statg_desc;
|
||||
|
||||
char *name = talloc_asprintf(OTC_SELECT, "%s-%d", cnpool->peer_name, nr);
|
||||
OSMO_ASSERT(cnpool);
|
||||
|
||||
fi = osmo_fsm_inst_alloc(&cnlink_fsm, g_hnbgw, NULL, LOGL_DEBUG, name);
|
||||
OSMO_ASSERT(fi);
|
||||
cnlink = talloc_zero(g_hnbgw, struct hnbgw_cnlink);
|
||||
fi->priv = cnlink;
|
||||
switch (cnpool->domain) {
|
||||
case DOMAIN_CS:
|
||||
ctrg_desc = &msc_ctrg_desc;
|
||||
statg_desc = &msc_statg_desc;
|
||||
break;
|
||||
case DOMAIN_PS:
|
||||
ctrg_desc = &sgsn_ctrg_desc;
|
||||
statg_desc = &sgsn_statg_desc;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
cnlink = talloc_zero(cnpool, struct hnbgw_cnlink);
|
||||
OSMO_ASSERT(cnlink);
|
||||
*cnlink = (struct hnbgw_cnlink){
|
||||
.name = name,
|
||||
.pool = cnpool,
|
||||
.fi = fi,
|
||||
.nr = nr,
|
||||
.vty = {
|
||||
/* VTY config defaults for the new cnlink */
|
||||
.nri_ranges = osmo_nri_ranges_alloc(cnlink),
|
||||
},
|
||||
.allow_attach = true,
|
||||
.ctrs = rate_ctr_group_alloc(g_hnbgw, cnpool->cnlink_ctrg_desc, nr),
|
||||
.ctrs = rate_ctr_group_alloc(cnlink, ctrg_desc, nr),
|
||||
.statg = osmo_stat_item_group_alloc(cnlink, statg_desc, nr),
|
||||
};
|
||||
talloc_steal(cnlink, name);
|
||||
cnlink->name = talloc_asprintf(cnlink, "%s-%d", cnpool->peer_name, nr);
|
||||
INIT_LLIST_HEAD(&cnlink->map_list);
|
||||
INIT_LLIST_HEAD(&cnlink->paging);
|
||||
|
||||
cnlink->fi = osmo_fsm_inst_alloc(&cnlink_fsm, cnlink, cnlink, LOGL_DEBUG, cnlink->name);
|
||||
OSMO_ASSERT(cnlink->fi);
|
||||
|
||||
llist_add_tail(&cnlink->entry, &cnpool->cnlinks);
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "allocated\n");
|
||||
|
||||
/* Immediately (1ms) kick off reset sending mechanism */
|
||||
osmo_fsm_inst_state_chg_ms(fi, CNLINK_ST_DISC, 1, 0);
|
||||
cnlink_resend_reset(cnlink);
|
||||
return cnlink;
|
||||
}
|
||||
|
||||
void cnlink_term_and_free(struct hnbgw_cnlink *cnlink)
|
||||
int hnbgw_cnlink_set_name(struct hnbgw_cnlink *cnlink, const char *name)
|
||||
{
|
||||
talloc_free(cnlink->name);
|
||||
cnlink->name = talloc_strdup(cnlink, name);
|
||||
osmo_fsm_inst_update_id_f_sanitize(cnlink->fi, '-', cnlink->name);
|
||||
/* Update rate_ctr/stats to report by name instead of index: */
|
||||
rate_ctr_group_set_name(cnlink->ctrs, cnlink->name);
|
||||
osmo_stat_item_group_set_name(cnlink->statg, cnlink->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hnbgw_cnlink_drop_sccp(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct hnbgw_context_map *map, *map2;
|
||||
struct hnbgw_sccp_user *hsu;
|
||||
|
||||
llist_for_each_entry_safe(map, map2, &cnlink->map_list, hnbgw_cnlink_entry) {
|
||||
map_sccp_dispatch(map, MAP_SCCP_EV_USER_ABORT, NULL);
|
||||
}
|
||||
|
||||
OSMO_ASSERT(cnlink->hnbgw_sccp_user);
|
||||
hsu = cnlink->hnbgw_sccp_user;
|
||||
cnlink->hnbgw_sccp_user = NULL;
|
||||
hnbgw_sccp_user_put(hsu, HSU_USE_CNLINK);
|
||||
}
|
||||
|
||||
void hnbgw_cnlink_term_and_free(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
if (!cnlink)
|
||||
return;
|
||||
|
||||
if (cnlink->hnbgw_sccp_user)
|
||||
hnbgw_cnlink_drop_sccp(cnlink);
|
||||
|
||||
osmo_fsm_inst_term(cnlink->fi, OSMO_FSM_TERM_REQUEST, NULL);
|
||||
cnlink->fi = NULL;
|
||||
osmo_stat_item_group_free(cnlink->statg);
|
||||
rate_ctr_group_free(cnlink->ctrs);
|
||||
llist_del(&cnlink->entry);
|
||||
talloc_free(cnlink);
|
||||
}
|
||||
|
||||
static void link_up(struct hnbgw_cnlink *cnlink)
|
||||
static int hnbgw_cnlink_tx_sccp_unitdata_req(struct hnbgw_cnlink *cnlink, struct msgb *msg)
|
||||
{
|
||||
LOGPFSML(cnlink->fi, LOGL_NOTICE, "link up\n");
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_SCCP_N_UNITDATA_REQ);
|
||||
return hnbgw_sccp_user_tx_unitdata_req(cnlink->hnbgw_sccp_user,
|
||||
&cnlink->remote_addr,
|
||||
msg);
|
||||
}
|
||||
|
||||
static void link_lost(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct hnbgw_context_map *map, *map2;
|
||||
|
||||
LOGPFSML(cnlink->fi, LOGL_NOTICE, "link lost\n");
|
||||
|
||||
llist_for_each_entry_safe(map, map2, &cnlink->map_list, hnbgw_cnlink_entry)
|
||||
context_map_cnlink_lost(map);
|
||||
}
|
||||
|
||||
static void tx_reset(struct hnbgw_cnlink *cnlink)
|
||||
int hnbgw_cnlink_tx_ranap_reset(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct msgb *msg;
|
||||
RANAP_Cause_t cause = {
|
||||
@@ -133,7 +296,7 @@ static void tx_reset(struct hnbgw_cnlink *cnlink)
|
||||
uint8_t plmn_buf[3];
|
||||
|
||||
if (!cnlink)
|
||||
return;
|
||||
return -1;
|
||||
|
||||
/* We need to have chosen an SCCP instance, and the remote SCCP address needs to be set.
|
||||
* Only check the remote_addr, allowing use.remote_addr_name to be NULL: if the user has not set an explicit
|
||||
@@ -141,12 +304,12 @@ static void tx_reset(struct hnbgw_cnlink *cnlink)
|
||||
if (!cnlink->hnbgw_sccp_user
|
||||
|| !osmo_sccp_check_addr(&cnlink->remote_addr, OSMO_SCCP_ADDR_T_PC | OSMO_SCCP_ADDR_T_SSN)) {
|
||||
LOG_CNLINK(cnlink, DRANAP, LOGL_DEBUG, "not yet configured, not sending RANAP RESET\n");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_CNLINK(cnlink, DRANAP, LOGL_DEBUG, "Tx RANAP RESET to %s %s\n",
|
||||
cnlink_is_cs(cnlink) ? "IuCS" : "IuPS",
|
||||
cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr));
|
||||
hnbgw_cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr));
|
||||
|
||||
if (g_hnbgw->config.plmn.mcc) {
|
||||
osmo_plmn_to_bcd(plmn_buf, &g_hnbgw->config.plmn);
|
||||
@@ -175,29 +338,26 @@ static void tx_reset(struct hnbgw_cnlink *cnlink)
|
||||
|
||||
msg = ranap_new_msg_reset2(cnlink->pool->domain, &cause, use_grnc_id);
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_TX_UDT_RESET);
|
||||
osmo_sccp_tx_unitdata_msg(cnlink->hnbgw_sccp_user->sccp_user,
|
||||
&cnlink->hnbgw_sccp_user->local_addr,
|
||||
&cnlink->remote_addr,
|
||||
msg);
|
||||
return hnbgw_cnlink_tx_sccp_unitdata_req(cnlink, msg);
|
||||
}
|
||||
|
||||
static void tx_reset_ack(struct hnbgw_cnlink *cnlink)
|
||||
int hnbgw_cnlink_tx_ranap_reset_ack(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmo_sccp_instance *sccp = cnlink_sccp(cnlink);
|
||||
struct osmo_sccp_instance *sccp = hnbgw_cnlink_sccp(cnlink);
|
||||
RANAP_GlobalRNC_ID_t grnc_id;
|
||||
RANAP_GlobalRNC_ID_t *use_grnc_id = NULL;
|
||||
uint8_t plmn_buf[3];
|
||||
|
||||
if (!sccp) {
|
||||
LOG_CNLINK(cnlink, DRANAP, LOGL_ERROR, "cannot send RANAP RESET ACK: no CN link\n");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_CNLINK(cnlink, DRANAP, LOGL_NOTICE, "Tx RANAP RESET ACK %s %s --> %s\n",
|
||||
cnlink_is_cs(cnlink) ? "IuCS" : "IuPS",
|
||||
cnlink_sccp_addr_to_str(cnlink, &cnlink->hnbgw_sccp_user->local_addr),
|
||||
cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr));
|
||||
hnbgw_cnlink_sccp_addr_to_str(cnlink, &cnlink->hnbgw_sccp_user->local_addr),
|
||||
hnbgw_cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr));
|
||||
|
||||
if (g_hnbgw->config.plmn.mcc) {
|
||||
osmo_plmn_to_bcd(plmn_buf, &g_hnbgw->config.plmn);
|
||||
@@ -226,149 +386,169 @@ static void tx_reset_ack(struct hnbgw_cnlink *cnlink)
|
||||
|
||||
msg = ranap_new_msg_reset_ack(cnlink->pool->domain, use_grnc_id);
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_TX_UDT_RESET_ACK);
|
||||
osmo_sccp_tx_unitdata_msg(cnlink->hnbgw_sccp_user->sccp_user,
|
||||
&cnlink->hnbgw_sccp_user->local_addr,
|
||||
&cnlink->remote_addr,
|
||||
msg);
|
||||
return hnbgw_cnlink_tx_sccp_unitdata_req(cnlink, msg);
|
||||
}
|
||||
|
||||
static void cnlink_disc_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
/* Return address found in sccp address-book, and fill in missing fields in the
|
||||
* entry with default values. */
|
||||
static struct osmo_ss7_instance *sccp_addr_by_name_filled(struct osmo_sccp_addr *dest, const char *addr_name, uint32_t default_pc)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
if (prev_state == CNLINK_ST_CONN)
|
||||
link_lost(cnlink);
|
||||
struct osmo_ss7_instance *s7i;
|
||||
s7i = osmo_sccp_addr_by_name(dest, addr_name);
|
||||
if (!s7i)
|
||||
return NULL;
|
||||
|
||||
/* Address exists in address-book but may not be filled entirely: */
|
||||
if (!dest->presence)
|
||||
osmo_sccp_make_addr_pc_ssn(dest, default_pc, OSMO_SCCP_SSN_RANAP);
|
||||
else if (!(dest->presence & OSMO_SCCP_ADDR_T_SSN))
|
||||
osmo_sccp_addr_set_ssn(dest, OSMO_SCCP_SSN_RANAP);
|
||||
|
||||
return s7i;
|
||||
}
|
||||
|
||||
static void cnlink_disc_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
char *hnbgw_cnlink_sccp_addr_to_str(struct hnbgw_cnlink *cnlink, const struct osmo_sccp_addr *addr)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
switch (event) {
|
||||
struct osmo_sccp_instance *sccp = hnbgw_cnlink_sccp(cnlink);
|
||||
if (!sccp)
|
||||
return osmo_sccp_addr_dump(addr);
|
||||
return osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sccp, addr);
|
||||
}
|
||||
|
||||
case CNLINK_EV_RX_RESET:
|
||||
tx_reset_ack(cnlink);
|
||||
cnlink_fsm_state_chg(fi, CNLINK_ST_CONN);
|
||||
break;
|
||||
static void hnbgw_cnlink_cfg_copy(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct osmo_nri_range *r;
|
||||
|
||||
case CNLINK_EV_RX_RESET_ACK:
|
||||
cnlink_fsm_state_chg(fi, CNLINK_ST_CONN);
|
||||
break;
|
||||
osmo_talloc_replace_string(cnlink, &cnlink->use.remote_addr_name, cnlink->vty.remote_addr_name);
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
osmo_nri_ranges_free(cnlink->use.nri_ranges);
|
||||
cnlink->use.nri_ranges = osmo_nri_ranges_alloc(cnlink);
|
||||
llist_for_each_entry(r, &cnlink->vty.nri_ranges->entries, entry)
|
||||
osmo_nri_ranges_add(cnlink->use.nri_ranges, r);
|
||||
}
|
||||
|
||||
static bool hnbgw_cnlink_sccp_cfg_changed(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
if (cnlink->vty.remote_addr_name && cnlink->use.remote_addr_name) {
|
||||
struct osmo_ss7_instance *s7i;
|
||||
struct osmo_sccp_addr remote_addr = {};
|
||||
|
||||
/* Instead of comparing whether the address book entry names are different, actually resolve the
|
||||
* resulting SCCP address, and only restart the cnlink if the resulting address changed. */
|
||||
s7i = sccp_addr_by_name_filled(&remote_addr, cnlink->vty.remote_addr_name,
|
||||
cnlink->pool->default_remote_pc);
|
||||
if (!s7i)
|
||||
return true;
|
||||
if (osmo_sccp_addr_cmp(&remote_addr, &cnlink->remote_addr,
|
||||
OSMO_SCCP_ADDR_T_PC | OSMO_SCCP_ADDR_T_SSN))
|
||||
changed = true;
|
||||
} else if (cnlink->vty.remote_addr_name != cnlink->use.remote_addr_name) {
|
||||
/* One of them is NULL, the other is not. */
|
||||
changed = true;
|
||||
}
|
||||
|
||||
/* if more cnlink configuration is added in the future, it needs to be compared here. */
|
||||
return changed;
|
||||
}
|
||||
|
||||
static void cnlink_conn_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
static void hnbgw_cnlink_log_self(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
if (prev_state != CNLINK_ST_CONN)
|
||||
link_up(cnlink);
|
||||
struct osmo_ss7_instance *ss7 = cnlink->hnbgw_sccp_user->ss7;
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "using: cs7-%u %s <-> %s %s %s\n",
|
||||
osmo_ss7_instance_get_id(ss7),
|
||||
/* printing the entire SCCP address is quite long, rather just print the point-code */
|
||||
osmo_ss7_pointcode_print(ss7, cnlink->hnbgw_sccp_user->local_addr.pc),
|
||||
osmo_ss7_pointcode_print2(ss7, cnlink->remote_addr.pc),
|
||||
cnlink->name, cnlink->use.remote_addr_name ? : "(default remote point-code)");
|
||||
}
|
||||
|
||||
static void cnlink_conn_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
/* If not present yet, set up all of osmo_ss7_instance, osmo_sccp_instance and hnbgw_sccp_user for the given cnlink.
|
||||
* The cs7 instance nr to use is determined by cnlink->remote_addr_name, or cs7 instance 0 if that is not present.
|
||||
* Set cnlink->hnbgw_sccp_user to the new SCCP instance. Return 0 on success, negative on error. */
|
||||
int hnbgw_cnlink_start_or_restart(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
struct osmo_ss7_instance *s7i = NULL;
|
||||
struct hnbgw_sccp_user *hsu;
|
||||
uint32_t ss7_id;
|
||||
int rc;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case CNLINK_EV_RX_RESET:
|
||||
/* We were connected, but the remote side has restarted. */
|
||||
link_lost(cnlink);
|
||||
tx_reset_ack(cnlink);
|
||||
link_up(cnlink);
|
||||
break;
|
||||
|
||||
case CNLINK_EV_RX_RESET_ACK:
|
||||
LOGPFSML(fi, LOGL_INFO, "Link is already up, ignoring RESET ACK\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
/* If a hnbgw_sccp_user has already been set up, use that. */
|
||||
if (cnlink->hnbgw_sccp_user) {
|
||||
if (!hnbgw_cnlink_sccp_cfg_changed(cnlink)) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "SCCP instance already set up, using %s\n",
|
||||
cnlink->hnbgw_sccp_user->name);
|
||||
return 0;
|
||||
}
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "config changed, restarting SCCP\n");
|
||||
hnbgw_cnlink_drop_sccp(cnlink);
|
||||
} else {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "no SCCP instance selected yet\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int cnlink_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
/* Copy the current configuration: cnlink->use = cnlink->vty */
|
||||
hnbgw_cnlink_cfg_copy(cnlink);
|
||||
|
||||
tx_reset(cnlink);
|
||||
if (!cnlink->use.remote_addr_name) {
|
||||
/* No remote address configured in VTY, set a default one and
|
||||
* make sure it becomes registered in the sccp address-book: */
|
||||
cnlink->use.remote_addr_name = talloc_strdup(cnlink, cnlink->pool->default_addr_name);
|
||||
s7i = osmo_sccp_addr_by_name(&cnlink->remote_addr, cnlink->use.remote_addr_name);
|
||||
if (!s7i) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "To auto-configure cnlink, creating cs7 instance 0 implicitly\n");
|
||||
s7i = osmo_ss7_instance_find_or_create(g_hnbgw, 0);
|
||||
OSMO_ASSERT(s7i);
|
||||
osmo_sccp_make_addr_pc_ssn(&cnlink->remote_addr,
|
||||
cnlink->pool->default_remote_pc,
|
||||
OSMO_SCCP_SSN_RANAP);
|
||||
rc = osmo_sccp_addr_create(s7i, cnlink->use.remote_addr_name, &cnlink->remote_addr);
|
||||
if (rc < 0) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Failed adding address '%s' to sccp address-book!\n",
|
||||
cnlink->use.remote_addr_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
/* Update VTY config to show & point to the address dynamically added to address-book: */
|
||||
cnlink->vty.remote_addr_name = talloc_strdup(cnlink, cnlink->use.remote_addr_name);
|
||||
} else {
|
||||
s7i = sccp_addr_by_name_filled(&cnlink->remote_addr, cnlink->use.remote_addr_name, cnlink->pool->default_remote_pc);
|
||||
if (!s7i) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "cannot initialize SCCP: there is no SCCP address named '%s'\n",
|
||||
cnlink->use.remote_addr_name);
|
||||
return -ENOENT;
|
||||
}
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "remote-addr is '%s', using cs7 instance %u\n",
|
||||
cnlink->use.remote_addr_name, osmo_ss7_instance_get_id(s7i));
|
||||
/* Address exists in address-book but may not be filled entirely: */
|
||||
rc = osmo_sccp_addr_update(s7i, cnlink->use.remote_addr_name, &cnlink->remote_addr);
|
||||
if (rc < 0) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Failed updating address '%s' in sccp address-book!\n",
|
||||
cnlink->use.remote_addr_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* (re-)enter disconnect state to resend RESET after timeout. */
|
||||
cnlink_fsm_state_chg(fi, CNLINK_ST_DISC);
|
||||
ss7_id = osmo_ss7_instance_get_id(s7i);
|
||||
|
||||
/* Return 0 to not terminate the fsm */
|
||||
/* Has another cnlink already set up an SCCP instance for this s7i? */
|
||||
llist_for_each_entry(hsu, &g_hnbgw->sccp.users, entry) {
|
||||
if (hsu->ss7 != s7i)
|
||||
continue;
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "using existing SCCP instance %s on cs7 instance %u\n",
|
||||
hsu->name, ss7_id);
|
||||
cnlink->hnbgw_sccp_user = hsu;
|
||||
hnbgw_sccp_user_get(cnlink->hnbgw_sccp_user, HSU_USE_CNLINK);
|
||||
hnbgw_cnlink_log_self(cnlink);
|
||||
return 0;
|
||||
}
|
||||
/* else cnlink->hnbgw_sccp_user stays NULL and is set up below. */
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "cs7 instance %u has no configured SCCP instance yet\n", ss7_id);
|
||||
|
||||
/* No SCCP instance yet for this ss7. Create it. If no address name is given that resolves to a
|
||||
* particular cs7 instance above, use 'cs7 instance 0'. */
|
||||
cnlink->hnbgw_sccp_user = hnbgw_sccp_user_alloc(ss7_id);
|
||||
hnbgw_sccp_user_get(cnlink->hnbgw_sccp_user, HSU_USE_CNLINK);
|
||||
hnbgw_cnlink_log_self(cnlink);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
static struct osmo_fsm_state cnlink_fsm_states[] = {
|
||||
[CNLINK_ST_DISC] = {
|
||||
.name = "DISCONNECTED",
|
||||
.in_event_mask = 0
|
||||
| S(CNLINK_EV_RX_RESET)
|
||||
| S(CNLINK_EV_RX_RESET_ACK)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(CNLINK_ST_DISC)
|
||||
| S(CNLINK_ST_CONN)
|
||||
,
|
||||
.onenter = cnlink_disc_onenter,
|
||||
.action = cnlink_disc_action,
|
||||
},
|
||||
[CNLINK_ST_CONN] = {
|
||||
.name = "CONNECTED",
|
||||
.in_event_mask = 0
|
||||
| S(CNLINK_EV_RX_RESET)
|
||||
| S(CNLINK_EV_RX_RESET_ACK)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(CNLINK_ST_DISC)
|
||||
| S(CNLINK_ST_CONN)
|
||||
,
|
||||
.onenter = cnlink_conn_onenter,
|
||||
.action = cnlink_conn_action,
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm cnlink_fsm = {
|
||||
.name = "cnlink",
|
||||
.states = cnlink_fsm_states,
|
||||
.num_states = ARRAY_SIZE(cnlink_fsm_states),
|
||||
.log_subsys = DRANAP,
|
||||
.timer_cb = cnlink_fsm_timer_cb,
|
||||
.event_names = cnlink_fsm_event_names,
|
||||
};
|
||||
|
||||
bool cnlink_is_conn_ready(const struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
return cnlink->fi->state == CNLINK_ST_CONN;
|
||||
}
|
||||
|
||||
void cnlink_resend_reset(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
/* Immediately (1ms) kick off reset sending mechanism */
|
||||
osmo_fsm_inst_state_chg_ms(cnlink->fi, CNLINK_ST_DISC, 1, 0);
|
||||
}
|
||||
|
||||
void cnlink_set_disconnected(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
/* Go to disconnected state, with the normal RESET timeout to re-send RESET. */
|
||||
cnlink_fsm_state_chg(cnlink->fi, CNLINK_ST_DISC);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void cnlink_fsm_init(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&cnlink_fsm) == 0);
|
||||
}
|
||||
|
||||
void cnlink_rx_reset_cmd(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
osmo_fsm_inst_dispatch(cnlink->fi, CNLINK_EV_RX_RESET, NULL);
|
||||
}
|
||||
|
||||
void cnlink_rx_reset_ack(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
osmo_fsm_inst_dispatch(cnlink->fi, CNLINK_EV_RX_RESET_ACK, NULL);
|
||||
}
|
||||
|
||||
|
220
src/osmo-hnbgw/cnlink_fsm.c
Normal file
220
src/osmo-hnbgw/cnlink_fsm.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/* (C) 2023 by sysmocom s.f.m.c. GmbH <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 <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
|
||||
#include <osmocom/gsm/gsm23236.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#include <asn1c/asn1helpers.h>
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/ranap/ranap_msg_factory.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
|
||||
enum cnlink_fsm_state {
|
||||
CNLINK_ST_DISC,
|
||||
CNLINK_ST_CONN,
|
||||
};
|
||||
|
||||
enum cnlink_fsm_event {
|
||||
CNLINK_EV_RX_RESET,
|
||||
CNLINK_EV_RX_RESET_ACK,
|
||||
};
|
||||
|
||||
static const struct value_string cnlink_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(CNLINK_EV_RX_RESET),
|
||||
OSMO_VALUE_STRING(CNLINK_EV_RX_RESET_ACK),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_tdef_state_timeout cnlink_timeouts[32] = {
|
||||
[CNLINK_ST_DISC] = { .T = 4 },
|
||||
};
|
||||
|
||||
#define cnlink_fsm_state_chg(FI, STATE) \
|
||||
osmo_tdef_fsm_inst_state_chg(FI, STATE, \
|
||||
cnlink_timeouts, \
|
||||
hnbgw_T_defs, \
|
||||
-1)
|
||||
|
||||
static void link_up(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
LOGPFSML(cnlink->fi, LOGL_NOTICE, "link up\n");
|
||||
CNLINK_STAT_SET(cnlink, CNLINK_STAT_CONNECTED, 1);
|
||||
}
|
||||
|
||||
static void link_lost(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct hnbgw_context_map *map, *map2;
|
||||
|
||||
LOGPFSML(cnlink->fi, LOGL_NOTICE, "link lost\n");
|
||||
CNLINK_STAT_SET(cnlink, CNLINK_STAT_CONNECTED, 0);
|
||||
|
||||
llist_for_each_entry_safe(map, map2, &cnlink->map_list, hnbgw_cnlink_entry)
|
||||
context_map_cnlink_lost(map);
|
||||
}
|
||||
|
||||
|
||||
static void cnlink_disc_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
if (prev_state == CNLINK_ST_CONN)
|
||||
link_lost(cnlink);
|
||||
}
|
||||
|
||||
static void cnlink_disc_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
switch (event) {
|
||||
|
||||
case CNLINK_EV_RX_RESET:
|
||||
hnbgw_cnlink_tx_ranap_reset_ack(cnlink);
|
||||
cnlink_fsm_state_chg(fi, CNLINK_ST_CONN);
|
||||
break;
|
||||
|
||||
case CNLINK_EV_RX_RESET_ACK:
|
||||
cnlink_fsm_state_chg(fi, CNLINK_ST_CONN);
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void cnlink_conn_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
if (prev_state != CNLINK_ST_CONN)
|
||||
link_up(cnlink);
|
||||
}
|
||||
|
||||
static void cnlink_conn_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case CNLINK_EV_RX_RESET:
|
||||
/* We were connected, but the remote side has restarted. */
|
||||
link_lost(cnlink);
|
||||
hnbgw_cnlink_tx_ranap_reset_ack(cnlink);
|
||||
link_up(cnlink);
|
||||
break;
|
||||
|
||||
case CNLINK_EV_RX_RESET_ACK:
|
||||
LOGPFSML(fi, LOGL_INFO, "Link is already up, ignoring RESET ACK\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int cnlink_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
|
||||
hnbgw_cnlink_tx_ranap_reset(cnlink);
|
||||
|
||||
/* (re-)enter disconnect state to resend RESET after timeout. */
|
||||
cnlink_fsm_state_chg(fi, CNLINK_ST_DISC);
|
||||
|
||||
/* Return 0 to not terminate the fsm */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
static struct osmo_fsm_state cnlink_fsm_states[] = {
|
||||
[CNLINK_ST_DISC] = {
|
||||
.name = "DISCONNECTED",
|
||||
.in_event_mask = 0
|
||||
| S(CNLINK_EV_RX_RESET)
|
||||
| S(CNLINK_EV_RX_RESET_ACK)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(CNLINK_ST_DISC)
|
||||
| S(CNLINK_ST_CONN)
|
||||
,
|
||||
.onenter = cnlink_disc_onenter,
|
||||
.action = cnlink_disc_action,
|
||||
},
|
||||
[CNLINK_ST_CONN] = {
|
||||
.name = "CONNECTED",
|
||||
.in_event_mask = 0
|
||||
| S(CNLINK_EV_RX_RESET)
|
||||
| S(CNLINK_EV_RX_RESET_ACK)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(CNLINK_ST_DISC)
|
||||
| S(CNLINK_ST_CONN)
|
||||
,
|
||||
.onenter = cnlink_conn_onenter,
|
||||
.action = cnlink_conn_action,
|
||||
},
|
||||
};
|
||||
|
||||
struct osmo_fsm cnlink_fsm = {
|
||||
.name = "cnlink",
|
||||
.states = cnlink_fsm_states,
|
||||
.num_states = ARRAY_SIZE(cnlink_fsm_states),
|
||||
.log_subsys = DRANAP,
|
||||
.timer_cb = cnlink_fsm_timer_cb,
|
||||
.event_names = cnlink_fsm_event_names,
|
||||
};
|
||||
|
||||
bool cnlink_is_conn_ready(const struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
return cnlink->fi->state == CNLINK_ST_CONN;
|
||||
}
|
||||
|
||||
void cnlink_resend_reset(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
/* Immediately (1ms) kick off reset sending mechanism */
|
||||
osmo_fsm_inst_state_chg_ms(cnlink->fi, CNLINK_ST_DISC, 1, 0);
|
||||
}
|
||||
|
||||
void cnlink_set_disconnected(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
/* Go to disconnected state, with the normal RESET timeout to re-send RESET. */
|
||||
cnlink_fsm_state_chg(cnlink->fi, CNLINK_ST_DISC);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void cnlink_fsm_init(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&cnlink_fsm) == 0);
|
||||
}
|
||||
|
||||
void cnlink_rx_reset_cmd(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
osmo_fsm_inst_dispatch(cnlink->fi, CNLINK_EV_RX_RESET, NULL);
|
||||
}
|
||||
|
||||
void cnlink_rx_reset_ack(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
osmo_fsm_inst_dispatch(cnlink->fi, CNLINK_EV_RX_RESET_ACK, NULL);
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
@@ -87,8 +88,8 @@ struct hnbgw_context_map *context_map_alloc(struct hnb_context *hnb, uint32_t ru
|
||||
map->hnb_ctx = hnb;
|
||||
map->rua_ctx_id = rua_ctx_id;
|
||||
map->is_ps = is_ps;
|
||||
INIT_LLIST_HEAD(&map->ps_rab_ass);
|
||||
INIT_LLIST_HEAD(&map->ps_rabs);
|
||||
INIT_LLIST_HEAD(&map->ps_rab_ass_list);
|
||||
INIT_LLIST_HEAD(&map->ps_rab_list);
|
||||
|
||||
map_rua_fsm_alloc(map);
|
||||
|
||||
@@ -186,7 +187,7 @@ void context_map_hnb_released(struct hnbgw_context_map *map)
|
||||
|
||||
void context_map_cnlink_lost(struct hnbgw_context_map *map)
|
||||
{
|
||||
map_sccp_dispatch(map, MAP_SCCP_EV_RAN_LINK_LOST, NULL);
|
||||
map_sccp_dispatch(map, MAP_SCCP_EV_CN_LINK_LOST, NULL);
|
||||
}
|
||||
|
||||
void context_map_free(struct hnbgw_context_map *map)
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
@@ -276,10 +277,27 @@ static void map_rua_disconnected_onenter(struct osmo_fsm_inst *fi, uint32_t prev
|
||||
|
||||
static void map_rua_disconnected_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct msgb *ranap_msg = data;
|
||||
if (msg_has_l2_data(ranap_msg))
|
||||
LOGPFSML(fi, LOGL_NOTICE, "RUA not connected, cannot dispatch RANAP message\n");
|
||||
/* Ignore all events. */
|
||||
struct msgb *ranap_msg;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case MAP_RUA_EV_TX_DIRECT_TRANSFER:
|
||||
/* This can happen if CN is buggy, or in general if there was a race
|
||||
* condition between us forwarding the release towards CN (SCCP Release
|
||||
* or RANAP Iu-ReleaseComplete) and CN sendig whatever to us. */
|
||||
ranap_msg = data;
|
||||
if (msg_has_l2_data(ranap_msg)) {
|
||||
LOGPFSML(fi, LOGL_NOTICE, "RUA already disconnected, skip forwarding DL RANAP msg (%u bytes)\n",
|
||||
msgb_l2len(ranap_msg));
|
||||
LOGPFSML(fi, LOGL_DEBUG, "%s\n", osmo_hexdump(msgb_l2(ranap_msg), msgb_l2len(ranap_msg)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MAP_RUA_EV_CN_DISC:
|
||||
case MAP_RUA_EV_HNB_LINK_LOST:
|
||||
/* Ignore events. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void map_rua_disrupted_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
@@ -331,6 +349,7 @@ static const struct osmo_fsm_state map_rua_fsm_states[] = {
|
||||
[MAP_RUA_ST_DISCONNECTED] = {
|
||||
.name = "disconnected",
|
||||
.in_event_mask = 0
|
||||
| S(MAP_RUA_EV_TX_DIRECT_TRANSFER)
|
||||
| S(MAP_RUA_EV_CN_DISC)
|
||||
| S(MAP_RUA_EV_HNB_LINK_LOST)
|
||||
,
|
||||
|
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/hnbgw_ranap.h>
|
||||
@@ -80,7 +81,7 @@ void map_sccp_fsm_alloc(struct hnbgw_context_map *map)
|
||||
|
||||
OSMO_ASSERT(map->sccp_fi == NULL);
|
||||
map->sccp_fi = fi;
|
||||
|
||||
INIT_LLIST_HEAD(&map->sccp_fi_ctx.wait_cc_tx_msg_list);
|
||||
/* trigger the timeout */
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_INIT);
|
||||
}
|
||||
@@ -117,10 +118,8 @@ bool map_sccp_is_active(struct hnbgw_context_map *map)
|
||||
static int tx_sccp_cr(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct osmo_scu_prim *prim;
|
||||
int rc;
|
||||
|
||||
if (!map->cnlink || !map->cnlink->hnbgw_sccp_user) {
|
||||
if (!map->cnlink) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP Connection Request: no CN link\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -130,53 +129,43 @@ static int tx_sccp_cr(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
ranap_msg = hnbgw_ranap_msg_alloc("SCCP-CR-empty");
|
||||
}
|
||||
|
||||
prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_REQUEST, ranap_msg);
|
||||
prim->u.connect.called_addr = map->cnlink->remote_addr;
|
||||
prim->u.connect.calling_addr = map->cnlink->hnbgw_sccp_user->local_addr;
|
||||
prim->u.connect.sccp_class = 2;
|
||||
prim->u.connect.conn_id = map->scu_conn_id;
|
||||
|
||||
rc = osmo_sccp_user_sap_down_nofree(map->cnlink->hnbgw_sccp_user->sccp_user, &prim->oph);
|
||||
if (rc)
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP Connection Request to CN\n");
|
||||
return rc;
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_N_CONNECT_REQ);
|
||||
return hnbgw_sccp_user_tx_connect_req(map->cnlink->hnbgw_sccp_user,
|
||||
&map->cnlink->remote_addr,
|
||||
map->scu_conn_id,
|
||||
ranap_msg);
|
||||
}
|
||||
|
||||
static int tx_sccp_df1(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct osmo_scu_prim *prim;
|
||||
int rc;
|
||||
|
||||
if (!msg_has_l2_data(ranap_msg))
|
||||
return 0;
|
||||
|
||||
if (!map->cnlink || !map->cnlink->hnbgw_sccp_user) {
|
||||
if (!map->cnlink) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP Data Form 1: no CN link\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, ranap_msg);
|
||||
prim->u.data.conn_id = map->scu_conn_id;
|
||||
|
||||
rc = osmo_sccp_user_sap_down_nofree(map->cnlink->hnbgw_sccp_user->sccp_user, &prim->oph);
|
||||
if (rc)
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP Data Form 1 to CN\n");
|
||||
return rc;
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_N_DATA_REQ);
|
||||
return hnbgw_sccp_user_tx_data_req(map->cnlink->hnbgw_sccp_user,
|
||||
map->scu_conn_id,
|
||||
ranap_msg);
|
||||
}
|
||||
|
||||
static int tx_sccp_rlsd(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
|
||||
if (!map->cnlink || !map->cnlink->hnbgw_sccp_user) {
|
||||
if (!map->cnlink) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP RLSD: no CN link\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return osmo_sccp_tx_disconn(map->cnlink->hnbgw_sccp_user->sccp_user, map->scu_conn_id, NULL, 0);
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_N_DISCONNECT_REQ);
|
||||
return hnbgw_sccp_user_tx_disconnect_req(map->cnlink->hnbgw_sccp_user,
|
||||
map->scu_conn_id);
|
||||
}
|
||||
|
||||
static int handle_rx_sccp(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
@@ -194,10 +183,24 @@ static int handle_rx_sccp(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
return hnbgw_ranap_rx_data_dl(map, ranap_msg);
|
||||
}
|
||||
|
||||
static void wait_cc_tx_msg_list_enqueue(struct hnbgw_context_map *map, struct msgb *ranap_msg)
|
||||
{
|
||||
talloc_steal(map, ranap_msg);
|
||||
msgb_enqueue(&map->sccp_fi_ctx.wait_cc_tx_msg_list, ranap_msg);
|
||||
}
|
||||
|
||||
static struct msgb *wait_cc_tx_msg_list_dequeue(struct hnbgw_context_map *map)
|
||||
{
|
||||
struct msgb *ranap_msg = msgb_dequeue(&map->sccp_fi_ctx.wait_cc_tx_msg_list);
|
||||
if (ranap_msg)
|
||||
talloc_steal(OTC_SELECT, ranap_msg);
|
||||
return ranap_msg;
|
||||
}
|
||||
|
||||
static void map_sccp_init_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct msgb *ranap_msg = NULL;
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
@@ -212,10 +215,6 @@ static void map_sccp_init_action(struct osmo_fsm_inst *fi, uint32_t event, void
|
||||
case MAP_SCCP_EV_RAN_LINK_LOST:
|
||||
case MAP_SCCP_EV_USER_ABORT:
|
||||
case MAP_SCCP_EV_CN_LINK_LOST:
|
||||
ranap_msg = data;
|
||||
/* No CR has been sent yet, just go to disconnected state. */
|
||||
if (msg_has_l2_data(ranap_msg))
|
||||
LOG_MAP(map, DLSCCP, LOGL_ERROR, "SCCP not connected, cannot dispatch RANAP message\n");
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
return;
|
||||
|
||||
@@ -229,6 +228,7 @@ static void map_sccp_init_action(struct osmo_fsm_inst *fi, uint32_t event, void
|
||||
case MAP_SCCP_EV_RX_RELEASED:
|
||||
/* SCCP RLSD received from CN. This will never happen since we haven't even asked for a connection, but
|
||||
* for completeness: */
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_RLSD_CN_ORIGIN);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
return;
|
||||
|
||||
@@ -252,17 +252,14 @@ static void map_sccp_wait_cc_action(struct osmo_fsm_inst *fi, uint32_t event, vo
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_TX_DATA_REQUEST:
|
||||
/* ranap_msg = data; */
|
||||
LOGPFSML(fi, LOGL_ERROR, "Connection not yet confirmed, cannot forward RANAP to CN\n");
|
||||
ranap_msg = data;
|
||||
LOGPFSML(fi, LOGL_INFO, "Caching RANAP msg from RUA while waiting for SCCP CC\n");
|
||||
wait_cc_tx_msg_list_enqueue(map, ranap_msg);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RAN_LINK_LOST:
|
||||
case MAP_SCCP_EV_USER_ABORT:
|
||||
case MAP_SCCP_EV_CN_LINK_LOST:
|
||||
ranap_msg = data;
|
||||
/* RUA connection was terminated. First wait for the CC before releasing the SCCP conn. */
|
||||
if (msg_has_l2_data(ranap_msg))
|
||||
LOGPFSML(fi, LOGL_ERROR, "Connection not yet confirmed, cannot forward RANAP to CN\n");
|
||||
map->please_disconnect = true;
|
||||
return;
|
||||
|
||||
@@ -277,6 +274,7 @@ static void map_sccp_wait_cc_action(struct osmo_fsm_inst *fi, uint32_t event, vo
|
||||
ranap_msg = data;
|
||||
/* SCCP RLSD received from CN. This will never happen since we haven't even received a Connection
|
||||
* Confirmed, but for completeness: */
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_RLSD_CN_ORIGIN);
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
return;
|
||||
@@ -289,9 +287,15 @@ static void map_sccp_wait_cc_action(struct osmo_fsm_inst *fi, uint32_t event, vo
|
||||
static void map_sccp_connected_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct msgb *ranap_msg;
|
||||
|
||||
/* Now that SCCP conn is confirmed, forward pending msgs received from RUA side: */
|
||||
while ((ranap_msg = wait_cc_tx_msg_list_dequeue(map)))
|
||||
tx_sccp_df1(fi, ranap_msg);
|
||||
|
||||
if (map->please_disconnect) {
|
||||
/* SCCP has already been asked to disconnect, so disconnect now that the CC has been received. Send RLSD
|
||||
* to SCCP (without RANAP data) */
|
||||
/* SCCP has already been asked to disconnect, so disconnect now that the
|
||||
* CC has been received. Send RLSD to SCCP (without RANAP data) */
|
||||
tx_sccp_rlsd(fi);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
}
|
||||
@@ -299,6 +303,7 @@ static void map_sccp_connected_onenter(struct osmo_fsm_inst *fi, uint32_t prev_s
|
||||
|
||||
static void map_sccp_connected_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct msgb *ranap_msg = NULL;
|
||||
bool rua_disconnect_err_condition;
|
||||
|
||||
@@ -341,21 +346,15 @@ static void map_sccp_connected_action(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
case MAP_SCCP_EV_USER_ABORT:
|
||||
/* The user is asking for disconnection, so there is no Iu Release in progress. Disconnect now. */
|
||||
case MAP_SCCP_EV_CN_LINK_LOST:
|
||||
ranap_msg = data;
|
||||
/* The CN peer has sent a RANAP RESET, so the old link that this map ran on is lost */
|
||||
|
||||
/* There won't be any ranap_msg, but if a caller wants to dispatch a msg, forward it before
|
||||
* disconnecting. */
|
||||
tx_sccp_df1(fi, ranap_msg);
|
||||
tx_sccp_rlsd(fi);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RX_RELEASED:
|
||||
ranap_msg = data;
|
||||
/* The CN sends an N-Disconnect (SCCP Released) out of the usual sequence. Not what we expected, but
|
||||
* handle it. */
|
||||
LOGPFSML(fi, LOGL_ERROR, "CN sends SCCP Released sooner than expected\n");
|
||||
/* The CN sends an N-Disconnect (SCCP Released). */
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_RLSD_CN_ORIGIN);
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
return;
|
||||
@@ -484,6 +483,7 @@ void map_sccp_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cau
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
map->sccp_fi = NULL;
|
||||
msgb_queue_free(&map->sccp_fi_ctx.wait_cc_tx_msg_list);
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
401
src/osmo-hnbgw/hnb.c
Normal file
401
src/osmo-hnbgw/hnb.c
Normal file
@@ -0,0 +1,401 @@
|
||||
/* HNB related code */
|
||||
|
||||
/* (C) 2015,2024 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2016-2025 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/sctp.h>
|
||||
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
|
||||
#include <osmocom/gsm/gsm23236.h>
|
||||
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/hnbgw/umts_cell_id.h>
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_hnbap.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
|
||||
/* update the active RAB duration rate_ctr for given HNB */
|
||||
void hnb_store_rab_durations(struct hnb_context *hnb)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
struct timespec now;
|
||||
uint64_t elapsed_cs_rab_ms = 0;
|
||||
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
/* iterate over all context_maps (subscribers) */
|
||||
llist_for_each_entry(map, &hnb->map_list, hnb_list) {
|
||||
/* skip any PS maps, we care about CS RABs only here */
|
||||
if (map->is_ps)
|
||||
continue;
|
||||
elapsed_cs_rab_ms += mgw_fsm_get_elapsed_ms(map, &now);
|
||||
}
|
||||
|
||||
/* Export to rate countes. */
|
||||
rate_ctr_add(HNBP_CTR(hnb->persistent, HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL), elapsed_cs_rab_ms);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* HNB Context
|
||||
***********************************************************************/
|
||||
|
||||
/* look-up HNB context by id. Used from CTRL */
|
||||
static struct hnb_context *hnb_context_by_id(uint32_t cid)
|
||||
{
|
||||
struct hnb_context *hnb;
|
||||
|
||||
llist_for_each_entry(hnb, &g_hnbgw->hnb_list, list) {
|
||||
if (hnb->id.cid == cid)
|
||||
return hnb;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look-up HNB context by identity_info. Used from VTY */
|
||||
struct hnb_context *hnb_context_by_identity_info(const char *identity_info)
|
||||
{
|
||||
struct hnb_context *hnb;
|
||||
|
||||
llist_for_each_entry(hnb, &g_hnbgw->hnb_list, list) {
|
||||
if (strcmp(identity_info, hnb->identity_info) == 0)
|
||||
return hnb;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int hnb_read_cb(struct osmo_stream_srv *conn);
|
||||
static int hnb_closed_cb(struct osmo_stream_srv *conn);
|
||||
|
||||
static struct hnb_context *hnb_context_alloc(struct osmo_stream_srv_link *link, int new_fd)
|
||||
{
|
||||
struct hnb_context *ctx;
|
||||
|
||||
ctx = talloc_zero(g_hnbgw, struct hnb_context);
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
INIT_LLIST_HEAD(&ctx->map_list);
|
||||
|
||||
ctx->conn = osmo_stream_srv_create(g_hnbgw, link, new_fd, hnb_read_cb, hnb_closed_cb, ctx);
|
||||
if (!ctx->conn) {
|
||||
LOGP(DMAIN, LOGL_INFO, "error while creating connection\n");
|
||||
talloc_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
llist_add_tail(&ctx->list, &g_hnbgw->hnb_list);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
const char *hnb_context_name(struct hnb_context *ctx)
|
||||
{
|
||||
char *result;
|
||||
if (!ctx)
|
||||
return "NULL";
|
||||
|
||||
if (ctx->conn) {
|
||||
char hostbuf_r[INET6_ADDRSTRLEN];
|
||||
char portbuf_r[6];
|
||||
int fd = osmo_stream_srv_get_ofd(ctx->conn)->fd;
|
||||
|
||||
/* get remote addr */
|
||||
if (osmo_sock_get_ip_and_port(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r, sizeof(portbuf_r), false) == 0)
|
||||
result = talloc_asprintf(OTC_SELECT, "%s:%s", hostbuf_r, portbuf_r);
|
||||
else
|
||||
result = "?";
|
||||
} else {
|
||||
result = "disconnected";
|
||||
}
|
||||
|
||||
if (g_hnbgw->config.log_prefix_hnb_id)
|
||||
result = talloc_asprintf(OTC_SELECT, "%s %s", result, ctx->identity_info);
|
||||
else
|
||||
result = talloc_asprintf(OTC_SELECT, "%s %s", result, umts_cell_id_to_str(&ctx->id));
|
||||
return result;
|
||||
}
|
||||
|
||||
void hnb_context_release_ue_state(struct hnb_context *ctx)
|
||||
{
|
||||
struct hnbgw_context_map *map, *map2;
|
||||
|
||||
/* deactivate all context maps */
|
||||
llist_for_each_entry_safe(map, map2, &ctx->map_list, hnb_list) {
|
||||
context_map_hnb_released(map);
|
||||
/* hnbgw_context_map will remove itself from lists when it is ready. */
|
||||
}
|
||||
}
|
||||
|
||||
void hnb_context_release(struct hnb_context *ctx)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
LOGHNB(ctx, DMAIN, LOGL_INFO, "Releasing HNB context\n");
|
||||
|
||||
if (ctx->persistent) {
|
||||
struct timespec tp;
|
||||
int rc;
|
||||
rc = osmo_clock_gettime(CLOCK_MONOTONIC, &tp);
|
||||
ctx->persistent->updowntime = (rc < 0) ? 0 : tp.tv_sec;
|
||||
}
|
||||
|
||||
/* remove from the list of HNB contexts */
|
||||
llist_del(&ctx->list);
|
||||
|
||||
hnb_context_release_ue_state(ctx);
|
||||
|
||||
if (ctx->conn) { /* we own a conn, we must free it: */
|
||||
LOGHNB(ctx, DMAIN, LOGL_INFO, "Closing HNB SCTP connection %s\n",
|
||||
osmo_sock_get_name2(osmo_stream_srv_get_ofd(ctx->conn)->fd));
|
||||
/* Avoid our closed_cb calling hnb_context_release() again: */
|
||||
osmo_stream_srv_set_data(ctx->conn, NULL);
|
||||
osmo_stream_srv_destroy(ctx->conn);
|
||||
} /* else: we are called from closed_cb, so conn is being freed separately */
|
||||
|
||||
/* hnbgw_context_map are still listed in ctx->map_list, but we are freeing ctx. Remove all entries from the
|
||||
* list, but keep the hnbgw_context_map around for graceful release. They are also listed under
|
||||
* hnbgw_cnlink->map_list, and will remove themselves when ready. */
|
||||
while ((map = llist_first_entry_or_null(&ctx->map_list, struct hnbgw_context_map, hnb_list))) {
|
||||
llist_del(&map->hnb_list);
|
||||
map->hnb_ctx = NULL;
|
||||
}
|
||||
|
||||
/* remove back reference from hnb_persistent to context */
|
||||
if (ctx->persistent)
|
||||
hnb_persistent_deregistered(ctx->persistent);
|
||||
|
||||
talloc_free(ctx);
|
||||
}
|
||||
|
||||
unsigned long long hnb_get_updowntime(const struct hnb_context *ctx)
|
||||
{
|
||||
if (!ctx->persistent)
|
||||
return 0;
|
||||
return hnbp_get_updowntime(ctx->persistent);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* SCTP Socket / stream handling
|
||||
***********************************************************************/
|
||||
|
||||
static int hnb_read_cb(struct osmo_stream_srv *conn)
|
||||
{
|
||||
struct hnb_context *hnb = osmo_stream_srv_get_data(conn);
|
||||
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
|
||||
struct msgb *msg = msgb_alloc(IUH_MSGB_SIZE, "Iuh rx");
|
||||
int rc;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
OSMO_ASSERT(hnb);
|
||||
/* we store a reference to the HomeNodeB in the msg->dest for the
|
||||
* benefit of various downstream processing functions */
|
||||
msg->dst = hnb;
|
||||
|
||||
rc = osmo_stream_srv_recv(conn, msg);
|
||||
/* Notification received */
|
||||
if (msgb_sctp_msg_flags(msg) & OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION) {
|
||||
union sctp_notification *notif = (union sctp_notification *)msgb_data(msg);
|
||||
rc = 0;
|
||||
switch (notif->sn_header.sn_type) {
|
||||
case SCTP_ASSOC_CHANGE:
|
||||
switch (notif->sn_assoc_change.sac_state) {
|
||||
case SCTP_COMM_LOST:
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE,
|
||||
"sctp_recvmsg(%s) = SCTP_COMM_LOST, closing conn\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
break;
|
||||
case SCTP_RESTART:
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE, "HNB SCTP conn RESTARTed, marking as HNBAP-unregistered\n");
|
||||
hnb->hnb_registered = false;
|
||||
hnb_context_release_ue_state(hnb);
|
||||
/* The tx queue may be quite full after an SCTP RESTART: (SYS#6113)
|
||||
* The link may have been flaky (a possible reason for the peer restarting the conn) and
|
||||
* hence the kernel socket Tx queue may be full (no ACKs coming back) and our own userspace
|
||||
* queue may contain plenty of oldish messages to be sent. Since the HNB will re-register after
|
||||
* this, we simply drop all those old messages: */
|
||||
osmo_stream_srv_clear_tx_queue(conn);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SCTP_SHUTDOWN_EVENT:
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE,
|
||||
"sctp_recvmsg(%s) = SCTP_SHUTDOWN_EVENT, closing conn\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
} else if (rc == -EAGAIN) {
|
||||
/* Older versions of osmo_stream_srv_recv() not supporting
|
||||
* msgb_sctp_msg_flags() may still return -EAGAIN when an sctp
|
||||
* notification is received. */
|
||||
rc = 0;
|
||||
goto out;
|
||||
} else if (rc < 0) {
|
||||
LOGHNB(hnb, DMAIN, LOGL_ERROR, "Error during sctp_recvmsg(%s)\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
goto out;
|
||||
} else if (rc == 0) {
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE, "Connection closed sctp_recvmsg(%s) = 0\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
goto out;
|
||||
} else {
|
||||
msgb_put(msg, rc);
|
||||
}
|
||||
|
||||
switch (msgb_sctp_ppid(msg)) {
|
||||
case IUH_PPI_HNBAP:
|
||||
hnb->hnbap_stream = msgb_sctp_stream(msg);
|
||||
rc = hnbgw_hnbap_rx(hnb, msg);
|
||||
break;
|
||||
case IUH_PPI_RUA:
|
||||
if (!hnb->hnb_registered) {
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE, "Discarding RUA as HNB is not registered\n");
|
||||
goto out;
|
||||
}
|
||||
hnb->rua_stream = msgb_sctp_stream(msg);
|
||||
rc = hnbgw_rua_rx(hnb, msg);
|
||||
break;
|
||||
case IUH_PPI_SABP:
|
||||
case IUH_PPI_RNA:
|
||||
case IUH_PPI_PUA:
|
||||
LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unimplemented SCTP PPID=%lu received\n", msgb_sctp_ppid(msg));
|
||||
rc = 0;
|
||||
break;
|
||||
default:
|
||||
LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unknown SCTP PPID=%lu received\n", msgb_sctp_ppid(msg));
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnb_closed_cb(struct osmo_stream_srv *conn)
|
||||
{
|
||||
struct hnb_context *hnb = osmo_stream_srv_get_data(conn);
|
||||
if (!hnb)
|
||||
return 0; /* hnb_context is being freed, nothing do be done */
|
||||
|
||||
/* hnb: conn became broken, let's release the associated hnb.
|
||||
* conn object is being freed after closed_cb(), so unassign it from hnb
|
||||
* if available to avoid it freeing it again: */
|
||||
hnb->conn = NULL;
|
||||
hnb_context_release(hnb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! call-back when the listen FD has something to read */
|
||||
int hnbgw_rua_accept_cb(struct osmo_stream_srv_link *srv, int fd)
|
||||
{
|
||||
struct hnb_context *ctx;
|
||||
|
||||
LOGP(DMAIN, LOGL_INFO, "New HNB SCTP connection %s\n",
|
||||
osmo_sock_get_name2(fd));
|
||||
|
||||
ctx = hnb_context_alloc(srv, fd);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_RO(hnb_info, "info");
|
||||
static int get_hnb_info(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct hnb_context *hnb = data;
|
||||
|
||||
cmd->reply = talloc_strdup(cmd, hnb->identity_info);
|
||||
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_RO(hnbs, "num-hnb");
|
||||
static int get_hnbs(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
cmd->reply = talloc_asprintf(cmd, "%u", llist_count(&g_hnbgw->hnb_list));
|
||||
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
int hnb_ctrl_cmds_install(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_hnbs);
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_HNB, &cmd_hnb_info);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int hnb_ctrl_node_lookup(void *data, vector vline, int *node_type, void **node_data, int *i)
|
||||
{
|
||||
const char *token = vector_slot(vline, *i);
|
||||
struct hnb_context *hnb;
|
||||
long num;
|
||||
|
||||
switch (*node_type) {
|
||||
case CTRL_NODE_ROOT:
|
||||
if (strcmp(token, "hnb") != 0)
|
||||
return 0;
|
||||
|
||||
(*i)++;
|
||||
|
||||
if (!ctrl_parse_get_num(vline, *i, &num))
|
||||
return -ERANGE;
|
||||
|
||||
hnb = hnb_context_by_id(num);
|
||||
if (!hnb)
|
||||
return -ENODEV;
|
||||
|
||||
*node_data = hnb;
|
||||
*node_type = CTRL_NODE_HNB;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
403
src/osmo-hnbgw/hnb_persistent.c
Normal file
403
src/osmo-hnbgw/hnb_persistent.c
Normal file
@@ -0,0 +1,403 @@
|
||||
/* HNB persistent related code */
|
||||
|
||||
/* (C) 2015,2024 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2016-2025 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/sctp.h>
|
||||
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
#include <osmocom/core/jhash.h>
|
||||
|
||||
#include <osmocom/gsm/gsm23236.h>
|
||||
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
|
||||
/***********************************************************************
|
||||
* HNB Persistent Data
|
||||
***********************************************************************/
|
||||
|
||||
const struct rate_ctr_desc hnb_ctr_description[] = {
|
||||
[HNB_CTR_IUH_ESTABLISHED] = {
|
||||
"iuh:established", "Number of times Iuh link was established" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_ERR_IND_UL] = {
|
||||
"ranap:ps:error_ind:ul", "Received ERROR Indications in Uplink (PS Domain)" },
|
||||
[HNB_CTR_RANAP_CS_ERR_IND_UL] = {
|
||||
"ranap:cs:error_ind:ul", "Received ERROR Indications in Uplink (PS Domain)" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RESET_REQ_UL] = {
|
||||
"ranap:ps:reset_req:ul", "Received RESET Requests in Uplink (PS Domain)" },
|
||||
[HNB_CTR_RANAP_CS_RESET_REQ_UL] = {
|
||||
"ranap:cs:reset_req:ul", "Received RESET Requests in Uplink (CS Domain)" },
|
||||
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_REQ] = {
|
||||
"ranap:ps:rab_act:req", "PS RAB Activations requested" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_REQ] = {
|
||||
"ranap:cs:rab_act:req", "CS RAB Activations requested" },
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_REQ_UNEXP] = {
|
||||
"ranap:ps:rab_act:req_unexp", "PS RAB Activations requested in unexpected state" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_REQ_UNEXP] = {
|
||||
"ranap:cs:rab_act:req_unexp", "CS RAB Activations requested in unexpected state" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_CNF] = {
|
||||
"ranap:ps:rab_act:cnf", "PS RAB Activations confirmed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_CNF] = {
|
||||
"ranap:cs:rab_act:cnf", "CS RAB Activations confirmed" },
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_CNF_UNEXP] = {
|
||||
"ranap:ps:rab_act:cnf_unexp", "PS RAB Activations confirmed in unexpected state" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_CNF_UNEXP] = {
|
||||
"ranap:cs:rab_act:cnf_unexp", "CS RAB Activations confirmed in unexpected state" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_FAIL] = {
|
||||
"ranap:ps:rab_act:fail", "PS RAB Activations failed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_FAIL] = {
|
||||
"ranap:cs:rab_act:fail", "CS RAB Activations failed" },
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_FAIL_UNEXP] = {
|
||||
"ranap:ps:rab_act:fail_unexp", "PS RAB Activations failed in unexpected state" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_FAIL_UNEXP] = {
|
||||
"ranap:cs:rab_act:fail_unexp", "CS RAB Activations failed in unexpected state" },
|
||||
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_MOD_REQ] = {
|
||||
"ranap:ps:rab_mod:req", "PS RAB Modifications requested" },
|
||||
[HNB_CTR_RANAP_CS_RAB_MOD_REQ] = {
|
||||
"ranap:cs:rab_mod:req", "CS RAB Modifications requested" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_MOD_CNF] = {
|
||||
"ranap:ps:rab_mod:cnf", "PS RAB Modifications confirmed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_MOD_CNF] = {
|
||||
"ranap:cs:rab_mod:cnf", "CS RAB Modifications confirmed" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_MOD_FAIL] = {
|
||||
"ranap:ps:rab_mod:fail", "PS RAB Modifications failed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_MOD_FAIL] = {
|
||||
"ranap:cs:rab_mod:fail", "CS RAB Modifications failed" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_REQ] = {
|
||||
"ranap:ps:rab_rel:req:normal", "PS RAB Release requested (by CN), normal" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_REQ] = {
|
||||
"ranap:cs:rab_rel:req:normal", "CS RAB Release requested (by CN), normal" },
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL] = {
|
||||
"ranap:ps:rab_rel:req:abnormal", "PS RAB Release requested (by CN), abnormal" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_REQ_ABNORMAL] = {
|
||||
"ranap:cs:rab_rel:req:abnormal", "CS RAB Release requested (by CN), abnormal" },
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_REQ_UNEXP] = {
|
||||
"ranap:ps:rab_rel:req:unexp", "PS RAB Release requested (by CN) in unexpected state" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_REQ_UNEXP] = {
|
||||
"ranap:cs:rab_rel:req:unexp", "CS RAB Release requested (by CN) in unexpected state" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_CNF] = {
|
||||
"ranap:ps:rab_rel:cnf", "PS RAB Release confirmed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_CNF] = {
|
||||
"ranap:cs:rab_rel:cnf", "CS RAB Release confirmed" },
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_CNF_UNEXP] = {
|
||||
"ranap:ps:rab_rel:cnf_unexp", "PS RAB Release confirmed in unexpected state" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_CNF_UNEXP] = {
|
||||
"ranap:cs:rab_rel:cnf_unexp", "CS RAB Release confirmed in unexpected state" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_FAIL] = {
|
||||
"ranap:ps:rab_rel:fail", "PS RAB Release failed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_FAIL] = {
|
||||
"ranap:cs:rab_rel:fail", "CS RAB Release failed" },
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_FAIL_UNEXP] = {
|
||||
"ranap:ps:rab_rel:fail_unexp", "PS RAB Release failed in unexpected state" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_FAIL_UNEXP] = {
|
||||
"ranap:cs:rab_rel:fail_unexp", "CS RAB Release failed in unexpected state" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT] = {
|
||||
"ranap:ps:rab_rel:implicit:normal", "PS RAB Release implicit (during Iu Release), normal" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT] = {
|
||||
"ranap:cs:rab_rel:implicit:normal", "CS RAB Release implicit (during Iu Release), normal" },
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL] = {
|
||||
"ranap:ps:rab_rel:implicit:abnormal", "PS RAB Release implicit (during Iu Release), abnormal" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT_ABNORMAL] = {
|
||||
"ranap:cs:rab_rel:implicit:abnormal", "CS RAB Release implicit (during Iu Release), abnormal" },
|
||||
|
||||
[HNB_CTR_RUA_ERR_IND] = {
|
||||
"rua:error_ind", "Received RUA Error Indications" },
|
||||
|
||||
[HNB_CTR_RUA_PS_CONNECT_UL] = {
|
||||
"rua:ps:connect:ul", "Received RUA Connect requests (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_CONNECT_UL] = {
|
||||
"rua:cs:connect:ul", "Received RUA Connect requests (CS Domain)" },
|
||||
|
||||
[HNB_CTR_RUA_PS_DISCONNECT_UL] = {
|
||||
"rua:ps:disconnect:ul", "Received RUA Disconnect requests in uplink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DISCONNECT_UL] = {
|
||||
"rua:cs:disconnect:ul", "Received RUA Disconnect requests in uplink (CS Domain)" },
|
||||
[HNB_CTR_RUA_PS_DISCONNECT_DL] = {
|
||||
"rua:ps:disconnect:dl", "Transmitted RUA Disconnect requests in downlink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DISCONNECT_DL] = {
|
||||
"rua:cs:disconnect:dl", "Transmitted RUA Disconnect requests in downlink (CS Domain)" },
|
||||
|
||||
[HNB_CTR_RUA_PS_DT_UL] = {
|
||||
"rua:ps:direct_transfer:ul", "Received RUA DirectTransfer in uplink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DT_UL] = {
|
||||
"rua:cs:direct_transfer:ul", "Received RUA DirectTransfer in uplink (CS Domain)" },
|
||||
[HNB_CTR_RUA_PS_DT_DL] = {
|
||||
"rua:ps:direct_transfer:dl", "Transmitted RUA DirectTransfer in downlink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DT_DL] = {
|
||||
"rua:cs:direct_transfer:dl", "Transmitted RUA DirectTransfer in downlink (CS Domain)" },
|
||||
|
||||
[HNB_CTR_RUA_UDT_UL] = {
|
||||
"rua:unit_data:ul", "Received RUA UnitData (UDT) in uplink" },
|
||||
[HNB_CTR_RUA_UDT_DL] = {
|
||||
"rua:unit_data:dl", "Transmitted RUA UnitData (UDT) in downlink" },
|
||||
|
||||
[HNB_CTR_PS_PAGING_ATTEMPTED] = {
|
||||
"paging:ps:attempted", "Transmitted PS Paging requests" },
|
||||
[HNB_CTR_CS_PAGING_ATTEMPTED] = {
|
||||
"paging:cs:attempted", "Transmitted CS Paging requests" },
|
||||
|
||||
[HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL] = {
|
||||
"rab:cs:active_milliseconds:total", "Cumulative number of milliseconds of CS RAB activity" },
|
||||
|
||||
[HNB_CTR_DTAP_CS_LU_REQ] = { "dtap:cs:location_update:req", "CS Location Update Requests" },
|
||||
[HNB_CTR_DTAP_CS_LU_ACC] = { "dtap:cs:location_update:accept", "CS Location Update Accepts" },
|
||||
[HNB_CTR_DTAP_CS_LU_REJ] = { "dtap:cs:location_update:reject", "CS Location Update Rejects" },
|
||||
|
||||
[HNB_CTR_DTAP_PS_ATT_REQ] = { "dtap:ps:attach:req", "PS Attach Requests" },
|
||||
[HNB_CTR_DTAP_PS_ATT_ACK] = { "dtap:ps:attach:accept", "PS Attach Accepts" },
|
||||
[HNB_CTR_DTAP_PS_ATT_REJ] = { "dtap:ps:attach:reject", "PS Attach Rejects" },
|
||||
|
||||
[HNB_CTR_DTAP_PS_RAU_REQ] = { "dtap:ps:routing_area_update:req", "PS Routing Area Update Requests" },
|
||||
[HNB_CTR_DTAP_PS_RAU_ACK] = { "dtap:ps:routing_area_update:accept", "PS Routing Area Update Accepts" },
|
||||
[HNB_CTR_DTAP_PS_RAU_REJ] = { "dtap:ps:routing_area_update:reject", "PS Routing Area Update Rejects" },
|
||||
|
||||
[HNB_CTR_GTPU_PACKETS_UL] = {
|
||||
"gtpu:packets:ul",
|
||||
"Count of GTP-U packets received from the HNB",
|
||||
},
|
||||
[HNB_CTR_GTPU_TOTAL_BYTES_UL] = {
|
||||
"gtpu:total_bytes:ul",
|
||||
"Count of total GTP-U bytes received from the HNB, including the GTP-U/UDP/IP headers",
|
||||
},
|
||||
[HNB_CTR_GTPU_UE_BYTES_UL] = {
|
||||
"gtpu:ue_bytes:ul",
|
||||
"Assuming an IP header length of 20 bytes, GTP-U bytes received from the HNB, excluding the GTP-U/UDP/IP headers",
|
||||
},
|
||||
[HNB_CTR_GTPU_PACKETS_DL] = {
|
||||
"gtpu:packets:dl",
|
||||
"Count of GTP-U packets sent to the HNB",
|
||||
},
|
||||
[HNB_CTR_GTPU_TOTAL_BYTES_DL] = {
|
||||
"gtpu:total_bytes:dl",
|
||||
"Count of total GTP-U bytes sent to the HNB, including the GTP-U/UDP/IP headers",
|
||||
},
|
||||
[HNB_CTR_GTPU_UE_BYTES_DL] = {
|
||||
"gtpu:ue_bytes:dl",
|
||||
"Assuming an IP header length of 20 bytes, GTP-U bytes sent to the HNB, excluding the GTP-U/UDP/IP headers",
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
const struct rate_ctr_group_desc hnb_ctrg_desc = {
|
||||
"hnb",
|
||||
"hNodeB",
|
||||
OSMO_STATS_CLASS_GLOBAL,
|
||||
ARRAY_SIZE(hnb_ctr_description),
|
||||
hnb_ctr_description,
|
||||
};
|
||||
|
||||
const struct osmo_stat_item_desc hnb_stat_desc[] = {
|
||||
[HNB_STAT_UPTIME_SECONDS] = { "uptime:seconds", "Seconds of uptime", "s", 60, 0 },
|
||||
};
|
||||
|
||||
const struct osmo_stat_item_group_desc hnb_statg_desc = {
|
||||
.group_name_prefix = "hnb",
|
||||
.group_description = "hNodeB",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_items = ARRAY_SIZE(hnb_stat_desc),
|
||||
.item_desc = hnb_stat_desc,
|
||||
};
|
||||
|
||||
static void hnb_persistent_disconnected_timeout_cb(void *data)
|
||||
{
|
||||
hnb_persistent_free(data);
|
||||
}
|
||||
|
||||
static void hnb_persistent_disconnected_timeout_schedule(struct hnb_persistent *hnbp)
|
||||
{
|
||||
unsigned long period_s = osmo_tdef_get(hnbgw_T_defs, -35, OSMO_TDEF_S, 60*60*24*7);
|
||||
if (period_s < 1) {
|
||||
LOG_HNBP(hnbp, LOGL_INFO,
|
||||
"timer X35 is zero, not setting a disconnected timeout for this hnb-persistent instance.\n");
|
||||
return;
|
||||
}
|
||||
/* It is fine if the timer is already active, osmo_timer_del() is done implicitly by the osmo_timer API. */
|
||||
osmo_timer_setup(&hnbp->disconnected_timeout, hnb_persistent_disconnected_timeout_cb, hnbp);
|
||||
osmo_timer_schedule(&hnbp->disconnected_timeout, period_s, 0);
|
||||
}
|
||||
|
||||
struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id)
|
||||
{
|
||||
struct hnb_persistent *hnbp = talloc_zero(g_hnbgw, struct hnb_persistent);
|
||||
if (!hnbp)
|
||||
return NULL;
|
||||
|
||||
hnbp->id = *id;
|
||||
hnbp->id_str = talloc_strdup(hnbp, umts_cell_id_to_str(id));
|
||||
hnbp->ctrs = rate_ctr_group_alloc(hnbp, &hnb_ctrg_desc, 0);
|
||||
if (!hnbp->ctrs)
|
||||
goto out_free;
|
||||
rate_ctr_group_set_name(hnbp->ctrs, hnbp->id_str);
|
||||
hnbp->statg = osmo_stat_item_group_alloc(hnbp, &hnb_statg_desc, 0);
|
||||
if (!hnbp->statg)
|
||||
goto out_free_ctrs;
|
||||
osmo_stat_item_group_set_name(hnbp->statg, hnbp->id_str);
|
||||
|
||||
llist_add(&hnbp->list, &g_hnbgw->hnb_persistent_list);
|
||||
hash_add(g_hnbgw->hnb_persistent_by_id, &hnbp->node_by_id, umts_cell_id_hash(&hnbp->id));
|
||||
|
||||
if (g_hnbgw->nft_kpi.active)
|
||||
nft_kpi_hnb_persistent_add(hnbp);
|
||||
|
||||
/* Normally the disconnected timer runs only when the hNodeB is not currently connected on Iuh. This here is paranoia:
|
||||
* In case we have to HNBAP HNB Register Reject, the disconnected timer should be active on this unused hnbp.
|
||||
* On success, hnb_persistent_registered() will stop the disconnected timer directly after this. */
|
||||
hnb_persistent_disconnected_timeout_schedule(hnbp);
|
||||
|
||||
return hnbp;
|
||||
|
||||
out_free_ctrs:
|
||||
rate_ctr_group_free(hnbp->ctrs);
|
||||
out_free:
|
||||
talloc_free(hnbp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct hnb_persistent *hnb_persistent_find_by_id(const struct umts_cell_id *id)
|
||||
{
|
||||
struct hnb_persistent *hnbp;
|
||||
uint32_t id_hash = umts_cell_id_hash(id);
|
||||
hash_for_each_possible(g_hnbgw->hnb_persistent_by_id, hnbp, node_by_id, id_hash) {
|
||||
if (umts_cell_id_equal(&hnbp->id, id))
|
||||
return hnbp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Read the peer's remote IP address from the Iuh conn's fd, and set up GTP-U counters for that remote address. */
|
||||
static void hnb_persistent_update_remote_addr(struct hnb_persistent *hnbp)
|
||||
{
|
||||
socklen_t socklen;
|
||||
struct osmo_sockaddr osa;
|
||||
struct osmo_sockaddr_str remote_str;
|
||||
int fd;
|
||||
|
||||
fd = osmo_stream_srv_get_fd(hnbp->ctx->conn);
|
||||
if (fd < 0) {
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "no active socket fd, cannot set up traffic counters\n");
|
||||
return;
|
||||
}
|
||||
|
||||
socklen = sizeof(struct osmo_sockaddr);
|
||||
if (getpeername(fd, &osa.u.sa, &socklen)) {
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "cannot read remote address, cannot set up traffic counters\n");
|
||||
return;
|
||||
}
|
||||
if (osmo_sockaddr_str_from_osa(&remote_str, &osa)) {
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "cannot parse remote address, cannot set up traffic counters\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* We got the remote address from the Iuh link (RUA), and now we are blatantly assuming that the hNodeB has its
|
||||
* GTP endpoint on the same IP address, just with UDP port 2152 (the fixed GTP port as per 3GPP spec). */
|
||||
remote_str.port = 2152;
|
||||
|
||||
if (nft_kpi_hnb_start(hnbp, &remote_str))
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "failed to set up traffic counters\n");
|
||||
}
|
||||
|
||||
/* Whenever HNBAP registers a HNB, hnbgw_hnbap.c calls this function to let the hnb_persistent update its state to the
|
||||
* (new) remote address being active. When calling this function, a hnbp->ctx should be present, with an active
|
||||
* osmo_stream_srv conn. */
|
||||
void hnb_persistent_registered(struct hnb_persistent *hnbp)
|
||||
{
|
||||
if (!hnbp->ctx) {
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "hnb_persistent_registered() invoked, but there is no hnb_ctx\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* The hNodeB is now connected, i.e. not disconnected. */
|
||||
osmo_timer_del(&hnbp->disconnected_timeout);
|
||||
|
||||
/* start counting traffic */
|
||||
if (g_hnbgw->nft_kpi.active)
|
||||
hnb_persistent_update_remote_addr(hnbp);
|
||||
}
|
||||
|
||||
/* Whenever a HNB is regarded as no longer registered (HNBAP HNB De-Register, or the Iuh link drops), this function is
|
||||
* called to to let the hnb_persistent update its state to the hNodeB being disconnected. Clear the ctx->persistent and
|
||||
* hnbp->ctx relations; do not delete the hnb_persistent instance. */
|
||||
void hnb_persistent_deregistered(struct hnb_persistent *hnbp)
|
||||
{
|
||||
/* clear out cross references of hnb_context and hnb_persistent */
|
||||
if (hnbp->ctx) {
|
||||
if (hnbp->ctx->persistent == hnbp)
|
||||
hnbp->ctx->persistent = NULL;
|
||||
hnbp->ctx = NULL;
|
||||
}
|
||||
|
||||
/* stop counting traffic */
|
||||
nft_kpi_hnb_stop(hnbp);
|
||||
|
||||
/* The hNodeB is now disconnected. Clear out hnb_persistent when the disconnected timeout has passed. */
|
||||
hnb_persistent_disconnected_timeout_schedule(hnbp);
|
||||
}
|
||||
|
||||
void hnb_persistent_free(struct hnb_persistent *hnbp)
|
||||
{
|
||||
/* FIXME: check if in use? */
|
||||
osmo_timer_del(&hnbp->disconnected_timeout);
|
||||
nft_kpi_hnb_stop(hnbp);
|
||||
nft_kpi_hnb_persistent_remove(hnbp);
|
||||
osmo_stat_item_group_free(hnbp->statg);
|
||||
rate_ctr_group_free(hnbp->ctrs);
|
||||
llist_del(&hnbp->list);
|
||||
hash_del(&hnbp->node_by_id);
|
||||
talloc_free(hnbp);
|
||||
}
|
||||
|
||||
/* return the amount of time the HNB is up (hnbp->ctx != NULL) or down (hnbp->ctx == NULL) */
|
||||
unsigned long long hnbp_get_updowntime(const struct hnb_persistent *hnbp)
|
||||
{
|
||||
struct timespec tp;
|
||||
|
||||
if (!hnbp->updowntime)
|
||||
return 0;
|
||||
|
||||
if (osmo_clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
|
||||
return 0;
|
||||
|
||||
return difftime(tp.tv_sec, hnbp->updowntime);
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
/* kitchen sink for OsmoHNBGW implementation */
|
||||
|
||||
/* (C) 2015,2024 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2016-2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* (C) 2016-2025 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -40,6 +40,8 @@
|
||||
#include <osmocom/pfcp/pfcp_proto.h>
|
||||
#endif
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_hnbap.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
@@ -56,25 +58,17 @@ const struct value_string ranap_domain_names[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
/* update the active RAB duration rate_ctr for given HNB */
|
||||
static void hnb_store_rab_durations(struct hnb_context *hnb)
|
||||
|
||||
/* timer call-back: Update the HNB_STAT_UPTIME_SECONDS stat item of each hnb_persistent */
|
||||
static void hnbgw_store_hnb_uptime(void *data)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
struct timespec now;
|
||||
uint64_t elapsed_cs_rab_ms = 0;
|
||||
struct hnb_persistent *hnbp;
|
||||
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
/* iterate over all context_maps (subscribers) */
|
||||
llist_for_each_entry(map, &hnb->map_list, hnb_list) {
|
||||
/* skip any PS maps, we care about CS RABs only here */
|
||||
if (map->is_ps)
|
||||
continue;
|
||||
elapsed_cs_rab_ms += mgw_fsm_get_elapsed_ms(map, &now);
|
||||
llist_for_each_entry(hnbp, &g_hnbgw->hnb_persistent_list, list) {
|
||||
HNBP_STAT_SET(hnbp, HNB_STAT_UPTIME_SECONDS, hnbp->ctx != NULL ? hnbp_get_updowntime(hnbp) : 0);
|
||||
}
|
||||
|
||||
/* Export to rate countes. */
|
||||
rate_ctr_add(HNBP_CTR(hnb->persistent, HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL), elapsed_cs_rab_ms);
|
||||
osmo_timer_schedule(&g_hnbgw->store_uptime_timer, STORE_UPTIME_INTERVAL, 0);
|
||||
}
|
||||
|
||||
static void hnbgw_store_hnb_rab_durations(void *data)
|
||||
@@ -91,7 +85,6 @@ static void hnbgw_store_hnb_rab_durations(void *data)
|
||||
osmo_timer_schedule(&g_hnbgw->hnb_store_rab_durations_timer, HNB_STORE_RAB_DURATIONS_INTERVAL, 0);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* UE Context
|
||||
***********************************************************************/
|
||||
@@ -101,764 +94,6 @@ uint32_t get_next_ue_ctx_id(void)
|
||||
return g_hnbgw->next_ue_ctx_id++;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* HNB Context
|
||||
***********************************************************************/
|
||||
|
||||
/* look-up HNB context by id. Used from CTRL */
|
||||
static struct hnb_context *hnb_context_by_id(uint32_t cid)
|
||||
{
|
||||
struct hnb_context *hnb;
|
||||
|
||||
llist_for_each_entry(hnb, &g_hnbgw->hnb_list, list) {
|
||||
if (hnb->id.cid == cid)
|
||||
return hnb;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look-up HNB context by identity_info. Used from VTY */
|
||||
struct hnb_context *hnb_context_by_identity_info(const char *identity_info)
|
||||
{
|
||||
struct hnb_context *hnb;
|
||||
|
||||
llist_for_each_entry(hnb, &g_hnbgw->hnb_list, list) {
|
||||
if (strcmp(identity_info, hnb->identity_info) == 0)
|
||||
return hnb;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int hnb_read_cb(struct osmo_stream_srv *conn);
|
||||
static int hnb_closed_cb(struct osmo_stream_srv *conn);
|
||||
|
||||
static struct hnb_context *hnb_context_alloc(struct osmo_stream_srv_link *link, int new_fd)
|
||||
{
|
||||
struct hnb_context *ctx;
|
||||
|
||||
ctx = talloc_zero(g_hnbgw, struct hnb_context);
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
INIT_LLIST_HEAD(&ctx->map_list);
|
||||
|
||||
ctx->conn = osmo_stream_srv_create(g_hnbgw, link, new_fd, hnb_read_cb, hnb_closed_cb, ctx);
|
||||
if (!ctx->conn) {
|
||||
LOGP(DMAIN, LOGL_INFO, "error while creating connection\n");
|
||||
talloc_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
llist_add_tail(&ctx->list, &g_hnbgw->hnb_list);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
int umts_cell_id_to_str_buf(char *buf, size_t buflen, const struct umts_cell_id *ucid)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_APPEND_NOLEN(sb, osmo_plmn_name_buf, &ucid->plmn);
|
||||
OSMO_STRBUF_PRINTF(sb, "-L%u-R%u-S%u-C%u", ucid->lac, ucid->rac, ucid->sac, ucid->cid);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *umts_cell_id_to_str_c(void *ctx, const struct umts_cell_id *ucid)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", umts_cell_id_to_str_buf, ucid)
|
||||
}
|
||||
|
||||
const char *umts_cell_id_to_str(const struct umts_cell_id *ucid)
|
||||
{
|
||||
return umts_cell_id_to_str_c(OTC_SELECT, ucid);
|
||||
}
|
||||
|
||||
/* Useful to index a hash table by struct umts_cell_id. */
|
||||
uint32_t umts_cell_id_hash(const struct umts_cell_id *ucid)
|
||||
{
|
||||
return osmo_jhash(ucid, sizeof(*ucid), 0x423423);
|
||||
}
|
||||
|
||||
/* parse a string representation of an umts_cell_id into its decoded representation */
|
||||
int umts_cell_id_from_str(struct umts_cell_id *ucid, const char *instr)
|
||||
{
|
||||
int rc;
|
||||
char buf[4];
|
||||
const char *pos = instr;
|
||||
const char *end;
|
||||
|
||||
/* We want to use struct umts_cell_id as hashtable key. If it ever happens to contain any padding bytes, make
|
||||
* sure everything is deterministically zero. */
|
||||
memset(ucid, 0, sizeof(*ucid));
|
||||
|
||||
/* read MCC */
|
||||
end = strchr(pos, '-');
|
||||
if (!end || end <= pos || (end - pos) >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
osmo_strlcpy(buf, pos, end - pos + 1);
|
||||
if (osmo_mcc_from_str(buf, &ucid->plmn.mcc))
|
||||
return -EINVAL;
|
||||
pos = end + 1;
|
||||
|
||||
/* read MNC -- here the number of leading zeros matters. */
|
||||
end = strchr(pos, '-');
|
||||
if (!end || end == pos || (end - pos) >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
osmo_strlcpy(buf, pos, end - pos + 1);
|
||||
if (osmo_mnc_from_str(buf, &ucid->plmn.mnc, &ucid->plmn.mnc_3_digits))
|
||||
return -EINVAL;
|
||||
pos = end + 1;
|
||||
|
||||
/* parse the rest, where leading zeros do not matter */
|
||||
rc = sscanf(pos, "L%" SCNu16 "-R%" SCNu8 "-S%" SCNu16 "-C%" SCNu32 "",
|
||||
&ucid->lac, &ucid->rac, &ucid->sac, &ucid->cid);
|
||||
if (rc < 0)
|
||||
return -errno;
|
||||
|
||||
if (rc != 4)
|
||||
return -EINVAL;
|
||||
|
||||
if (ucid->lac == 0 || ucid->lac == 0xffff)
|
||||
return -EINVAL;
|
||||
|
||||
/* CellIdentity in the ASN.1 syntax is a bit-string of 28 bits length */
|
||||
if (ucid->cid >= (1 << 28))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *hnb_context_name(struct hnb_context *ctx)
|
||||
{
|
||||
char *result;
|
||||
if (!ctx)
|
||||
return "NULL";
|
||||
|
||||
if (ctx->conn) {
|
||||
char hostbuf_r[INET6_ADDRSTRLEN];
|
||||
char portbuf_r[6];
|
||||
int fd = osmo_stream_srv_get_ofd(ctx->conn)->fd;
|
||||
|
||||
/* get remote addr */
|
||||
if (osmo_sock_get_ip_and_port(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r, sizeof(portbuf_r), false) == 0)
|
||||
result = talloc_asprintf(OTC_SELECT, "%s:%s", hostbuf_r, portbuf_r);
|
||||
else
|
||||
result = "?";
|
||||
} else {
|
||||
result = "disconnected";
|
||||
}
|
||||
|
||||
if (g_hnbgw->config.log_prefix_hnb_id)
|
||||
result = talloc_asprintf(OTC_SELECT, "%s %s", result, ctx->identity_info);
|
||||
else
|
||||
result = talloc_asprintf(OTC_SELECT, "%s %s", result, umts_cell_id_to_str(&ctx->id));
|
||||
return result;
|
||||
}
|
||||
|
||||
void hnb_context_release_ue_state(struct hnb_context *ctx)
|
||||
{
|
||||
struct hnbgw_context_map *map, *map2;
|
||||
|
||||
/* deactivate all context maps */
|
||||
llist_for_each_entry_safe(map, map2, &ctx->map_list, hnb_list) {
|
||||
context_map_hnb_released(map);
|
||||
/* hnbgw_context_map will remove itself from lists when it is ready. */
|
||||
}
|
||||
}
|
||||
|
||||
void hnb_context_release(struct hnb_context *ctx)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
LOGHNB(ctx, DMAIN, LOGL_INFO, "Releasing HNB context\n");
|
||||
|
||||
if (ctx->persistent) {
|
||||
struct timespec tp;
|
||||
int rc;
|
||||
rc = osmo_clock_gettime(CLOCK_MONOTONIC, &tp);
|
||||
ctx->persistent->updowntime = (rc < 0) ? 0 : tp.tv_sec;
|
||||
}
|
||||
|
||||
/* remove from the list of HNB contexts */
|
||||
llist_del(&ctx->list);
|
||||
|
||||
hnb_context_release_ue_state(ctx);
|
||||
|
||||
if (ctx->conn) { /* we own a conn, we must free it: */
|
||||
LOGHNB(ctx, DMAIN, LOGL_INFO, "Closing HNB SCTP connection %s\n",
|
||||
osmo_sock_get_name2(osmo_stream_srv_get_ofd(ctx->conn)->fd));
|
||||
/* Avoid our closed_cb calling hnb_context_release() again: */
|
||||
osmo_stream_srv_set_data(ctx->conn, NULL);
|
||||
osmo_stream_srv_destroy(ctx->conn);
|
||||
} /* else: we are called from closed_cb, so conn is being freed separately */
|
||||
|
||||
/* hnbgw_context_map are still listed in ctx->map_list, but we are freeing ctx. Remove all entries from the
|
||||
* list, but keep the hnbgw_context_map around for graceful release. They are also listed under
|
||||
* hnbgw_cnlink->map_list, and will remove themselves when ready. */
|
||||
while ((map = llist_first_entry_or_null(&ctx->map_list, struct hnbgw_context_map, hnb_list))) {
|
||||
llist_del(&map->hnb_list);
|
||||
map->hnb_ctx = NULL;
|
||||
}
|
||||
|
||||
/* remove back reference from hnb_persistent to context */
|
||||
if (ctx->persistent)
|
||||
hnb_persistent_deregistered(ctx->persistent);
|
||||
|
||||
talloc_free(ctx);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* HNB Persistent Data
|
||||
***********************************************************************/
|
||||
|
||||
const struct rate_ctr_desc hnb_ctr_description[] = {
|
||||
[HNB_CTR_IUH_ESTABLISHED] = {
|
||||
"iuh:established", "Number of times Iuh link was established" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_ERR_IND_UL] = {
|
||||
"ranap:ps:error_ind:ul", "Received ERROR Indications in Uplink (PS Domain)" },
|
||||
[HNB_CTR_RANAP_CS_ERR_IND_UL] = {
|
||||
"ranap:cs:error_ind:ul", "Received ERROR Indications in Uplink (PS Domain)" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RESET_REQ_UL] = {
|
||||
"ranap:ps:reset_req:ul", "Received RESET Requests in Uplink (PS Domain)" },
|
||||
[HNB_CTR_RANAP_CS_RESET_REQ_UL] = {
|
||||
"ranap:cs:reset_req:ul", "Received RESET Requests in Uplink (CS Domain)" },
|
||||
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_REQ] = {
|
||||
"ranap:ps:rab_act:req", "PS RAB Activations requested" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_REQ] = {
|
||||
"ranap:cs:rab_act:req", "CS RAB Activations requested" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_CNF] = {
|
||||
"ranap:ps:rab_act:cnf", "PS RAB Activations confirmed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_CNF] = {
|
||||
"ranap:cs:rab_act:cnf", "CS RAB Activations confirmed" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_FAIL] = {
|
||||
"ranap:ps:rab_act:fail", "PS RAB Activations failed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_FAIL] = {
|
||||
"ranap:cs:rab_act:fail", "CS RAB Activations failed" },
|
||||
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_MOD_REQ] = {
|
||||
"ranap:ps:rab_mod:req", "PS RAB Modifications requested" },
|
||||
[HNB_CTR_RANAP_CS_RAB_MOD_REQ] = {
|
||||
"ranap:cs:rab_mod:req", "CS RAB Modifications requested" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_MOD_CNF] = {
|
||||
"ranap:ps:rab_mod:cnf", "PS RAB Modifications confirmed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_MOD_CNF] = {
|
||||
"ranap:cs:rab_mod:cnf", "CS RAB Modifications confirmed" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_MOD_FAIL] = {
|
||||
"ranap:ps:rab_mod:fail", "PS RAB Modifications failed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_MOD_FAIL] = {
|
||||
"ranap:cs:rab_mod:fail", "CS RAB Modifications failed" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_REQ] = {
|
||||
"ranap:ps:rab_rel:req:normal", "PS RAB Release requested (by CN), normal" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_REQ] = {
|
||||
"ranap:cs:rab_rel:req:normal", "CS RAB Release requested (by CN), normal" },
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL] = {
|
||||
"ranap:ps:rab_rel:req:abnormal", "PS RAB Release requested (by CN), abnormal" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_REQ_ABNORMAL] = {
|
||||
"ranap:cs:rab_rel:req:abnormal", "CS RAB Release requested (by CN), abnormal" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_CNF] = {
|
||||
"ranap:ps:rab_rel:cnf", "PS RAB Release confirmed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_CNF] = {
|
||||
"ranap:cs:rab_rel:cnf", "CS RAB Release confirmed" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_FAIL] = {
|
||||
"ranap:ps:rab_rel:fail", "PS RAB Release failed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_FAIL] = {
|
||||
"ranap:cs:rab_rel:fail", "CS RAB Release failed" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT] = {
|
||||
"ranap:ps:rab_rel:implicit:normal", "PS RAB Release implicit (during Iu Release), normal" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT] = {
|
||||
"ranap:cs:rab_rel:implicit:normal", "CS RAB Release implicit (during Iu Release), normal" },
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL] = {
|
||||
"ranap:ps:rab_rel:implicit:abnormal", "PS RAB Release implicit (during Iu Release), abnormal" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT_ABNORMAL] = {
|
||||
"ranap:cs:rab_rel:implicit:abnormal", "CS RAB Release implicit (during Iu Release), abnormal" },
|
||||
|
||||
[HNB_CTR_RUA_ERR_IND] = {
|
||||
"rua:error_ind", "Received RUA Error Indications" },
|
||||
|
||||
[HNB_CTR_RUA_PS_CONNECT_UL] = {
|
||||
"rua:ps:connect:ul", "Received RUA Connect requests (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_CONNECT_UL] = {
|
||||
"rua:cs:connect:ul", "Received RUA Connect requests (CS Domain)" },
|
||||
|
||||
[HNB_CTR_RUA_PS_DISCONNECT_UL] = {
|
||||
"rua:ps:disconnect:ul", "Received RUA Disconnect requests in uplink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DISCONNECT_UL] = {
|
||||
"rua:cs:disconnect:ul", "Received RUA Disconnect requests in uplink (CS Domain)" },
|
||||
[HNB_CTR_RUA_PS_DISCONNECT_DL] = {
|
||||
"rua:ps:disconnect:dl", "Transmitted RUA Disconnect requests in downlink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DISCONNECT_DL] = {
|
||||
"rua:cs:disconnect:dl", "Transmitted RUA Disconnect requests in downlink (CS Domain)" },
|
||||
|
||||
[HNB_CTR_RUA_PS_DT_UL] = {
|
||||
"rua:ps:direct_transfer:ul", "Received RUA DirectTransfer in uplink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DT_UL] = {
|
||||
"rua:cs:direct_transfer:ul", "Received RUA DirectTransfer in uplink (CS Domain)" },
|
||||
[HNB_CTR_RUA_PS_DT_DL] = {
|
||||
"rua:ps:direct_transfer:dl", "Transmitted RUA DirectTransfer in downlink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DT_DL] = {
|
||||
"rua:cs:direct_transfer:dl", "Transmitted RUA DirectTransfer in downlink (CS Domain)" },
|
||||
|
||||
[HNB_CTR_RUA_UDT_UL] = {
|
||||
"rua:unit_data:ul", "Received RUA UnitData (UDT) in uplink" },
|
||||
[HNB_CTR_RUA_UDT_DL] = {
|
||||
"rua:unit_data:dl", "Transmitted RUA UnitData (UDT) in downlink" },
|
||||
|
||||
[HNB_CTR_PS_PAGING_ATTEMPTED] = {
|
||||
"paging:ps:attempted", "Transmitted PS Paging requests" },
|
||||
[HNB_CTR_CS_PAGING_ATTEMPTED] = {
|
||||
"paging:cs:attempted", "Transmitted CS Paging requests" },
|
||||
|
||||
[HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL] = {
|
||||
"rab:cs:active_milliseconds:total", "Cumulative number of milliseconds of CS RAB activity" },
|
||||
|
||||
[HNB_CTR_DTAP_CS_LU_REQ] = { "dtap:cs:location_update:req", "CS Location Update Requests" },
|
||||
[HNB_CTR_DTAP_CS_LU_ACC] = { "dtap:cs:location_update:accept", "CS Location Update Accepts" },
|
||||
[HNB_CTR_DTAP_CS_LU_REJ] = { "dtap:cs:location_update:reject", "CS Location Update Rejects" },
|
||||
|
||||
[HNB_CTR_DTAP_PS_ATT_REQ] = { "dtap:ps:attach:req", "PS Attach Requests" },
|
||||
[HNB_CTR_DTAP_PS_ATT_ACK] = { "dtap:ps:attach:accept", "PS Attach Accepts" },
|
||||
[HNB_CTR_DTAP_PS_ATT_REJ] = { "dtap:ps:attach:reject", "PS Attach Rejects" },
|
||||
|
||||
[HNB_CTR_DTAP_PS_RAU_REQ] = { "dtap:ps:routing_area_update:req", "PS Routing Area Update Requests" },
|
||||
[HNB_CTR_DTAP_PS_RAU_ACK] = { "dtap:ps:routing_area_update:accept", "PS Routing Area Update Accepts" },
|
||||
[HNB_CTR_DTAP_PS_RAU_REJ] = { "dtap:ps:routing_area_update:reject", "PS Routing Area Update Rejects" },
|
||||
|
||||
[HNB_CTR_GTPU_PACKETS_UL] = {
|
||||
"gtpu:packets:ul",
|
||||
"Count of GTP-U packets received from the HNB",
|
||||
},
|
||||
[HNB_CTR_GTPU_TOTAL_BYTES_UL] = {
|
||||
"gtpu:total_bytes:ul",
|
||||
"Count of total GTP-U bytes received from the HNB, including the GTP-U/UDP/IP headers",
|
||||
},
|
||||
[HNB_CTR_GTPU_UE_BYTES_UL] = {
|
||||
"gtpu:ue_bytes:ul",
|
||||
"Assuming an IP header length of 20 bytes, GTP-U bytes received from the HNB, excluding the GTP-U/UDP/IP headers",
|
||||
},
|
||||
[HNB_CTR_GTPU_PACKETS_DL] = {
|
||||
"gtpu:packets:dl",
|
||||
"Count of GTP-U packets sent to the HNB",
|
||||
},
|
||||
[HNB_CTR_GTPU_TOTAL_BYTES_DL] = {
|
||||
"gtpu:total_bytes:dl",
|
||||
"Count of total GTP-U bytes sent to the HNB, including the GTP-U/UDP/IP headers",
|
||||
},
|
||||
[HNB_CTR_GTPU_UE_BYTES_DL] = {
|
||||
"gtpu:ue_bytes:dl",
|
||||
"Assuming an IP header length of 20 bytes, GTP-U bytes sent to the HNB, excluding the GTP-U/UDP/IP headers",
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
const struct rate_ctr_group_desc hnb_ctrg_desc = {
|
||||
"hnb",
|
||||
"hNodeB",
|
||||
OSMO_STATS_CLASS_GLOBAL,
|
||||
ARRAY_SIZE(hnb_ctr_description),
|
||||
hnb_ctr_description,
|
||||
};
|
||||
|
||||
const struct osmo_stat_item_desc hnb_stat_desc[] = {
|
||||
[HNB_STAT_UPTIME_SECONDS] = { "uptime:seconds", "Seconds of uptime", "s", 60, 0 },
|
||||
};
|
||||
|
||||
const struct osmo_stat_item_group_desc hnb_statg_desc = {
|
||||
.group_name_prefix = "hnb",
|
||||
.group_description = "hNodeB",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_items = ARRAY_SIZE(hnb_stat_desc),
|
||||
.item_desc = hnb_stat_desc,
|
||||
};
|
||||
|
||||
static void hnb_persistent_disconnected_timeout_cb(void *data)
|
||||
{
|
||||
hnb_persistent_free(data);
|
||||
}
|
||||
|
||||
static void hnb_persistent_disconnected_timeout_schedule(struct hnb_persistent *hnbp)
|
||||
{
|
||||
unsigned long period_s = osmo_tdef_get(hnbgw_T_defs, -35, OSMO_TDEF_S, 60*60*24*7);
|
||||
if (period_s < 1) {
|
||||
LOG_HNBP(hnbp, LOGL_INFO,
|
||||
"timer X35 is zero, not setting a disconnected timeout for this hnb-persistent instance.\n");
|
||||
return;
|
||||
}
|
||||
/* It is fine if the timer is already active, osmo_timer_del() is done implicitly by the osmo_timer API. */
|
||||
osmo_timer_setup(&hnbp->disconnected_timeout, hnb_persistent_disconnected_timeout_cb, hnbp);
|
||||
osmo_timer_schedule(&hnbp->disconnected_timeout, period_s, 0);
|
||||
}
|
||||
|
||||
struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id)
|
||||
{
|
||||
struct hnb_persistent *hnbp = talloc_zero(g_hnbgw, struct hnb_persistent);
|
||||
if (!hnbp)
|
||||
return NULL;
|
||||
|
||||
hnbp->id = *id;
|
||||
hnbp->id_str = talloc_strdup(hnbp, umts_cell_id_to_str(id));
|
||||
hnbp->ctrs = rate_ctr_group_alloc(hnbp, &hnb_ctrg_desc, 0);
|
||||
if (!hnbp->ctrs)
|
||||
goto out_free;
|
||||
rate_ctr_group_set_name(hnbp->ctrs, hnbp->id_str);
|
||||
hnbp->statg = osmo_stat_item_group_alloc(hnbp, &hnb_statg_desc, 0);
|
||||
if (!hnbp->statg)
|
||||
goto out_free_ctrs;
|
||||
osmo_stat_item_group_set_name(hnbp->statg, hnbp->id_str);
|
||||
|
||||
llist_add(&hnbp->list, &g_hnbgw->hnb_persistent_list);
|
||||
hash_add(g_hnbgw->hnb_persistent_by_id, &hnbp->node_by_id, umts_cell_id_hash(&hnbp->id));
|
||||
|
||||
if (g_hnbgw->nft_kpi.active)
|
||||
nft_kpi_hnb_persistent_add(hnbp);
|
||||
|
||||
/* Normally the disconnected timer runs only when the hNodeB is not currently connected on Iuh. This here is paranoia:
|
||||
* In case we have to HNBAP HNB Register Reject, the disconnected timer should be active on this unused hnbp.
|
||||
* On success, hnb_persistent_registered() will stop the disconnected timer directly after this. */
|
||||
hnb_persistent_disconnected_timeout_schedule(hnbp);
|
||||
|
||||
return hnbp;
|
||||
|
||||
out_free_ctrs:
|
||||
rate_ctr_group_free(hnbp->ctrs);
|
||||
out_free:
|
||||
talloc_free(hnbp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct hnb_persistent *hnb_persistent_find_by_id(const struct umts_cell_id *id)
|
||||
{
|
||||
struct hnb_persistent *hnbp;
|
||||
uint32_t id_hash = umts_cell_id_hash(id);
|
||||
hash_for_each_possible (g_hnbgw->hnb_persistent_by_id, hnbp, node_by_id, id_hash) {
|
||||
if (umts_cell_id_equal(&hnbp->id, id))
|
||||
return hnbp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Read the peer's remote IP address from the Iuh conn's fd, and set up GTP-U counters for that remote address. */
|
||||
static void hnb_persistent_update_remote_addr(struct hnb_persistent *hnbp)
|
||||
{
|
||||
socklen_t socklen;
|
||||
struct osmo_sockaddr osa;
|
||||
struct osmo_sockaddr_str remote_str;
|
||||
int fd;
|
||||
|
||||
fd = osmo_stream_srv_get_fd(hnbp->ctx->conn);
|
||||
if (fd < 0) {
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "no active socket fd, cannot set up traffic counters\n");
|
||||
return;
|
||||
}
|
||||
|
||||
socklen = sizeof(struct osmo_sockaddr);
|
||||
if (getpeername(fd, &osa.u.sa, &socklen)) {
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "cannot read remote address, cannot set up traffic counters\n");
|
||||
return;
|
||||
}
|
||||
if (osmo_sockaddr_str_from_osa(&remote_str, &osa)) {
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "cannot parse remote address, cannot set up traffic counters\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* We got the remote address from the Iuh link (RUA), and now we are blatantly assuming that the hNodeB has its
|
||||
* GTP endpoint on the same IP address, just with UDP port 2152 (the fixed GTP port as per 3GPP spec). */
|
||||
remote_str.port = 2152;
|
||||
|
||||
if (nft_kpi_hnb_start(hnbp, &remote_str))
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "failed to set up traffic counters\n");
|
||||
}
|
||||
|
||||
/* Whenever HNBAP registers a HNB, hnbgw_hnbap.c calls this function to let the hnb_persistent update its state to the
|
||||
* (new) remote address being active. When calling this function, a hnbp->ctx should be present, with an active
|
||||
* osmo_stream_srv conn. */
|
||||
void hnb_persistent_registered(struct hnb_persistent *hnbp)
|
||||
{
|
||||
if (!hnbp->ctx) {
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "hnb_persistent_registered() invoked, but there is no hnb_ctx\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* The hNodeB is now connected, i.e. not disconnected. */
|
||||
osmo_timer_del(&hnbp->disconnected_timeout);
|
||||
|
||||
/* start counting traffic */
|
||||
if (g_hnbgw->nft_kpi.active)
|
||||
hnb_persistent_update_remote_addr(hnbp);
|
||||
}
|
||||
|
||||
/* Whenever a HNB is regarded as no longer registered (HNBAP HNB De-Register, or the Iuh link drops), this function is
|
||||
* called to to let the hnb_persistent update its state to the hNodeB being disconnected. Clear the ctx->persistent and
|
||||
* hnbp->ctx relations; do not delete the hnb_persistent instance. */
|
||||
void hnb_persistent_deregistered(struct hnb_persistent *hnbp)
|
||||
{
|
||||
/* clear out cross references of hnb_context and hnb_persistent */
|
||||
if (hnbp->ctx) {
|
||||
if (hnbp->ctx->persistent == hnbp)
|
||||
hnbp->ctx->persistent = NULL;
|
||||
hnbp->ctx = NULL;
|
||||
}
|
||||
|
||||
/* stop counting traffic */
|
||||
nft_kpi_hnb_stop(hnbp);
|
||||
|
||||
/* The hNodeB is now disconnected. Clear out hnb_persistent when the disconnected timeout has passed. */
|
||||
hnb_persistent_disconnected_timeout_schedule(hnbp);
|
||||
}
|
||||
|
||||
void hnb_persistent_free(struct hnb_persistent *hnbp)
|
||||
{
|
||||
/* FIXME: check if in use? */
|
||||
osmo_timer_del(&hnbp->disconnected_timeout);
|
||||
nft_kpi_hnb_stop(hnbp);
|
||||
nft_kpi_hnb_persistent_remove(hnbp);
|
||||
osmo_stat_item_group_free(hnbp->statg);
|
||||
rate_ctr_group_free(hnbp->ctrs);
|
||||
llist_del(&hnbp->list);
|
||||
hash_del(&hnbp->node_by_id);
|
||||
talloc_free(hnbp);
|
||||
}
|
||||
|
||||
/* return the amount of time the HNB is up (hnbp->ctx != NULL) or down (hnbp->ctx == NULL) */
|
||||
static unsigned long long hnbp_get_updowntime(const struct hnb_persistent *hnbp)
|
||||
{
|
||||
struct timespec tp;
|
||||
|
||||
if (!hnbp->updowntime)
|
||||
return 0;
|
||||
|
||||
if (osmo_clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
|
||||
return 0;
|
||||
|
||||
return difftime(tp.tv_sec, hnbp->updowntime);
|
||||
}
|
||||
|
||||
unsigned long long hnb_get_updowntime(const struct hnb_context *ctx)
|
||||
{
|
||||
if (!ctx->persistent)
|
||||
return 0;
|
||||
return hnbp_get_updowntime(ctx->persistent);
|
||||
}
|
||||
|
||||
/* timer call-back: Update the HNB_STAT_UPTIME_SECONDS stat item of each hnb_persistent */
|
||||
static void hnbgw_store_hnb_uptime(void *data)
|
||||
{
|
||||
struct hnb_persistent *hnbp;
|
||||
|
||||
llist_for_each_entry(hnbp, &g_hnbgw->hnb_persistent_list, list) {
|
||||
HNBP_STAT_SET(hnbp, HNB_STAT_UPTIME_SECONDS, hnbp->ctx != NULL ? hnbp_get_updowntime(hnbp) : 0);
|
||||
}
|
||||
|
||||
osmo_timer_schedule(&g_hnbgw->store_uptime_timer, STORE_UPTIME_INTERVAL, 0);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* SCTP Socket / stream handling
|
||||
***********************************************************************/
|
||||
|
||||
static int hnb_read_cb(struct osmo_stream_srv *conn)
|
||||
{
|
||||
struct hnb_context *hnb = osmo_stream_srv_get_data(conn);
|
||||
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
|
||||
struct msgb *msg = msgb_alloc(IUH_MSGB_SIZE, "Iuh rx");
|
||||
int rc;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
OSMO_ASSERT(hnb);
|
||||
/* we store a reference to the HomeNodeB in the msg->dest for the
|
||||
* benefit of various downstream processing functions */
|
||||
msg->dst = hnb;
|
||||
|
||||
rc = osmo_stream_srv_recv(conn, msg);
|
||||
/* Notification received */
|
||||
if (msgb_sctp_msg_flags(msg) & OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION) {
|
||||
union sctp_notification *notif = (union sctp_notification *)msgb_data(msg);
|
||||
rc = 0;
|
||||
switch (notif->sn_header.sn_type) {
|
||||
case SCTP_ASSOC_CHANGE:
|
||||
switch (notif->sn_assoc_change.sac_state) {
|
||||
case SCTP_COMM_LOST:
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE,
|
||||
"sctp_recvmsg(%s) = SCTP_COMM_LOST, closing conn\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
break;
|
||||
case SCTP_RESTART:
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE, "HNB SCTP conn RESTARTed, marking as HNBAP-unregistered\n");
|
||||
hnb->hnb_registered = false;
|
||||
hnb_context_release_ue_state(hnb);
|
||||
/* The tx queue may be quite full after an SCTP RESTART: (SYS#6113)
|
||||
* The link may have been flaky (a possible reason for the peer restarting the conn) and
|
||||
* hence the kernel socket Tx queue may be full (no ACKs coming back) and our own userspace
|
||||
* queue may contain plenty of oldish messages to be sent. Since the HNB will re-register after
|
||||
* this, we simply drop all those old messages: */
|
||||
osmo_stream_srv_clear_tx_queue(conn);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SCTP_SHUTDOWN_EVENT:
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE,
|
||||
"sctp_recvmsg(%s) = SCTP_SHUTDOWN_EVENT, closing conn\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
} else if (rc == -EAGAIN) {
|
||||
/* Older versions of osmo_stream_srv_recv() not supporting
|
||||
* msgb_sctp_msg_flags() may still return -EAGAIN when an sctp
|
||||
* notification is received. */
|
||||
rc = 0;
|
||||
goto out;
|
||||
} else if (rc < 0) {
|
||||
LOGHNB(hnb, DMAIN, LOGL_ERROR, "Error during sctp_recvmsg(%s)\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
goto out;
|
||||
} else if (rc == 0) {
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE, "Connection closed sctp_recvmsg(%s) = 0\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
goto out;
|
||||
} else {
|
||||
msgb_put(msg, rc);
|
||||
}
|
||||
|
||||
switch (msgb_sctp_ppid(msg)) {
|
||||
case IUH_PPI_HNBAP:
|
||||
hnb->hnbap_stream = msgb_sctp_stream(msg);
|
||||
rc = hnbgw_hnbap_rx(hnb, msg);
|
||||
break;
|
||||
case IUH_PPI_RUA:
|
||||
if (!hnb->hnb_registered) {
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE, "Discarding RUA as HNB is not registered\n");
|
||||
goto out;
|
||||
}
|
||||
hnb->rua_stream = msgb_sctp_stream(msg);
|
||||
rc = hnbgw_rua_rx(hnb, msg);
|
||||
break;
|
||||
case IUH_PPI_SABP:
|
||||
case IUH_PPI_RNA:
|
||||
case IUH_PPI_PUA:
|
||||
LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unimplemented SCTP PPID=%lu received\n", msgb_sctp_ppid(msg));
|
||||
rc = 0;
|
||||
break;
|
||||
default:
|
||||
LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unknown SCTP PPID=%lu received\n", msgb_sctp_ppid(msg));
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnb_closed_cb(struct osmo_stream_srv *conn)
|
||||
{
|
||||
struct hnb_context *hnb = osmo_stream_srv_get_data(conn);
|
||||
if (!hnb)
|
||||
return 0; /* hnb_context is being freed, nothing do be done */
|
||||
|
||||
/* hnb: conn became broken, let's release the associated hnb.
|
||||
* conn object is being freed after closed_cb(), so unassign it from hnb
|
||||
* if available to avoid it freeing it again: */
|
||||
hnb->conn = NULL;
|
||||
hnb_context_release(hnb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! call-back when the listen FD has something to read */
|
||||
int hnbgw_rua_accept_cb(struct osmo_stream_srv_link *srv, int fd)
|
||||
{
|
||||
struct hnb_context *ctx;
|
||||
|
||||
LOGP(DMAIN, LOGL_INFO, "New HNB SCTP connection %s\n",
|
||||
osmo_sock_get_name2(fd));
|
||||
|
||||
ctx = hnb_context_alloc(srv, fd);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_RO(hnb_info, "info");
|
||||
static int get_hnb_info(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct hnb_context *hnb = data;
|
||||
|
||||
cmd->reply = talloc_strdup(cmd, hnb->identity_info);
|
||||
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_RO(hnbs, "num-hnb");
|
||||
static int get_hnbs(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
cmd->reply = talloc_asprintf(cmd, "%u", llist_count(&g_hnbgw->hnb_list));
|
||||
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
int hnb_ctrl_cmds_install(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_hnbs);
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_HNB, &cmd_hnb_info);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int hnb_ctrl_node_lookup(void *data, vector vline, int *node_type, void **node_data, int *i)
|
||||
{
|
||||
const char *token = vector_slot(vline, *i);
|
||||
struct hnb_context *hnb;
|
||||
long num;
|
||||
|
||||
switch (*node_type) {
|
||||
case CTRL_NODE_ROOT:
|
||||
if (strcmp(token, "hnb") != 0)
|
||||
return 0;
|
||||
|
||||
(*i)++;
|
||||
|
||||
if (!ctrl_parse_get_num(vline, *i, &num))
|
||||
return -ERANGE;
|
||||
|
||||
hnb = hnb_context_by_id(num);
|
||||
if (!hnb)
|
||||
return -ENODEV;
|
||||
|
||||
*node_data = hnb;
|
||||
*node_type = CTRL_NODE_HNB;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int hnbgw_mgw_setup(void)
|
||||
{
|
||||
struct mgcp_client *mgcp_client_single;
|
||||
@@ -1000,35 +235,8 @@ void g_hnbgw_alloc(void *ctx)
|
||||
g_hnbgw->config.pfcp.remote_port = OSMO_PFCP_PORT;
|
||||
#endif
|
||||
|
||||
g_hnbgw->sccp.cnpool_iucs = (struct hnbgw_cnpool){
|
||||
.domain = DOMAIN_CS,
|
||||
.pool_name = "iucs",
|
||||
.peer_name = "msc",
|
||||
.default_remote_pc = DEFAULT_PC_MSC,
|
||||
.vty = {
|
||||
.nri_bitlen = OSMO_NRI_BITLEN_DEFAULT,
|
||||
.null_nri_ranges = osmo_nri_ranges_alloc(g_hnbgw),
|
||||
},
|
||||
.cnlink_ctrg_desc = &msc_ctrg_desc,
|
||||
|
||||
.ctrs = rate_ctr_group_alloc(g_hnbgw, &iucs_ctrg_desc, 0),
|
||||
};
|
||||
INIT_LLIST_HEAD(&g_hnbgw->sccp.cnpool_iucs.cnlinks);
|
||||
|
||||
g_hnbgw->sccp.cnpool_iups = (struct hnbgw_cnpool){
|
||||
.domain = DOMAIN_PS,
|
||||
.pool_name = "iups",
|
||||
.peer_name = "sgsn",
|
||||
.default_remote_pc = DEFAULT_PC_SGSN,
|
||||
.vty = {
|
||||
.nri_bitlen = OSMO_NRI_BITLEN_DEFAULT,
|
||||
.null_nri_ranges = osmo_nri_ranges_alloc(g_hnbgw),
|
||||
},
|
||||
.cnlink_ctrg_desc = &sgsn_ctrg_desc,
|
||||
|
||||
.ctrs = rate_ctr_group_alloc(g_hnbgw, &iups_ctrg_desc, 0),
|
||||
};
|
||||
INIT_LLIST_HEAD(&g_hnbgw->sccp.cnpool_iups.cnlinks);
|
||||
g_hnbgw->sccp.cnpool_iucs = hnbgw_cnpool_alloc(DOMAIN_CS);
|
||||
g_hnbgw->sccp.cnpool_iups = hnbgw_cnpool_alloc(DOMAIN_PS);
|
||||
|
||||
osmo_timer_setup(&g_hnbgw->store_uptime_timer, hnbgw_store_hnb_uptime, g_hnbgw);
|
||||
osmo_timer_schedule(&g_hnbgw->store_uptime_timer, STORE_UPTIME_INTERVAL, 0);
|
||||
|
@@ -37,323 +37,11 @@
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/hnbgw/hnbgw_sccp.h>
|
||||
#include <osmocom/hnbgw/hnbgw_ranap.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
|
||||
/***********************************************************************
|
||||
* Incoming primitives from SCCP User SAP
|
||||
***********************************************************************/
|
||||
|
||||
static struct hnbgw_cnlink *cnlink_from_addr(struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *calling_addr,
|
||||
const struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = NULL;
|
||||
cnlink = hnbgw_cnlink_find_by_addr(hsu, calling_addr);
|
||||
if (!cnlink) {
|
||||
LOG_HSI(hsu, DRANAP, LOGL_ERROR, "Rx from unknown SCCP peer: %s: %s\n",
|
||||
osmo_sccp_inst_addr_name(osmo_ss7_get_sccp(hsu->ss7), calling_addr),
|
||||
osmo_scu_prim_hdr_name_c(OTC_SELECT, oph));
|
||||
return NULL;
|
||||
}
|
||||
return cnlink;
|
||||
}
|
||||
|
||||
static struct hnbgw_context_map *map_from_conn_id(struct hnbgw_sccp_user *hsu, uint32_t conn_id,
|
||||
const struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
hash_for_each_possible(hsu->hnbgw_context_map_by_conn_id, map, hnbgw_sccp_user_entry, conn_id) {
|
||||
if (map->scu_conn_id == conn_id)
|
||||
return map;
|
||||
}
|
||||
LOGP(DRANAP, LOGL_ERROR, "Rx for unknown SCCP connection ID: %u: %s\n",
|
||||
conn_id, osmo_scu_prim_hdr_name_c(OTC_SELECT, oph));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int handle_cn_unitdata(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_unitdata_param *param,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = cnlink_from_addr(hsu, ¶m->calling_addr, oph);
|
||||
if (!cnlink)
|
||||
return -ENOENT;
|
||||
|
||||
if (param->called_addr.ssn != OSMO_SCCP_SSN_RANAP) {
|
||||
LOGP(DCN, LOGL_NOTICE, "N-UNITDATA.ind for unknown SSN %u\n",
|
||||
param->called_addr.ssn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return hnbgw_ranap_rx_udt_dl(cnlink, param, msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
}
|
||||
|
||||
static int handle_cn_conn_conf(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_connect_param *param,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
map = map_from_conn_id(hsu, param->conn_id, oph);
|
||||
if (!map || !map->cnlink)
|
||||
return -ENOENT;
|
||||
|
||||
LOGP(DCN, LOGL_DEBUG, "handle_cn_conn_conf() conn_id=%d, addrs: called=%s calling=%s responding=%s\n",
|
||||
param->conn_id,
|
||||
cnlink_sccp_addr_to_str(map->cnlink, ¶m->called_addr),
|
||||
cnlink_sccp_addr_to_str(map->cnlink, ¶m->calling_addr),
|
||||
cnlink_sccp_addr_to_str(map->cnlink, ¶m->responding_addr));
|
||||
|
||||
map_sccp_dispatch(map, MAP_SCCP_EV_RX_CONNECTION_CONFIRM, oph->msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_cn_data_ind(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_data_param *param,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
map = map_from_conn_id(hsu, param->conn_id, oph);
|
||||
if (!map || !map->cnlink)
|
||||
return -ENOENT;
|
||||
|
||||
return map_sccp_dispatch(map, MAP_SCCP_EV_RX_DATA_INDICATION, oph->msg);
|
||||
}
|
||||
|
||||
static int handle_cn_disc_ind(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_disconn_param *param,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
char cause_buf[128];
|
||||
|
||||
map = map_from_conn_id(hsu, param->conn_id, oph);
|
||||
if (!map || !map->cnlink)
|
||||
return -ENOENT;
|
||||
|
||||
LOGP(DCN, LOGL_DEBUG, "handle_cn_disc_ind() conn_id=%u responding_addr=%s cause=%s\n",
|
||||
param->conn_id,
|
||||
cnlink_sccp_addr_to_str(map->cnlink, ¶m->responding_addr),
|
||||
osmo_sua_sccp_cause_name(param->cause, cause_buf, sizeof(cause_buf)));
|
||||
|
||||
return map_sccp_dispatch(map, MAP_SCCP_EV_RX_RELEASED, oph->msg);
|
||||
}
|
||||
|
||||
static struct hnbgw_cnlink *_cnlink_find_by_remote_pc(struct hnbgw_cnpool *cnpool, struct osmo_ss7_instance *cs7, uint32_t pc)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) {
|
||||
if (!cnlink->hnbgw_sccp_user)
|
||||
continue;
|
||||
if (cnlink->hnbgw_sccp_user->ss7 != cs7)
|
||||
continue;
|
||||
if ((cnlink->remote_addr.presence & OSMO_SCCP_ADDR_T_PC) == 0)
|
||||
continue;
|
||||
if (cnlink->remote_addr.pc != pc)
|
||||
continue;
|
||||
return cnlink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find a cnlink by its remote sigtran point code on a given cs7 instance. */
|
||||
static struct hnbgw_cnlink *cnlink_find_by_remote_pc(struct osmo_ss7_instance *cs7, uint32_t pc)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
cnlink = _cnlink_find_by_remote_pc(&g_hnbgw->sccp.cnpool_iucs, cs7, pc);
|
||||
if (!cnlink)
|
||||
cnlink = _cnlink_find_by_remote_pc(&g_hnbgw->sccp.cnpool_iups, cs7, pc);
|
||||
return cnlink;
|
||||
}
|
||||
|
||||
static void handle_pcstate_ind(struct hnbgw_sccp_user *hsu, const struct osmo_scu_pcstate_param *pcst)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
bool connected;
|
||||
bool disconnected;
|
||||
struct osmo_ss7_instance *cs7 = hsu->ss7;
|
||||
|
||||
LOGP(DCN, LOGL_DEBUG, "N-PCSTATE ind: affected_pc=%u sp_status=%s remote_sccp_status=%s\n",
|
||||
pcst->affected_pc, osmo_sccp_sp_status_name(pcst->sp_status),
|
||||
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
|
||||
|
||||
/* If we don't care about that point-code, ignore PCSTATE. */
|
||||
cnlink = cnlink_find_by_remote_pc(cs7, pcst->affected_pc);
|
||||
if (!cnlink)
|
||||
return;
|
||||
|
||||
/* See if this marks the point code to have become available, or to have been lost.
|
||||
*
|
||||
* I want to detect two events:
|
||||
* - connection event (both indicators say PC is reachable).
|
||||
* - disconnection event (at least one indicator says the PC is not reachable).
|
||||
*
|
||||
* There are two separate incoming indicators with various possible values -- the incoming events can be:
|
||||
*
|
||||
* - neither connection nor disconnection indicated -- just indicating congestion
|
||||
* connected == false, disconnected == false --> do nothing.
|
||||
* - both incoming values indicate that we are connected
|
||||
* --> trigger connected
|
||||
* - both indicate we are disconnected
|
||||
* --> trigger disconnected
|
||||
* - one value indicates 'connected', the other indicates 'disconnected'
|
||||
* --> trigger disconnected
|
||||
*
|
||||
* Congestion could imply that we're connected, but it does not indicate that a PC's reachability changed, so no need to
|
||||
* trigger on that.
|
||||
*/
|
||||
connected = false;
|
||||
disconnected = false;
|
||||
|
||||
switch (pcst->sp_status) {
|
||||
case OSMO_SCCP_SP_S_ACCESSIBLE:
|
||||
connected = true;
|
||||
break;
|
||||
case OSMO_SCCP_SP_S_INACCESSIBLE:
|
||||
disconnected = true;
|
||||
break;
|
||||
default:
|
||||
case OSMO_SCCP_SP_S_CONGESTED:
|
||||
/* Neither connecting nor disconnecting */
|
||||
break;
|
||||
}
|
||||
|
||||
switch (pcst->remote_sccp_status) {
|
||||
case OSMO_SCCP_REM_SCCP_S_AVAILABLE:
|
||||
if (!disconnected)
|
||||
connected = true;
|
||||
break;
|
||||
case OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN:
|
||||
case OSMO_SCCP_REM_SCCP_S_UNEQUIPPED:
|
||||
case OSMO_SCCP_REM_SCCP_S_INACCESSIBLE:
|
||||
disconnected = true;
|
||||
connected = false;
|
||||
break;
|
||||
default:
|
||||
case OSMO_SCCP_REM_SCCP_S_CONGESTED:
|
||||
/* Neither connecting nor disconnecting */
|
||||
break;
|
||||
}
|
||||
|
||||
if (disconnected && cnlink_is_conn_ready(cnlink)) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE,
|
||||
"now unreachable: N-PCSTATE ind: pc=%u sp_status=%s remote_sccp_status=%s\n",
|
||||
pcst->affected_pc,
|
||||
osmo_sccp_sp_status_name(pcst->sp_status),
|
||||
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
|
||||
/* A previously usable cnlink has disconnected. Kick it back to DISC state. */
|
||||
cnlink_set_disconnected(cnlink);
|
||||
} else if (connected && !cnlink_is_conn_ready(cnlink)) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE,
|
||||
"now available: N-PCSTATE ind: pc=%u sp_status=%s remote_sccp_status=%s\n",
|
||||
pcst->affected_pc,
|
||||
osmo_sccp_sp_status_name(pcst->sp_status),
|
||||
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
|
||||
/* A previously unusable cnlink has become reachable. Trigger immediate RANAP RESET -- we would resend a
|
||||
* RESET either way, but we might as well do it now to speed up connecting. */
|
||||
cnlink_resend_reset(cnlink);
|
||||
}
|
||||
}
|
||||
|
||||
/* Entry point for primitives coming up from SCCP User SAP.
|
||||
* Ownership of oph->msg is transferred to us. */
|
||||
static int sccp_sap_up(struct osmo_prim_hdr *oph, void *ctx)
|
||||
{
|
||||
struct osmo_sccp_user *scu = ctx;
|
||||
struct hnbgw_sccp_user *hsu;
|
||||
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DCN, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph));
|
||||
|
||||
if (!scu) {
|
||||
LOGP(DCN, LOGL_ERROR,
|
||||
"sccp_sap_up(): NULL osmo_sccp_user, cannot send prim (sap %u prim %u op %d)\n",
|
||||
oph->sap, oph->primitive, oph->operation);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hsu = osmo_sccp_user_get_priv(scu);
|
||||
if (!hsu) {
|
||||
LOGP(DCN, LOGL_ERROR,
|
||||
"sccp_sap_up(): NULL hnbgw_sccp_user, cannot send prim (sap %u prim %u op %d)\n",
|
||||
oph->sap, oph->primitive, oph->operation);
|
||||
return -1;
|
||||
}
|
||||
|
||||
talloc_steal(OTC_SELECT, oph->msg);
|
||||
|
||||
switch (OSMO_PRIM_HDR(oph)) {
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
|
||||
rc = handle_cn_unitdata(hsu, &prim->u.unitdata, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
|
||||
rc = handle_cn_conn_conf(hsu, &prim->u.connect, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
|
||||
rc = handle_cn_data_ind(hsu, &prim->u.data, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
|
||||
rc = handle_cn_disc_ind(hsu, &prim->u.disconnect, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION):
|
||||
handle_pcstate_ind(hsu, &prim->u.pcstate);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGP(DCN, LOGL_ERROR,
|
||||
"Received unknown prim %u from SCCP USER SAP\n",
|
||||
OSMO_PRIM_HDR(oph));
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool addr_has_pc_and_ssn(const struct osmo_sccp_addr *addr)
|
||||
{
|
||||
if (!(addr->presence & OSMO_SCCP_ADDR_T_SSN))
|
||||
return false;
|
||||
if (!(addr->presence & OSMO_SCCP_ADDR_T_PC))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int resolve_addr_name(struct osmo_sccp_addr *dest, struct osmo_ss7_instance **ss7,
|
||||
const char *addr_name, const char *label,
|
||||
uint32_t default_pc)
|
||||
{
|
||||
if (!addr_name) {
|
||||
osmo_sccp_make_addr_pc_ssn(dest, default_pc, OSMO_SCCP_SSN_RANAP);
|
||||
if (label)
|
||||
LOGP(DCN, LOGL_INFO, "%s remote addr not configured, using default: %s\n", label,
|
||||
osmo_sccp_addr_name(*ss7, dest));
|
||||
return 0;
|
||||
}
|
||||
|
||||
*ss7 = osmo_sccp_addr_by_name(dest, addr_name);
|
||||
if (!*ss7) {
|
||||
if (label)
|
||||
LOGP(DCN, LOGL_ERROR, "%s remote addr: no such SCCP address book entry: '%s'\n",
|
||||
label, addr_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
osmo_sccp_addr_set_ssn(dest, OSMO_SCCP_SSN_RANAP);
|
||||
|
||||
if (!addr_has_pc_and_ssn(dest)) {
|
||||
if (label)
|
||||
LOGP(DCN, LOGL_ERROR, "Invalid/incomplete %s remote-addr: %s\n",
|
||||
label, osmo_sccp_addr_name(*ss7, dest));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hnbgw_cnpool_apply_cfg(struct hnbgw_cnpool *cnpool)
|
||||
{
|
||||
struct osmo_nri_range *r;
|
||||
@@ -361,187 +49,11 @@ void hnbgw_cnpool_apply_cfg(struct hnbgw_cnpool *cnpool)
|
||||
cnpool->use.nri_bitlen = cnpool->vty.nri_bitlen;
|
||||
|
||||
osmo_nri_ranges_free(cnpool->use.null_nri_ranges);
|
||||
cnpool->use.null_nri_ranges = osmo_nri_ranges_alloc(g_hnbgw);
|
||||
cnpool->use.null_nri_ranges = osmo_nri_ranges_alloc(cnpool);
|
||||
llist_for_each_entry(r, &cnpool->vty.null_nri_ranges->entries, entry)
|
||||
osmo_nri_ranges_add(cnpool->use.null_nri_ranges, r);
|
||||
}
|
||||
|
||||
static void hnbgw_cnlink_cfg_copy(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct osmo_nri_range *r;
|
||||
|
||||
osmo_talloc_replace_string(cnlink, &cnlink->use.remote_addr_name, cnlink->vty.remote_addr_name);
|
||||
|
||||
osmo_nri_ranges_free(cnlink->use.nri_ranges);
|
||||
cnlink->use.nri_ranges = osmo_nri_ranges_alloc(cnlink);
|
||||
llist_for_each_entry(r, &cnlink->vty.nri_ranges->entries, entry)
|
||||
osmo_nri_ranges_add(cnlink->use.nri_ranges, r);
|
||||
}
|
||||
|
||||
static bool hnbgw_cnlink_sccp_cfg_changed(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
if (cnlink->vty.remote_addr_name && cnlink->use.remote_addr_name) {
|
||||
struct osmo_ss7_instance *ss7;
|
||||
struct osmo_sccp_addr remote_addr = {};
|
||||
|
||||
/* Instead of comparing whether the address book entry names are different, actually resolve the
|
||||
* resulting SCCP address, and only restart the cnlink if the resulting address changed. */
|
||||
resolve_addr_name(&remote_addr, &ss7, cnlink->vty.remote_addr_name, NULL, DEFAULT_PC_HNBGW);
|
||||
if (osmo_sccp_addr_cmp(&remote_addr, &cnlink->remote_addr, OSMO_SCCP_ADDR_T_PC | OSMO_SCCP_ADDR_T_SSN))
|
||||
changed = true;
|
||||
} else if (cnlink->vty.remote_addr_name != cnlink->use.remote_addr_name) {
|
||||
/* One of them is NULL, the other is not. */
|
||||
changed = true;
|
||||
}
|
||||
|
||||
/* if more cnlink configuration is added in the future, it needs to be compared here. */
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static void hnbgw_cnlink_drop_sccp(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct hnbgw_context_map *map, *map2;
|
||||
|
||||
llist_for_each_entry_safe(map, map2, &cnlink->map_list, hnbgw_cnlink_entry) {
|
||||
map_sccp_dispatch(map, MAP_SCCP_EV_USER_ABORT, NULL);
|
||||
}
|
||||
|
||||
cnlink->hnbgw_sccp_user = NULL;
|
||||
}
|
||||
|
||||
static void hnbgw_cnlink_log_self(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct osmo_ss7_instance *ss7 = cnlink->hnbgw_sccp_user->ss7;
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "using: cs7-%u %s <-> %s %s %s\n",
|
||||
osmo_ss7_instance_get_id(ss7),
|
||||
/* printing the entire SCCP address is quite long, rather just print the point-code */
|
||||
osmo_ss7_pointcode_print(ss7, cnlink->hnbgw_sccp_user->local_addr.pc),
|
||||
osmo_ss7_pointcode_print2(ss7, cnlink->remote_addr.pc),
|
||||
cnlink->name, cnlink->use.remote_addr_name ? : "(default remote point-code)");
|
||||
}
|
||||
|
||||
/* If not present yet, set up all of osmo_ss7_instance, osmo_sccp_instance and hnbgw_sccp_user for the given cnlink.
|
||||
* The cs7 instance nr to use is determined by cnlink->remote_addr_name, or cs7 instance 0 if that is not present.
|
||||
* Set cnlink->hnbgw_sccp_user to the new SCCP instance. Return 0 on success, negative on error. */
|
||||
int hnbgw_cnlink_start_or_restart(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct osmo_ss7_instance *ss7 = NULL;
|
||||
struct osmo_sccp_instance *sccp;
|
||||
struct osmo_sccp_user *sccp_user;
|
||||
uint32_t local_pc;
|
||||
struct hnbgw_sccp_user *hsu;
|
||||
|
||||
/* If a hnbgw_sccp_user has already been set up, use that. */
|
||||
if (cnlink->hnbgw_sccp_user) {
|
||||
if (hnbgw_cnlink_sccp_cfg_changed(cnlink)) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "config changed, restarting SCCP\n");
|
||||
hnbgw_cnlink_drop_sccp(cnlink);
|
||||
} else {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "SCCP instance already set up, using %s\n",
|
||||
cnlink->hnbgw_sccp_user->name);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "no SCCP instance selected yet\n");
|
||||
}
|
||||
|
||||
/* Copy the current configuration: cnlink->use = cnlink->vty */
|
||||
hnbgw_cnlink_cfg_copy(cnlink);
|
||||
|
||||
/* Figure out which cs7 instance to use. If cnlink->remote_addr_name is set, it points to an address book entry
|
||||
* in a specific cs7 instance. If it is not set, leave ss7 == NULL to use cs7 instance 0. */
|
||||
if (cnlink->use.remote_addr_name) {
|
||||
if (resolve_addr_name(&cnlink->remote_addr, &ss7, cnlink->use.remote_addr_name, cnlink->name,
|
||||
DEFAULT_PC_HNBGW)) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "cannot initialize SCCP: there is no SCCP address named '%s'\n",
|
||||
cnlink->use.remote_addr_name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "remote-addr is '%s', using cs7 instance %u\n",
|
||||
cnlink->use.remote_addr_name, osmo_ss7_instance_get_id(ss7));
|
||||
} else {
|
||||
/* If no address is configured, use the default remote CN address, according to legacy behavior. */
|
||||
osmo_sccp_make_addr_pc_ssn(&cnlink->remote_addr, cnlink->pool->default_remote_pc, OSMO_SCCP_SSN_RANAP);
|
||||
}
|
||||
|
||||
/* If no 'cs7 instance' has been selected by the address, see if there already is a cs7 0 we can use by default.
|
||||
* If it doesn't exist, it will get created by osmo_sccp_simple_client_on_ss7_id(). */
|
||||
if (!ss7) {
|
||||
ss7 = osmo_ss7_instance_find(0);
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "Using default 'cs7 instance 0' (%s)\n", ss7 ? "already exists" : "will create");
|
||||
}
|
||||
|
||||
if (ss7) {
|
||||
/* Has another cnlink already set up an SCCP instance for this ss7? */
|
||||
llist_for_each_entry(hsu, &g_hnbgw->sccp.users, entry) {
|
||||
if (hsu->ss7 != ss7)
|
||||
continue;
|
||||
cnlink->hnbgw_sccp_user = hsu;
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "using existing SCCP instance %s on cs7 instance %u\n",
|
||||
hsu->name, osmo_ss7_instance_get_id(ss7));
|
||||
hnbgw_cnlink_log_self(cnlink);
|
||||
return 0;
|
||||
}
|
||||
/* else cnlink->hnbgw_sccp_user stays NULL and is set up below. */
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "cs7 instance %u has no configured SCCP instance yet\n", osmo_ss7_instance_get_id(ss7));
|
||||
}
|
||||
|
||||
/* No SCCP instance yet for this ss7. Create it. If no address name is given that resolves to a
|
||||
* particular cs7 instance above, use 'cs7 instance 0'. */
|
||||
sccp = osmo_sccp_simple_client_on_ss7_id(g_hnbgw,
|
||||
ss7 ? osmo_ss7_instance_get_id(ss7) : 0,
|
||||
cnlink->name,
|
||||
DEFAULT_PC_HNBGW,
|
||||
OSMO_SS7_ASP_PROT_M3UA,
|
||||
0,
|
||||
"localhost",
|
||||
-1,
|
||||
"localhost");
|
||||
if (!sccp) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Failed to configure SCCP on 'cs7 instance %u'\n",
|
||||
ss7 ? osmo_ss7_instance_get_id(ss7) : 0);
|
||||
return -1;
|
||||
}
|
||||
ss7 = osmo_sccp_get_ss7(sccp);
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "created SCCP instance on cs7 instance %u\n", osmo_ss7_instance_get_id(ss7));
|
||||
|
||||
/* Bind the SCCP user, using the cs7 instance's default point-code if one is configured, or osmo-hnbgw's default
|
||||
* local PC. */
|
||||
local_pc = osmo_ss7_instance_get_primary_pc(ss7);
|
||||
if (!osmo_ss7_pc_is_valid(local_pc))
|
||||
local_pc = DEFAULT_PC_HNBGW;
|
||||
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "binding OsmoHNBGW user to cs7 instance %u, local PC %u = %s\n",
|
||||
osmo_ss7_instance_get_id(ss7), local_pc, osmo_ss7_pointcode_print(ss7, local_pc));
|
||||
|
||||
sccp_user = osmo_sccp_user_bind_pc(sccp, "OsmoHNBGW", sccp_sap_up, OSMO_SCCP_SSN_RANAP, local_pc);
|
||||
if (!sccp_user) {
|
||||
LOGP(DCN, LOGL_ERROR, "Failed to init SCCP User\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
hsu = talloc_zero(cnlink, struct hnbgw_sccp_user);
|
||||
*hsu = (struct hnbgw_sccp_user){
|
||||
.name = talloc_asprintf(hsu, "cs7-%u.sccp", osmo_ss7_instance_get_id(ss7)),
|
||||
.ss7 = ss7,
|
||||
.sccp_user = sccp_user,
|
||||
};
|
||||
osmo_sccp_make_addr_pc_ssn(&hsu->local_addr, local_pc, OSMO_SCCP_SSN_RANAP);
|
||||
hash_init(hsu->hnbgw_context_map_by_conn_id);
|
||||
osmo_sccp_user_set_priv(sccp_user, hsu);
|
||||
|
||||
llist_add_tail(&hsu->entry, &g_hnbgw->sccp.users);
|
||||
|
||||
cnlink->hnbgw_sccp_user = hsu;
|
||||
|
||||
hnbgw_cnlink_log_self(cnlink);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hnbgw_cnpool_cnlinks_start_or_restart(struct hnbgw_cnpool *cnpool)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
@@ -571,31 +83,7 @@ struct hnbgw_cnlink *cnlink_get_nr(struct hnbgw_cnpool *cnpool, int nr, bool cre
|
||||
if (!create_if_missing)
|
||||
return NULL;
|
||||
|
||||
return cnlink_alloc(cnpool, nr);
|
||||
}
|
||||
|
||||
static bool cnlink_matches(const struct hnbgw_cnlink *cnlink, const struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *remote_addr)
|
||||
{
|
||||
if (cnlink->hnbgw_sccp_user != hsu)
|
||||
return false;
|
||||
if (osmo_sccp_addr_cmp(&cnlink->remote_addr, remote_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct hnbgw_cnlink *hnbgw_cnlink_find_by_addr(const struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_sccp_addr *remote_addr)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iucs.cnlinks, entry) {
|
||||
if (cnlink_matches(cnlink, hsu, remote_addr))
|
||||
return cnlink;
|
||||
}
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iups.cnlinks, entry) {
|
||||
if (cnlink_matches(cnlink, hsu, remote_addr))
|
||||
return cnlink;
|
||||
}
|
||||
return NULL;
|
||||
return hnbgw_cnlink_alloc(cnpool, nr);
|
||||
}
|
||||
|
||||
static bool is_cnlink_usable(struct hnbgw_cnlink *cnlink, bool is_emerg)
|
||||
@@ -617,7 +105,7 @@ static bool is_cnlink_usable(struct hnbgw_cnlink *cnlink, bool is_emerg)
|
||||
*/
|
||||
struct hnbgw_cnlink *hnbgw_cnlink_select(struct hnbgw_context_map *map)
|
||||
{
|
||||
struct hnbgw_cnpool *cnpool = map->is_ps ? &g_hnbgw->sccp.cnpool_iups : &g_hnbgw->sccp.cnpool_iucs;
|
||||
struct hnbgw_cnpool *cnpool = map->is_ps ? g_hnbgw->sccp.cnpool_iups : g_hnbgw->sccp.cnpool_iucs;
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
struct hnbgw_cnlink *round_robin_next = NULL;
|
||||
struct hnbgw_cnlink *round_robin_first = NULL;
|
||||
@@ -762,98 +250,6 @@ struct hnbgw_cnlink *hnbgw_cnlink_select(struct hnbgw_context_map *map)
|
||||
#undef LOG_NRI
|
||||
}
|
||||
|
||||
char *cnlink_sccp_addr_to_str(struct hnbgw_cnlink *cnlink, const struct osmo_sccp_addr *addr)
|
||||
{
|
||||
struct osmo_sccp_instance *sccp = cnlink_sccp(cnlink);
|
||||
if (!sccp)
|
||||
return osmo_sccp_addr_dump(addr);
|
||||
return osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sccp, addr);
|
||||
}
|
||||
|
||||
static const struct rate_ctr_desc cnlink_ctr_description[] = {
|
||||
[CNLINK_CTR_RANAP_RX_UDT_RESET] = {
|
||||
"ranap:rx:udt:reset",
|
||||
"RANAP Unitdata RESET messages received"
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_RESET_ACK] = {
|
||||
"ranap:rx:udt:reset_ack",
|
||||
"RANAP Unitdata RESET ACK messages received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_PAGING] = {
|
||||
"ranap:rx:udt:paging",
|
||||
"RANAP Unitdata PAGING messages received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_UNKNOWN] = {
|
||||
"ranap:rx:udt:unknown",
|
||||
"Unknown RANAP Unitdata messages received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_UNSUPPORTED] = {
|
||||
"ranap:rx:udt:unsupported",
|
||||
"Unsupported RANAP Unitdata messages received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_OVERLOAD_IND] = {
|
||||
"ranap:rx:udt:overload_ind",
|
||||
"RANAP Unitdata Overload Indications received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_ERROR_IND] = {
|
||||
"ranap:rx:udt:error_ind",
|
||||
"RANAP Unitdata Error Indications received",
|
||||
},
|
||||
|
||||
[CNLINK_CTR_RANAP_TX_UDT_RESET] = {
|
||||
"ranap:tx:udt:reset",
|
||||
"RANAP Unitdata RESET messages transmitted",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_TX_UDT_RESET_ACK] = {
|
||||
"ranap:tx:udt:reset_ack",
|
||||
"RANAP Unitdata RESET ACK messages transmitted",
|
||||
},
|
||||
|
||||
/* Indicators for CN pool usage */
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_NEW] = {
|
||||
"cnpool:subscr:new",
|
||||
"Complete Layer 3 requests assigned to this CN link by round-robin (no NRI was assigned yet).",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_REATTACH] = {
|
||||
"cnpool:subscr:reattach",
|
||||
"Complete Layer 3 requests assigned to this CN link by round-robin because the subscriber indicates a"
|
||||
" NULL-NRI (previously assigned by another CN link).",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_KNOWN] = {
|
||||
"cnpool:subscr:known",
|
||||
"Complete Layer 3 requests directed to this CN link because the subscriber indicates an NRI of this CN link.",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_PAGED] = {
|
||||
"cnpool:subscr:paged",
|
||||
"Paging Response directed to this CN link because the subscriber was recently paged by this CN link.",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_ATTACH_LOST] = {
|
||||
"cnpool:subscr:attach_lost",
|
||||
"A subscriber indicates an NRI value matching this CN link, but the CN link is not connected:"
|
||||
" a re-attach to another CN link (if available) was forced, with possible service failure.",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_EMERG_FORWARDED] = {
|
||||
"cnpool:emerg:forwarded",
|
||||
"Emergency call requests forwarded to this CN link.",
|
||||
},
|
||||
};
|
||||
|
||||
const struct rate_ctr_group_desc msc_ctrg_desc = {
|
||||
"msc",
|
||||
"MSC",
|
||||
OSMO_STATS_CLASS_GLOBAL,
|
||||
ARRAY_SIZE(cnlink_ctr_description),
|
||||
cnlink_ctr_description,
|
||||
};
|
||||
|
||||
const struct rate_ctr_group_desc sgsn_ctrg_desc = {
|
||||
"sgsn",
|
||||
"SGSN",
|
||||
OSMO_STATS_CLASS_GLOBAL,
|
||||
ARRAY_SIZE(cnlink_ctr_description),
|
||||
cnlink_ctr_description,
|
||||
};
|
||||
|
||||
static const struct rate_ctr_desc cnpool_ctr_description[] = {
|
||||
[CNPOOL_CTR_SUBSCR_NO_CNLINK] = {
|
||||
"cnpool:subscr:no_cnlink",
|
||||
@@ -884,3 +280,51 @@ const struct rate_ctr_group_desc iups_ctrg_desc = {
|
||||
ARRAY_SIZE(cnpool_ctr_description),
|
||||
cnpool_ctr_description,
|
||||
};
|
||||
|
||||
static int hnbgw_cnpool_talloc_destructor(struct hnbgw_cnpool *cnpool)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
osmo_nri_ranges_free(cnpool->vty.null_nri_ranges);
|
||||
cnpool->vty.null_nri_ranges = NULL;
|
||||
|
||||
while ((cnlink = llist_first_entry_or_null(&cnpool->cnlinks, struct hnbgw_cnlink, entry)))
|
||||
hnbgw_cnlink_term_and_free(cnlink);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct hnbgw_cnpool *hnbgw_cnpool_alloc(RANAP_CN_DomainIndicator_t domain)
|
||||
{
|
||||
struct hnbgw_cnpool *cnpool = talloc_zero(g_hnbgw, struct hnbgw_cnpool);
|
||||
OSMO_ASSERT(cnpool);
|
||||
|
||||
cnpool->domain = domain;
|
||||
cnpool->vty = (struct hnbgw_cnpool_cfg){
|
||||
.nri_bitlen = OSMO_NRI_BITLEN_DEFAULT,
|
||||
.null_nri_ranges = osmo_nri_ranges_alloc(cnpool),
|
||||
};
|
||||
OSMO_ASSERT(cnpool->vty.null_nri_ranges);
|
||||
INIT_LLIST_HEAD(&cnpool->cnlinks);
|
||||
|
||||
talloc_set_destructor(cnpool, hnbgw_cnpool_talloc_destructor);
|
||||
|
||||
switch (domain) {
|
||||
case DOMAIN_CS:
|
||||
cnpool->pool_name = "iucs";
|
||||
cnpool->peer_name = "msc";
|
||||
cnpool->default_remote_pc = DEFAULT_PC_MSC;
|
||||
cnpool->default_addr_name = DEFAULT_ADDR_NAME_MSC;
|
||||
cnpool->ctrs = rate_ctr_group_alloc(cnpool, &iucs_ctrg_desc, 0);
|
||||
break;
|
||||
case DOMAIN_PS:
|
||||
cnpool->pool_name = "iups";
|
||||
cnpool->peer_name = "sgsn";
|
||||
cnpool->default_remote_pc = DEFAULT_PC_SGSN;
|
||||
cnpool->default_addr_name = DEFAULT_ADDR_NAME_SGSN;
|
||||
cnpool->ctrs = rate_ctr_group_alloc(cnpool, &iups_ctrg_desc, 0);
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
return cnpool;
|
||||
}
|
||||
|
@@ -33,6 +33,8 @@
|
||||
#include <osmocom/hnbap/hnbap_common.h>
|
||||
#include <osmocom/ranap/iu_helpers.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbap/hnbap_ies_defs.h>
|
||||
|
||||
@@ -465,6 +467,16 @@ static int hnbgw_rx_hnb_deregister(struct hnb_context *ctx, ANY_t *in)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_asn1_octet_string_empty(const OCTET_STRING_t *val)
|
||||
{
|
||||
return !val || !val->buf || !val->size;
|
||||
}
|
||||
|
||||
static bool is_asn1_bit_string_empty(const BIT_STRING_t *val)
|
||||
{
|
||||
return !val || !val->buf || !val->size;
|
||||
}
|
||||
|
||||
static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
{
|
||||
struct hnb_persistent *hnbp;
|
||||
@@ -480,7 +492,13 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
socklen_t len = sizeof(cur_osa);
|
||||
|
||||
rc = hnbap_decode_hnbregisterrequesties(&ies, in);
|
||||
if (rc < 0) {
|
||||
if (rc < 0
|
||||
/* CID#465551: make sure that actual values ended up in the asn1 octet strings: */
|
||||
|| is_asn1_octet_string_empty(&ies.lac)
|
||||
|| is_asn1_octet_string_empty(&ies.sac)
|
||||
|| is_asn1_octet_string_empty(&ies.rac)
|
||||
|| is_asn1_bit_string_empty(&ies.cellIdentity)
|
||||
|| is_asn1_octet_string_empty(&ies.plmNidentity)) {
|
||||
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Failure to decode HNB-REGISTER-REQ: rc=%d\n", rc);
|
||||
cause.present = HNBAP_Cause_PR_protocol;
|
||||
cause.choice.radioNetwork = HNBAP_CauseProtocol_unspecified;
|
||||
|
@@ -39,6 +39,8 @@
|
||||
#include <osmocom/pfcp/pfcp_cp_peer.h>
|
||||
#endif
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
|
@@ -33,6 +33,8 @@
|
||||
|
||||
#include "asn1helpers.h"
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/hnbgw_ranap.h>
|
||||
#include <osmocom/rua/rua_common.h>
|
||||
|
566
src/osmo-hnbgw/hnbgw_sccp.c
Normal file
566
src/osmo-hnbgw/hnbgw_sccp.c
Normal file
@@ -0,0 +1,566 @@
|
||||
/* hnb-gw specific code for SCCP, ITU Q.711 - Q.714 */
|
||||
|
||||
/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2025 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
#include <osmocom/sigtran/protocol/sua.h>
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/hnbgw_ranap.h>
|
||||
|
||||
/***********************************************************************
|
||||
* Incoming primitives from SCCP User SAP
|
||||
***********************************************************************/
|
||||
|
||||
static bool cnlink_matches(const struct hnbgw_cnlink *cnlink, const struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *remote_addr)
|
||||
{
|
||||
if (cnlink->hnbgw_sccp_user != hsu)
|
||||
return false;
|
||||
if (osmo_sccp_addr_cmp(&cnlink->remote_addr, remote_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct hnbgw_cnlink *hnbgw_cnlink_find_by_addr(const struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_sccp_addr *remote_addr)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iucs->cnlinks, entry) {
|
||||
if (cnlink_matches(cnlink, hsu, remote_addr))
|
||||
return cnlink;
|
||||
}
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iups->cnlinks, entry) {
|
||||
if (cnlink_matches(cnlink, hsu, remote_addr))
|
||||
return cnlink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct hnbgw_cnlink *cnlink_from_addr(struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *calling_addr,
|
||||
const struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = NULL;
|
||||
cnlink = hnbgw_cnlink_find_by_addr(hsu, calling_addr);
|
||||
if (!cnlink) {
|
||||
LOG_HSU(hsu, DRANAP, LOGL_ERROR, "Rx from unknown SCCP peer: %s: %s\n",
|
||||
osmo_sccp_inst_addr_name(osmo_ss7_get_sccp(hsu->ss7), calling_addr),
|
||||
osmo_scu_prim_hdr_name_c(OTC_SELECT, oph));
|
||||
return NULL;
|
||||
}
|
||||
return cnlink;
|
||||
}
|
||||
|
||||
static struct hnbgw_context_map *map_from_conn_id(struct hnbgw_sccp_user *hsu, uint32_t conn_id,
|
||||
const struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
hash_for_each_possible(hsu->hnbgw_context_map_by_conn_id, map, hnbgw_sccp_user_entry, conn_id) {
|
||||
if (map->scu_conn_id == conn_id)
|
||||
return map;
|
||||
}
|
||||
LOGP(DRANAP, LOGL_ERROR, "Rx for unknown SCCP connection ID: %u: %s\n",
|
||||
conn_id, osmo_scu_prim_hdr_name_c(OTC_SELECT, oph));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int handle_cn_unitdata(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_unitdata_param *param,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = cnlink_from_addr(hsu, ¶m->calling_addr, oph);
|
||||
if (!cnlink)
|
||||
return -ENOENT;
|
||||
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_SCCP_N_UNITDATA_IND);
|
||||
|
||||
if (param->called_addr.ssn != OSMO_SCCP_SSN_RANAP) {
|
||||
LOGP(DCN, LOGL_NOTICE, "N-UNITDATA.ind for unknown SSN %u\n",
|
||||
param->called_addr.ssn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return hnbgw_ranap_rx_udt_dl(cnlink, param, msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
}
|
||||
|
||||
static void handle_notice_ind(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_notice_param *param,
|
||||
const struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
|
||||
cnlink = cnlink_from_addr(hsu, ¶m->calling_addr, oph);
|
||||
if (!cnlink) {
|
||||
LOGP(DCN, LOGL_DEBUG, "(calling_addr=%s) N-NOTICE.ind cause=%u='%s' importance=%u didn't match any cnlink, ignoring\n",
|
||||
osmo_sccp_addr_dump(¶m->calling_addr),
|
||||
param->cause, osmo_sccp_return_cause_name(param->cause),
|
||||
param->importance);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "N-NOTICE.ind cause=%u='%s' importance=%u\n",
|
||||
param->cause, osmo_sccp_return_cause_name(param->cause),
|
||||
param->importance);
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_SCCP_N_NOTICE_IND);
|
||||
switch (param->cause) {
|
||||
case SCCP_RETURN_CAUSE_SUBSYSTEM_CONGESTION:
|
||||
case SCCP_RETURN_CAUSE_NETWORK_CONGESTION:
|
||||
/* Transient failures (hopefully), keep going. */
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Messages are not arriving to destination of cnlink. Kick it back to DISC state. */
|
||||
cnlink_set_disconnected(cnlink);
|
||||
}
|
||||
|
||||
static int handle_cn_conn_conf(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_connect_param *param,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
map = map_from_conn_id(hsu, param->conn_id, oph);
|
||||
if (!map || !map->cnlink)
|
||||
return -ENOENT;
|
||||
|
||||
LOGP(DCN, LOGL_DEBUG, "handle_cn_conn_conf() conn_id=%d, addrs: called=%s calling=%s responding=%s\n",
|
||||
param->conn_id,
|
||||
hnbgw_cnlink_sccp_addr_to_str(map->cnlink, ¶m->called_addr),
|
||||
hnbgw_cnlink_sccp_addr_to_str(map->cnlink, ¶m->calling_addr),
|
||||
hnbgw_cnlink_sccp_addr_to_str(map->cnlink, ¶m->responding_addr));
|
||||
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_N_CONNECT_CNF);
|
||||
|
||||
map_sccp_dispatch(map, MAP_SCCP_EV_RX_CONNECTION_CONFIRM, oph->msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_cn_data_ind(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_data_param *param,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
map = map_from_conn_id(hsu, param->conn_id, oph);
|
||||
if (!map || !map->cnlink)
|
||||
return -ENOENT;
|
||||
|
||||
return map_sccp_dispatch(map, MAP_SCCP_EV_RX_DATA_INDICATION, oph->msg);
|
||||
}
|
||||
|
||||
static int handle_cn_disc_ind(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_disconn_param *param,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
char cause_buf[128];
|
||||
|
||||
map = map_from_conn_id(hsu, param->conn_id, oph);
|
||||
if (!map || !map->cnlink)
|
||||
return -ENOENT;
|
||||
|
||||
LOGP(DCN, LOGL_DEBUG, "handle_cn_disc_ind() conn_id=%u responding_addr=%s cause=%s\n",
|
||||
param->conn_id,
|
||||
hnbgw_cnlink_sccp_addr_to_str(map->cnlink, ¶m->responding_addr),
|
||||
osmo_sua_sccp_cause_name(param->cause, cause_buf, sizeof(cause_buf)));
|
||||
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_N_DISCONNECT_IND);
|
||||
|
||||
return map_sccp_dispatch(map, MAP_SCCP_EV_RX_RELEASED, oph->msg);
|
||||
}
|
||||
|
||||
static struct hnbgw_cnlink *_cnlink_find_by_remote_pc(struct hnbgw_cnpool *cnpool, struct osmo_ss7_instance *cs7, uint32_t pc)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) {
|
||||
if (!cnlink->hnbgw_sccp_user)
|
||||
continue;
|
||||
if (cnlink->hnbgw_sccp_user->ss7 != cs7)
|
||||
continue;
|
||||
if ((cnlink->remote_addr.presence & OSMO_SCCP_ADDR_T_PC) == 0)
|
||||
continue;
|
||||
if (cnlink->remote_addr.pc != pc)
|
||||
continue;
|
||||
return cnlink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find a cnlink by its remote sigtran point code on a given cs7 instance. */
|
||||
static struct hnbgw_cnlink *cnlink_find_by_remote_pc(struct osmo_ss7_instance *cs7, uint32_t pc)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
cnlink = _cnlink_find_by_remote_pc(g_hnbgw->sccp.cnpool_iucs, cs7, pc);
|
||||
if (!cnlink)
|
||||
cnlink = _cnlink_find_by_remote_pc(g_hnbgw->sccp.cnpool_iups, cs7, pc);
|
||||
return cnlink;
|
||||
}
|
||||
|
||||
static void handle_pcstate_ind(struct hnbgw_sccp_user *hsu, const struct osmo_scu_pcstate_param *pcst)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
bool connected;
|
||||
bool disconnected;
|
||||
struct osmo_ss7_instance *cs7 = hsu->ss7;
|
||||
|
||||
LOGP(DCN, LOGL_DEBUG, "N-PCSTATE ind: affected_pc=%u=%s sp_status=%s remote_sccp_status=%s\n",
|
||||
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc),
|
||||
osmo_sccp_sp_status_name(pcst->sp_status),
|
||||
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
|
||||
|
||||
/* If we don't care about that point-code, ignore PCSTATE. */
|
||||
cnlink = cnlink_find_by_remote_pc(cs7, pcst->affected_pc);
|
||||
if (!cnlink)
|
||||
return;
|
||||
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_SCCP_N_PCSTATE_IND);
|
||||
|
||||
/* See if this marks the point code to have become available, or to have been lost.
|
||||
*
|
||||
* I want to detect two events:
|
||||
* - connection event (both indicators say PC is reachable).
|
||||
* - disconnection event (at least one indicator says the PC is not reachable).
|
||||
*
|
||||
* There are two separate incoming indicators with various possible values -- the incoming events can be:
|
||||
*
|
||||
* - neither connection nor disconnection indicated -- just indicating congestion
|
||||
* connected == false, disconnected == false --> do nothing.
|
||||
* - both incoming values indicate that we are connected
|
||||
* --> trigger connected
|
||||
* - both indicate we are disconnected
|
||||
* --> trigger disconnected
|
||||
* - one value indicates 'connected', the other indicates 'disconnected'
|
||||
* --> trigger disconnected
|
||||
*
|
||||
* Congestion could imply that we're connected, but it does not indicate that a PC's reachability changed, so no need to
|
||||
* trigger on that.
|
||||
*/
|
||||
connected = false;
|
||||
disconnected = false;
|
||||
|
||||
switch (pcst->sp_status) {
|
||||
case OSMO_SCCP_SP_S_ACCESSIBLE:
|
||||
connected = true;
|
||||
break;
|
||||
case OSMO_SCCP_SP_S_INACCESSIBLE:
|
||||
disconnected = true;
|
||||
break;
|
||||
default:
|
||||
case OSMO_SCCP_SP_S_CONGESTED:
|
||||
/* Neither connecting nor disconnecting */
|
||||
break;
|
||||
}
|
||||
|
||||
switch (pcst->remote_sccp_status) {
|
||||
case OSMO_SCCP_REM_SCCP_S_AVAILABLE:
|
||||
if (!disconnected)
|
||||
connected = true;
|
||||
break;
|
||||
case OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN:
|
||||
case OSMO_SCCP_REM_SCCP_S_UNEQUIPPED:
|
||||
case OSMO_SCCP_REM_SCCP_S_INACCESSIBLE:
|
||||
disconnected = true;
|
||||
connected = false;
|
||||
break;
|
||||
default:
|
||||
case OSMO_SCCP_REM_SCCP_S_CONGESTED:
|
||||
/* Neither connecting nor disconnecting */
|
||||
break;
|
||||
}
|
||||
|
||||
if (disconnected && cnlink_is_conn_ready(cnlink)) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE,
|
||||
"now unreachable: N-PCSTATE ind: pc=%u=%s sp_status=%s remote_sccp_status=%s\n",
|
||||
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc),
|
||||
osmo_sccp_sp_status_name(pcst->sp_status),
|
||||
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
|
||||
/* A previously usable cnlink has disconnected. Kick it back to DISC state. */
|
||||
cnlink_set_disconnected(cnlink);
|
||||
} else if (connected && !cnlink_is_conn_ready(cnlink)) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE,
|
||||
"now available: N-PCSTATE ind: pc=%u=%s sp_status=%s remote_sccp_status=%s\n",
|
||||
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc),
|
||||
osmo_sccp_sp_status_name(pcst->sp_status),
|
||||
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
|
||||
/* A previously unusable cnlink has become reachable. Trigger immediate RANAP RESET -- we would resend a
|
||||
* RESET either way, but we might as well do it now to speed up connecting. */
|
||||
cnlink_resend_reset(cnlink);
|
||||
}
|
||||
}
|
||||
|
||||
/* Entry point for primitives coming up from SCCP User SAP.
|
||||
* Ownership of oph->msg is transferred to us. */
|
||||
static int sccp_sap_up(struct osmo_prim_hdr *oph, void *ctx)
|
||||
{
|
||||
struct osmo_sccp_user *scu = ctx;
|
||||
struct hnbgw_sccp_user *hsu;
|
||||
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DCN, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph));
|
||||
|
||||
if (!scu) {
|
||||
LOGP(DCN, LOGL_ERROR,
|
||||
"sccp_sap_up(): NULL osmo_sccp_user, cannot send prim (sap %u prim %u op %d)\n",
|
||||
oph->sap, oph->primitive, oph->operation);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hsu = osmo_sccp_user_get_priv(scu);
|
||||
if (!hsu) {
|
||||
LOGP(DCN, LOGL_ERROR,
|
||||
"sccp_sap_up(): NULL hnbgw_sccp_user, cannot send prim (sap %u prim %u op %d)\n",
|
||||
oph->sap, oph->primitive, oph->operation);
|
||||
return -1;
|
||||
}
|
||||
|
||||
talloc_steal(OTC_SELECT, oph->msg);
|
||||
|
||||
switch (OSMO_PRIM_HDR(oph)) {
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
|
||||
rc = handle_cn_unitdata(hsu, &prim->u.unitdata, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_NOTICE, PRIM_OP_INDICATION):
|
||||
handle_notice_ind(hsu, &prim->u.notice, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
|
||||
rc = handle_cn_conn_conf(hsu, &prim->u.connect, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
|
||||
rc = handle_cn_data_ind(hsu, &prim->u.data, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
|
||||
rc = handle_cn_disc_ind(hsu, &prim->u.disconnect, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION):
|
||||
handle_pcstate_ind(hsu, &prim->u.pcstate);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGP(DCN, LOGL_ERROR,
|
||||
"Received unknown prim %u from SCCP USER SAP\n",
|
||||
OSMO_PRIM_HDR(oph));
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Submit primitives to SCCP User SAP
|
||||
***********************************************************************/
|
||||
|
||||
int hnbgw_sccp_user_tx_unitdata_req(struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *called_addr, struct msgb *ranap_msg)
|
||||
{
|
||||
if (!hsu) {
|
||||
LOGP(DCN, LOGL_ERROR, "Failed to send SCCP N-UNITDATA.req: no SCCP User\n");
|
||||
return -1;
|
||||
}
|
||||
OSMO_ASSERT(called_addr);
|
||||
return osmo_sccp_tx_unitdata_msg(hsu->sccp_user,
|
||||
&hsu->local_addr,
|
||||
called_addr,
|
||||
ranap_msg);
|
||||
}
|
||||
|
||||
int hnbgw_sccp_user_tx_connect_req(struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *called_addr, uint32_t scu_conn_id, struct msgb *ranap_msg)
|
||||
{
|
||||
struct osmo_scu_prim *prim;
|
||||
int rc;
|
||||
|
||||
if (!hsu) {
|
||||
LOGP(DCN, LOGL_ERROR, "Failed to send SCCP N-CONNECT.req(%u): no SCCP User\n", scu_conn_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(called_addr);
|
||||
|
||||
prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_REQUEST, ranap_msg);
|
||||
prim->u.connect.called_addr = *called_addr;
|
||||
prim->u.connect.calling_addr = hsu->local_addr;
|
||||
prim->u.connect.sccp_class = 2;
|
||||
prim->u.connect.conn_id = scu_conn_id;
|
||||
|
||||
rc = osmo_sccp_user_sap_down_nofree(hsu->sccp_user, &prim->oph);
|
||||
if (rc)
|
||||
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to send SCCP Connection Request to CN\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
int hnbgw_sccp_user_tx_data_req(struct hnbgw_sccp_user *hsu, uint32_t scu_conn_id, struct msgb *ranap_msg)
|
||||
{
|
||||
struct osmo_scu_prim *prim;
|
||||
int rc;
|
||||
|
||||
if (!hsu) {
|
||||
LOGP(DCN, LOGL_ERROR, "Failed to send SCCP N-DATA.req(%u): no SCCP User\n", scu_conn_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, ranap_msg);
|
||||
prim->u.data.conn_id = scu_conn_id;
|
||||
|
||||
rc = osmo_sccp_user_sap_down_nofree(hsu->sccp_user, &prim->oph);
|
||||
if (rc)
|
||||
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to send SCCP N-DATA.req(%u)\n", scu_conn_id);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int hnbgw_sccp_user_tx_disconnect_req(struct hnbgw_sccp_user *hsu, uint32_t scu_conn_id)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!hsu) {
|
||||
LOGP(DCN, LOGL_ERROR, "Failed to send SCCP N-DISCONNECT.req(%u): no SCCP User\n", scu_conn_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = osmo_sccp_tx_disconn(hsu->sccp_user, scu_conn_id, NULL,
|
||||
SCCP_RELEASE_CAUSE_END_USER_ORIGINATED);
|
||||
if (rc)
|
||||
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to send SCCP N-DISCONNECT.req(%u)\n", scu_conn_id);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* struct hnbgw_sccp_user lifecycle:
|
||||
***********************************************************************/
|
||||
|
||||
static int hnbgw_sccp_user_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
|
||||
{
|
||||
struct hnbgw_sccp_user *hsu = e->use_count->talloc_object;
|
||||
int32_t total;
|
||||
int level;
|
||||
|
||||
if (!e->use)
|
||||
return -EINVAL;
|
||||
|
||||
total = osmo_use_count_total(&hsu->use_count);
|
||||
|
||||
if (total == 0
|
||||
|| (total == 1 && old_use_count == 0 && e->count == 1))
|
||||
level = LOGL_INFO;
|
||||
else
|
||||
level = LOGL_DEBUG;
|
||||
|
||||
LOGPSRC(DCN, level, file, line,
|
||||
"%s: %s %s: now used by %s\n",
|
||||
hsu->name,
|
||||
(e->count - old_use_count) > 0 ? "+" : "-",
|
||||
e->use,
|
||||
osmo_use_count_to_str_c(OTC_SELECT, &hsu->use_count));
|
||||
|
||||
if (e->count < 0)
|
||||
return -ERANGE;
|
||||
|
||||
if (total == 0)
|
||||
talloc_free(hsu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hnbgw_sccp_user_talloc_destructor(struct hnbgw_sccp_user *hsu)
|
||||
{
|
||||
if (hsu->sccp_user) {
|
||||
osmo_sccp_user_unbind(hsu->sccp_user);
|
||||
hsu->sccp_user = NULL;
|
||||
}
|
||||
llist_del(&hsu->entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct hnbgw_sccp_user *hnbgw_sccp_user_alloc(int ss7_id)
|
||||
{
|
||||
struct osmo_sccp_instance *sccp;
|
||||
uint32_t local_pc;
|
||||
struct hnbgw_sccp_user *hsu;
|
||||
|
||||
hsu = talloc_zero(g_hnbgw, struct hnbgw_sccp_user);
|
||||
OSMO_ASSERT(hsu);
|
||||
*hsu = (struct hnbgw_sccp_user){
|
||||
.name = talloc_asprintf(hsu, "cs7-%u-sccp-OsmoHNBGW", ss7_id),
|
||||
.use_count = {
|
||||
.talloc_object = hsu,
|
||||
.use_cb = hnbgw_sccp_user_use_cb,
|
||||
},
|
||||
};
|
||||
hash_init(hsu->hnbgw_context_map_by_conn_id);
|
||||
llist_add_tail(&hsu->entry, &g_hnbgw->sccp.users);
|
||||
talloc_set_destructor(hsu, hnbgw_sccp_user_talloc_destructor);
|
||||
|
||||
sccp = osmo_sccp_simple_client_on_ss7_id(g_hnbgw,
|
||||
ss7_id,
|
||||
hsu->name,
|
||||
DEFAULT_PC_HNBGW,
|
||||
OSMO_SS7_ASP_PROT_M3UA,
|
||||
0,
|
||||
"localhost",
|
||||
-1,
|
||||
"localhost");
|
||||
if (!sccp) {
|
||||
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to configure SCCP on 'cs7 instance %u'\n",
|
||||
ss7_id);
|
||||
goto free_hsu_ret;
|
||||
}
|
||||
hsu->ss7 = osmo_sccp_get_ss7(sccp);
|
||||
LOG_HSU(hsu, DCN, LOGL_NOTICE, "created SCCP instance on cs7 instance %u\n", osmo_ss7_instance_get_id(hsu->ss7));
|
||||
|
||||
/* Bind the SCCP user, using the cs7 instance's default point-code if one is configured, or osmo-hnbgw's default
|
||||
* local PC. */
|
||||
local_pc = osmo_ss7_instance_get_primary_pc(hsu->ss7);
|
||||
if (!osmo_ss7_pc_is_valid(local_pc))
|
||||
local_pc = DEFAULT_PC_HNBGW;
|
||||
|
||||
LOG_HSU(hsu, DCN, LOGL_DEBUG, "binding OsmoHNBGW user to cs7 instance %u, local PC %u = %s\n",
|
||||
osmo_ss7_instance_get_id(hsu->ss7), local_pc, osmo_ss7_pointcode_print(hsu->ss7, local_pc));
|
||||
|
||||
char *sccp_user_name = talloc_asprintf(hsu, "%s-RANAP", hsu->name);
|
||||
hsu->sccp_user = osmo_sccp_user_bind_pc(sccp, sccp_user_name, sccp_sap_up, OSMO_SCCP_SSN_RANAP, local_pc);
|
||||
talloc_free(sccp_user_name);
|
||||
if (!hsu->sccp_user) {
|
||||
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to init SCCP User\n");
|
||||
goto free_hsu_ret;
|
||||
}
|
||||
|
||||
osmo_sccp_make_addr_pc_ssn(&hsu->local_addr, local_pc, OSMO_SCCP_SSN_RANAP);
|
||||
osmo_sccp_user_set_priv(hsu->sccp_user, hsu);
|
||||
|
||||
return hsu;
|
||||
|
||||
free_hsu_ret:
|
||||
talloc_free(hsu);
|
||||
return NULL;
|
||||
}
|
||||
|
@@ -31,6 +31,8 @@
|
||||
|
||||
#include <osmocom/hnbgw/vty.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
@@ -84,7 +86,7 @@ DEFUN(cfg_hnbgw_iucs, cfg_hnbgw_iucs_cmd,
|
||||
"iucs", "Configure IuCS options")
|
||||
{
|
||||
vty->node = IUCS_NODE;
|
||||
vty->index = &g_hnbgw->sccp.cnpool_iucs;
|
||||
vty->index = g_hnbgw->sccp.cnpool_iucs;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -98,7 +100,7 @@ DEFUN(cfg_hnbgw_iups, cfg_hnbgw_iups_cmd,
|
||||
"iups", "Configure IuPS options")
|
||||
{
|
||||
vty->node = IUPS_NODE;
|
||||
vty->index = &g_hnbgw->sccp.cnpool_iups;
|
||||
vty->index = g_hnbgw->sccp.cnpool_iups;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -147,12 +149,13 @@ static void _show_cnlink(struct vty *vty, struct hnbgw_cnlink *cnlink)
|
||||
return;
|
||||
}
|
||||
|
||||
vty_out(vty, "%s <->",
|
||||
vty_out(vty, "%s: %s <->",
|
||||
cnlink->name,
|
||||
osmo_sccp_user_name(cnlink->hnbgw_sccp_user->sccp_user));
|
||||
vty_out(vty, " %s%s%s%s",
|
||||
cnlink->use.remote_addr_name ? : "",
|
||||
cnlink->use.remote_addr_name ? "=" : "",
|
||||
cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr),
|
||||
hnbgw_cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr),
|
||||
VTY_NEWLINE);
|
||||
|
||||
rt = osmo_ss7_route_lookup(ss7, cnlink->remote_addr.pc);
|
||||
@@ -165,10 +168,10 @@ DEFUN(show_cnlink, show_cnlink_cmd, "show cnlink",
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
vty_out(vty, "IuCS: ");
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iucs.cnlinks, entry)
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iucs->cnlinks, entry)
|
||||
_show_cnlink(vty, cnlink);
|
||||
vty_out(vty, "IuPS: ");
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iups.cnlinks, entry)
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iups->cnlinks, entry)
|
||||
_show_cnlink(vty, cnlink);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -509,7 +512,7 @@ DEFUN(cfg_msc_nr, cfg_msc_nr_cmd,
|
||||
"Configure an IuCS link to an MSC\n"
|
||||
"MSC nr\n")
|
||||
{
|
||||
return cnlink_nr(vty, &g_hnbgw->sccp.cnpool_iucs, argc, argv);
|
||||
return cnlink_nr(vty, g_hnbgw->sccp.cnpool_iucs, argc, argv);
|
||||
}
|
||||
|
||||
/* 'sgsn 0' */
|
||||
@@ -518,12 +521,24 @@ DEFUN(cfg_sgsn_nr, cfg_sgsn_nr_cmd,
|
||||
"Configure an IuPS link to an SGSN\n"
|
||||
"SGSN nr\n")
|
||||
{
|
||||
return cnlink_nr(vty, &g_hnbgw->sccp.cnpool_iups, argc, argv);
|
||||
return cnlink_nr(vty, g_hnbgw->sccp.cnpool_iups, argc, argv);
|
||||
}
|
||||
|
||||
/* 'msc 0' / 'remote-addr my-msc' and
|
||||
* 'sgsn 0' / 'remote-addr my-sgsn'
|
||||
*/
|
||||
DEFUN(cfg_cnlink_name,
|
||||
cfg_cnlink_name_cmd,
|
||||
"name NAME",
|
||||
"Set user defined name for this msc/sgsn\n"
|
||||
"The user defined name to be set for this msc/sgsn\n")
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = vty->index;
|
||||
if (hnbgw_cnlink_set_name(cnlink, argv[0]) < 0)
|
||||
return CMD_WARNING;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_cnlink_remote_addr,
|
||||
cfg_cnlink_remote_addr_cmd,
|
||||
"remote-addr NAME",
|
||||
@@ -714,14 +729,14 @@ DEFUN(show_nri, show_nri_cmd,
|
||||
* nri null add ...
|
||||
*/
|
||||
vty_out(vty, "hnbgw%s", VTY_NEWLINE);
|
||||
cnpool_write_nri(vty, &g_hnbgw->sccp.cnpool_iucs, true);
|
||||
cnpool_write_nri(vty, &g_hnbgw->sccp.cnpool_iups, true);
|
||||
cnpool_write_nri(vty, g_hnbgw->sccp.cnpool_iucs, true);
|
||||
cnpool_write_nri(vty, g_hnbgw->sccp.cnpool_iups, true);
|
||||
|
||||
/* msc 0
|
||||
* nri add ...
|
||||
*/
|
||||
cnlinks_write_nri(vty, &g_hnbgw->sccp.cnpool_iucs, true);
|
||||
cnlinks_write_nri(vty, &g_hnbgw->sccp.cnpool_iups, true);
|
||||
cnlinks_write_nri(vty, g_hnbgw->sccp.cnpool_iucs, true);
|
||||
cnlinks_write_nri(vty, g_hnbgw->sccp.cnpool_iups, true);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -737,9 +752,9 @@ DEFUN_HIDDEN(cnpool_roundrobin_next, cnpool_roundrobin_next_cmd,
|
||||
{
|
||||
struct hnbgw_cnpool *cnpool;
|
||||
if (!strcmp("msc", argv[0]))
|
||||
cnpool = &g_hnbgw->sccp.cnpool_iucs;
|
||||
cnpool = g_hnbgw->sccp.cnpool_iucs;
|
||||
else
|
||||
cnpool = &g_hnbgw->sccp.cnpool_iups;
|
||||
cnpool = g_hnbgw->sccp.cnpool_iups;
|
||||
cnpool->round_robin_next_nr = atoi(argv[1]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -758,9 +773,9 @@ DEFUN(cnlink_ranap_reset, cnlink_ranap_reset_cmd,
|
||||
int nr = atoi(argv[1]);
|
||||
|
||||
if (!strcmp("msc", msc_sgsn))
|
||||
cnpool = &g_hnbgw->sccp.cnpool_iucs;
|
||||
cnpool = g_hnbgw->sccp.cnpool_iucs;
|
||||
else
|
||||
cnpool = &g_hnbgw->sccp.cnpool_iups;
|
||||
cnpool = g_hnbgw->sccp.cnpool_iups;
|
||||
|
||||
cnlink = cnlink_get_nr(cnpool, nr, false);
|
||||
if (!cnlink) {
|
||||
@@ -799,11 +814,11 @@ DEFUN(cfg_config_apply_sccp, cfg_config_apply_sccp_cmd,
|
||||
{
|
||||
struct hnbgw_cnpool *cnpool;
|
||||
|
||||
cnpool = &g_hnbgw->sccp.cnpool_iucs;
|
||||
cnpool = g_hnbgw->sccp.cnpool_iucs;
|
||||
hnbgw_cnpool_apply_cfg(cnpool);
|
||||
hnbgw_cnpool_cnlinks_start_or_restart(cnpool);
|
||||
|
||||
cnpool = &g_hnbgw->sccp.cnpool_iups;
|
||||
cnpool = g_hnbgw->sccp.cnpool_iups;
|
||||
hnbgw_cnpool_apply_cfg(cnpool);
|
||||
hnbgw_cnpool_cnlinks_start_or_restart(cnpool);
|
||||
|
||||
@@ -1012,8 +1027,8 @@ static int config_write_hnbgw(struct vty *vty)
|
||||
llist_for_each_entry(hnbp, &g_hnbgw->hnb_persistent_list, list)
|
||||
write_one_hnbp(vty, hnbp);
|
||||
|
||||
_config_write_cnpool(vty, &g_hnbgw->sccp.cnpool_iucs);
|
||||
_config_write_cnpool(vty, &g_hnbgw->sccp.cnpool_iups);
|
||||
_config_write_cnpool(vty, g_hnbgw->sccp.cnpool_iucs);
|
||||
_config_write_cnpool(vty, g_hnbgw->sccp.cnpool_iups);
|
||||
|
||||
if (g_hnbgw->config.nft_kpi.enable)
|
||||
vty_out(vty, " nft-kpi%s%s%s",
|
||||
@@ -1055,6 +1070,7 @@ static void _config_write_cnlink(struct vty *vty, struct hnbgw_cnpool *cnpool)
|
||||
|
||||
llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) {
|
||||
vty_out(vty, "%s %d%s", cnpool->peer_name, cnlink->nr, VTY_NEWLINE);
|
||||
vty_out(vty, " name %s%s", cnlink->name, VTY_NEWLINE);
|
||||
if (cnlink->vty.remote_addr_name)
|
||||
vty_out(vty, " remote-addr %s%s", cnlink->vty.remote_addr_name, VTY_NEWLINE);
|
||||
cnlink_write_nri(vty, cnlink, false);
|
||||
@@ -1063,13 +1079,13 @@ static void _config_write_cnlink(struct vty *vty, struct hnbgw_cnpool *cnpool)
|
||||
|
||||
static int config_write_msc(struct vty *vty)
|
||||
{
|
||||
_config_write_cnlink(vty, &g_hnbgw->sccp.cnpool_iucs);
|
||||
_config_write_cnlink(vty, g_hnbgw->sccp.cnpool_iucs);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int config_write_sgsn(struct vty *vty)
|
||||
{
|
||||
_config_write_cnlink(vty, &g_hnbgw->sccp.cnpool_iups);
|
||||
_config_write_cnlink(vty, g_hnbgw->sccp.cnpool_iups);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1096,6 +1112,7 @@ static int config_write_hnbgw_pfcp(struct vty *vty)
|
||||
|
||||
static void install_cnlink_elements(int node)
|
||||
{
|
||||
install_element(node, &cfg_cnlink_name_cmd);
|
||||
install_element(node, &cfg_cnlink_remote_addr_cmd);
|
||||
install_element(node, &cfg_cnlink_nri_add_cmd);
|
||||
install_element(node, &cfg_cnlink_nri_del_cmd);
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/kpi.h>
|
||||
|
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/kpi.h>
|
||||
@@ -51,18 +52,16 @@ static void kpi_ranap_process_dl_iu_rel_cmd(struct hnbgw_context_map *map, const
|
||||
|
||||
/* When Iu is released, all RABs are released implicitly */
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE(map->rab_state); i++) {
|
||||
unsigned int ctr_num;
|
||||
switch (map->rab_state[i]) {
|
||||
case RAB_STATE_ACTIVE:
|
||||
if (cause->present == RANAP_Cause_PR_nAS ||
|
||||
cause->choice.nAS == RANAP_CauseNAS_normal_release) {
|
||||
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT;
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT);
|
||||
} else {
|
||||
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL;
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT_ABNORMAL);
|
||||
}
|
||||
if (!map->is_ps)
|
||||
ctr_num++;
|
||||
HNBP_CTR_INC(hnbp, ctr_num);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -107,13 +106,17 @@ static void kpi_ranap_process_dl_rab_ass_req(struct hnbgw_context_map *map, rana
|
||||
* new, it is a setup */
|
||||
switch (map->rab_state[rab_id]) {
|
||||
case RAB_STATE_ACTIVE:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_REQ : HNB_CTR_RANAP_CS_RAB_MOD_REQ);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_REQ :
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_REQ);
|
||||
break;
|
||||
case RAB_STATE_INACTIVE:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_REQ : HNB_CTR_RANAP_CS_RAB_ACT_REQ);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_REQ :
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_REQ);
|
||||
map->rab_state[rab_id] = RAB_STATE_ACT_REQ;
|
||||
break;
|
||||
default:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_REQ_UNEXP :
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_REQ_UNEXP);
|
||||
LOG_MAP(map, DRANAP, LOGL_NOTICE,
|
||||
"Unexpected RAB Activation/Modification Req for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
@@ -131,7 +134,6 @@ static void kpi_ranap_process_dl_rab_ass_req(struct hnbgw_context_map *map, rana
|
||||
RANAP_RAB_ReleaseItemIEs_t _rab_rel_item_ies = {};
|
||||
RANAP_RAB_ReleaseItemIEs_t *rab_rel_item_ies = &_rab_rel_item_ies;
|
||||
RANAP_RAB_ReleaseItem_t *rab_rel_item;
|
||||
unsigned int ctr_num;
|
||||
uint8_t rab_id;
|
||||
|
||||
if (!release_list_ie)
|
||||
@@ -152,15 +154,16 @@ static void kpi_ranap_process_dl_rab_ass_req(struct hnbgw_context_map *map, rana
|
||||
case RAB_STATE_ACTIVE:
|
||||
if (rab_rel_item->cause.present == RANAP_Cause_PR_nAS &&
|
||||
rab_rel_item->cause.choice.nAS == RANAP_CauseNAS_normal_release) {
|
||||
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_REQ;
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_REQ :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ);
|
||||
} else {
|
||||
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL;
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ_ABNORMAL);
|
||||
}
|
||||
if (!map->is_ps)
|
||||
ctr_num++;
|
||||
HNBP_CTR_INC(hnbp, ctr_num);
|
||||
break;
|
||||
default:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_REQ_UNEXP :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ_UNEXP);
|
||||
LOG_MAP(map, DRANAP, LOGL_NOTICE,
|
||||
"Unexpected RAB Release Req in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
@@ -250,13 +253,17 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
|
||||
/* differentiate modify / activate */
|
||||
switch (map->rab_state[rab_id]) {
|
||||
case RAB_STATE_ACT_REQ:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_CNF : HNB_CTR_RANAP_CS_RAB_ACT_CNF);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_CNF :
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_CNF);
|
||||
map->rab_state[rab_id] = RAB_STATE_ACTIVE;
|
||||
break;
|
||||
case RAB_STATE_ACTIVE:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_CNF : HNB_CTR_RANAP_CS_RAB_MOD_CNF);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_CNF :
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_CNF);
|
||||
break;
|
||||
default:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_CNF_UNEXP :
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_CNF_UNEXP);
|
||||
LOG_MAP(map, DRANAP, LOGL_NOTICE,
|
||||
"Unexpected RAB Activation/Modification Conf for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
@@ -270,8 +277,6 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
|
||||
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_RELEASEDLIST_PRESENT) {
|
||||
RANAP_RAB_ReleasedList_t *r_list = &ies->raB_ReleasedList;
|
||||
/* increment number of released RABs, we don't need to do that individually during iteration */
|
||||
HNBP_CTR_ADD(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_CNF : HNB_CTR_RANAP_CS_RAB_REL_CNF,
|
||||
r_list->raB_ReleasedList_ies.list.count);
|
||||
for (unsigned int i = 0; i < r_list->raB_ReleasedList_ies.list.count; i++) {
|
||||
RANAP_IE_t *released_list_ie = r_list->raB_ReleasedList_ies.list.array[i];
|
||||
RANAP_RAB_ReleasedItemIEs_t _rab_rel_item_ies = {};
|
||||
@@ -295,8 +300,12 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
|
||||
|
||||
switch (map->rab_state[rab_id]) {
|
||||
case RAB_STATE_REL_REQ:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_CNF :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_CNF);
|
||||
break;
|
||||
default:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_CNF_UNEXP :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_CNF_UNEXP);
|
||||
LOG_MAP(map, DRANAP, LOGL_NOTICE,
|
||||
"Unexpected RAB Release Conf for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
@@ -338,14 +347,18 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
|
||||
/* differentiate modify / activate */
|
||||
switch (map->rab_state[rab_id]) {
|
||||
case RAB_STATE_ACT_REQ:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_FAIL : HNB_CTR_RANAP_CS_RAB_ACT_FAIL);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_FAIL :
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_FAIL);
|
||||
map->rab_state[rab_id] = RAB_STATE_INACTIVE;
|
||||
break;
|
||||
case RAB_STATE_ACTIVE:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_FAIL : HNB_CTR_RANAP_CS_RAB_MOD_FAIL);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_FAIL :
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_FAIL);
|
||||
// FIXME: does it remain active after modification failure?
|
||||
break;
|
||||
default:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_FAIL_UNEXP :
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_FAIL_UNEXP);
|
||||
LOG_MAP(map, DRANAP, LOGL_NOTICE,
|
||||
"Unexpected RAB Activation/Modification Failed for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
@@ -359,8 +372,6 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
|
||||
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_RELEASEFAILEDLIST_PRESENT) {
|
||||
RANAP_RAB_ReleaseFailedList_t *rf_list = &ies->raB_ReleaseFailedList;
|
||||
/* increment number of released RABs, we don't need to do that individually during iteration */
|
||||
HNBP_CTR_ADD(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_FAIL : HNB_CTR_RANAP_CS_RAB_REL_FAIL,
|
||||
rf_list->raB_FailedList_ies.list.count);
|
||||
for (unsigned int i = 0; i < rf_list->raB_FailedList_ies.list.count; i++) {
|
||||
RANAP_IE_t *failed_list_ie = rf_list->raB_FailedList_ies.list.array[i];
|
||||
RANAP_RAB_FailedItemIEs_t _rab_failed_item_ies = {};
|
||||
@@ -385,14 +396,18 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
|
||||
/* differentiate modify / activate */
|
||||
switch (map->rab_state[rab_id]) {
|
||||
case RAB_STATE_ACT_REQ:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_FAIL : HNB_CTR_RANAP_CS_RAB_ACT_FAIL);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_FAIL :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_FAIL);
|
||||
map->rab_state[rab_id] = RAB_STATE_INACTIVE;
|
||||
break;
|
||||
case RAB_STATE_ACTIVE:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_FAIL : HNB_CTR_RANAP_CS_RAB_MOD_FAIL);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_FAIL :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_FAIL);
|
||||
// FIXME: does it remain active after modification failure?
|
||||
break;
|
||||
default:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_FAIL_UNEXP :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_FAIL_UNEXP);
|
||||
LOG_MAP(map, DRANAP, LOGL_NOTICE,
|
||||
"Unexpected RAB Release Failed for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
|
@@ -19,8 +19,10 @@
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/it_q.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/nft_kpi.h>
|
||||
#include <osmocom/hnbgw/umts_cell_id.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
@@ -45,6 +45,7 @@
|
||||
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/hnbgw_pfcp.h>
|
||||
@@ -344,8 +345,8 @@ int main(int argc, char **argv)
|
||||
#endif
|
||||
}
|
||||
|
||||
hnbgw_cnpool_start(&g_hnbgw->sccp.cnpool_iucs);
|
||||
hnbgw_cnpool_start(&g_hnbgw->sccp.cnpool_iups);
|
||||
hnbgw_cnpool_start(g_hnbgw->sccp.cnpool_iucs);
|
||||
hnbgw_cnpool_start(g_hnbgw->sccp.cnpool_iups);
|
||||
|
||||
if (hnbgw_cmdline_config.daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
|
@@ -61,6 +61,7 @@ static const struct value_string ps_rab_ass_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(PS_RAB_ASS_EV_LOCAL_F_TEIDS_RX),
|
||||
OSMO_VALUE_STRING(PS_RAB_ASS_EV_RAB_ASS_RESP),
|
||||
OSMO_VALUE_STRING(PS_RAB_ASS_EV_RAB_ESTABLISHED),
|
||||
OSMO_VALUE_STRING(PS_RAB_ASS_EV_RAB_RELEASED),
|
||||
OSMO_VALUE_STRING(PS_RAB_ASS_EV_RAB_FAIL),
|
||||
{}
|
||||
};
|
||||
@@ -81,10 +82,10 @@ enum ps_rab_ass_state {
|
||||
* This structure manages the RAB Assignment procedures, and the currently set up RABs:
|
||||
*
|
||||
* - hnbgw_context_map
|
||||
* - .ps_rab_ass: list of PS RAB Assignment procedures
|
||||
* - .ps_rab_ass_list: list of PS RAB Assignment procedures
|
||||
* - ps_rab_ass_fsm: one RANAP PS RAB Assignment procedure
|
||||
* - ...
|
||||
* - .ps_rabs: list of individual PS RABs
|
||||
* - .ps_rab_list: list of individual PS RABs
|
||||
* - ps_rab_fsm: one GTP mapping with PFCP session to the UPF, for a single RAB
|
||||
* - ...
|
||||
*
|
||||
@@ -111,6 +112,9 @@ struct ps_rab_ass {
|
||||
* ps_rab_fsms, without iterating the RAB Assignment message every time (minor optimisation). */
|
||||
int rabs_count;
|
||||
int rabs_done_count;
|
||||
|
||||
unsigned int rabs_rel_count;
|
||||
unsigned int rabs_rel_done_count;
|
||||
};
|
||||
|
||||
struct osmo_tdef_state_timeout ps_rab_ass_fsm_timeouts[32] = {
|
||||
@@ -118,7 +122,7 @@ struct osmo_tdef_state_timeout ps_rab_ass_fsm_timeouts[32] = {
|
||||
/* PS_RAB_ASS_ST_WAIT_RABS_ESTABLISHED is terminated by PFCP timeouts via ps_rab_fsm */
|
||||
};
|
||||
|
||||
#define ps_rab_ass_fsm_state_chg(state) \
|
||||
#define ps_rab_ass_fsm_state_chg(fi, state) \
|
||||
osmo_tdef_fsm_inst_state_chg(fi, state, ps_rab_ass_fsm_timeouts, hnbgw_T_defs, -1)
|
||||
|
||||
static struct osmo_fsm ps_rab_ass_fsm;
|
||||
@@ -140,7 +144,7 @@ static struct ps_rab_ass *ps_rab_ass_alloc(struct hnbgw_context_map *map)
|
||||
};
|
||||
fi->priv = rab_ass;
|
||||
|
||||
llist_add_tail(&rab_ass->entry, &map->ps_rab_ass);
|
||||
llist_add_tail(&rab_ass->entry, &map->ps_rab_ass_list);
|
||||
return rab_ass;
|
||||
}
|
||||
|
||||
@@ -219,12 +223,9 @@ int hnbgw_gtpmap_rx_rab_ass_req(struct hnbgw_context_map *map, struct msgb *rana
|
||||
{
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies = &message->msg.raB_AssignmentRequestIEs;
|
||||
int i;
|
||||
|
||||
struct ps_rab_ass *rab_ass;
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
rab_ass = ps_rab_ass_alloc(map);
|
||||
|
||||
talloc_steal(rab_ass, message);
|
||||
rab_ass->ranap_rab_ass_req_message = message;
|
||||
/* Now rab_ass owns message and will clean it up */
|
||||
@@ -234,33 +235,39 @@ int hnbgw_gtpmap_rx_rab_ass_req(struct hnbgw_context_map *map, struct msgb *rana
|
||||
goto no_rab;
|
||||
}
|
||||
|
||||
/* Make sure we indeed deal with a setup-or-modify list */
|
||||
if (!(ies->presenceMask & RAB_ASSIGNMENTREQUESTIES_RANAP_RAB_SETUPORMODIFYLIST_PRESENT)) {
|
||||
LOG_MAP(map, DLPFCP, LOGL_ERROR, "RANAP PS RAB AssignmentRequest lacks setup-or-modify list\n");
|
||||
goto no_rab;
|
||||
/* setup-or-modify list */
|
||||
if (ies->presenceMask & RAB_ASSIGNMENTREQUESTIES_RANAP_RAB_SETUPORMODIFYLIST_PRESENT) {
|
||||
/* Multiple RABs may be set up, assemble in list map->ps_rab_list. */
|
||||
for (i = 0; i < ies->raB_SetupOrModifyList.list.count; i++) {
|
||||
RANAP_ProtocolIE_ContainerPair_t *protocol_ie_container_pair;
|
||||
RANAP_ProtocolIE_FieldPair_t *protocol_ie_field_pair;
|
||||
|
||||
protocol_ie_container_pair = ies->raB_SetupOrModifyList.list.array[i];
|
||||
protocol_ie_field_pair = protocol_ie_container_pair->list.array[0];
|
||||
if (!protocol_ie_field_pair)
|
||||
goto no_rab;
|
||||
if (protocol_ie_field_pair->id != RANAP_ProtocolIE_ID_id_RAB_SetupOrModifyItem)
|
||||
goto no_rab;
|
||||
|
||||
if (ps_rab_setup_core_remote(rab_ass, protocol_ie_field_pair))
|
||||
goto no_rab;
|
||||
}
|
||||
}
|
||||
|
||||
/* Multiple RABs may be set up, assemble in list rab_ass->ps_rabs. */
|
||||
for (i = 0; i < ies->raB_SetupOrModifyList.list.count; i++) {
|
||||
RANAP_ProtocolIE_ContainerPair_t *protocol_ie_container_pair;
|
||||
RANAP_ProtocolIE_FieldPair_t *protocol_ie_field_pair;
|
||||
/* Note: ReleaseList is handled at RAB Assign Response time. */
|
||||
|
||||
protocol_ie_container_pair = ies->raB_SetupOrModifyList.list.array[i];
|
||||
protocol_ie_field_pair = protocol_ie_container_pair->list.array[0];
|
||||
if (!protocol_ie_field_pair)
|
||||
goto no_rab;
|
||||
if (protocol_ie_field_pair->id != RANAP_ProtocolIE_ID_id_RAB_SetupOrModifyItem)
|
||||
goto no_rab;
|
||||
|
||||
if (ps_rab_setup_core_remote(rab_ass, protocol_ie_field_pair))
|
||||
goto no_rab;
|
||||
if (rab_ass->rabs_count > 0) {
|
||||
/* Got all RABs' state and their Core side GTP info in map->ps_rab_list.
|
||||
* For each, a ps_rab_fsm has been started and each will call back with
|
||||
* PS_RAB_ASS_EV_LOCAL_F_TEIDS_RX or PS_RAB_ASS_EV_RAB_FAIL. */
|
||||
return ps_rab_ass_fsm_state_chg(rab_ass->fi, PS_RAB_ASS_ST_WAIT_LOCAL_F_TEIDS);
|
||||
}
|
||||
|
||||
/* Got all RABs' state and their Core side GTP info in map->ps_rabs. For each, a ps_rab_fsm has been started and
|
||||
* each will call back with PS_RAB_ASS_EV_LOCAL_F_TEIDS_RX or PS_RAB_ASS_EV_RAB_FAIL. */
|
||||
fi = rab_ass->fi;
|
||||
return ps_rab_ass_fsm_state_chg(PS_RAB_ASS_ST_WAIT_LOCAL_F_TEIDS);
|
||||
|
||||
/* No Setup-or-modify, only Release RABs. Forward the message as is,
|
||||
* RABs will be released at RAB ASS Response time. */
|
||||
map_rua_dispatch(map, MAP_RUA_EV_TX_DIRECT_TRANSFER, ranap_msg);
|
||||
osmo_fsm_inst_term(rab_ass->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
return 0;
|
||||
no_rab:
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return -1;
|
||||
@@ -413,70 +420,127 @@ int hnbgw_gtpmap_rx_rab_ass_resp(struct hnbgw_context_map *map, struct msgb *ran
|
||||
* each other, we are able to handle mismatching RAB IDs in request and response messages.
|
||||
*/
|
||||
|
||||
RANAP_RAB_AssignmentResponseIEs_t *ies = &message->msg.raB_AssignmentResponseIEs;
|
||||
int rc;
|
||||
int i;
|
||||
struct ps_rab_ass *rab_ass;
|
||||
struct osmo_fsm_inst *fi;
|
||||
RANAP_RAB_AssignmentResponseIEs_t *ies;
|
||||
|
||||
/* Make sure we indeed deal with a setup-or-modify list */
|
||||
ies = &message->msg.raB_AssignmentResponseIEs;
|
||||
if (!(ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_SETUPORMODIFIEDLIST_PRESENT)) {
|
||||
LOG_MAP(map, DRUA, LOGL_ERROR, "RANAP PS RAB AssignmentResponse lacks setup-or-modify list\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rab_ass = ps_rab_ass_alloc(map);
|
||||
|
||||
talloc_steal(rab_ass, message);
|
||||
rab_ass->ranap_rab_ass_resp_message = message;
|
||||
|
||||
talloc_steal(rab_ass, ranap_msg);
|
||||
rab_ass->ranap_rab_ass_resp_msgb = ranap_msg;
|
||||
/* Now rab_ass owns message and will clean it up */
|
||||
|
||||
if (!osmo_pfcp_cp_peer_is_associated(g_hnbgw->pfcp.cp_peer)) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "PFCP is not associated, cannot set up GTP mapping\n");
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return -1;
|
||||
goto no_rab;
|
||||
}
|
||||
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_INFO, "PS RAB-AssignmentResponse received, updating RABs\n");
|
||||
/* setup-or-modify list */
|
||||
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_SETUPORMODIFIEDLIST_PRESENT) {
|
||||
|
||||
/* Multiple RABs may be set up, bump matching FSMs in list rab_ass->ps_rabs. */
|
||||
for (i = 0; i < ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.count; i++) {
|
||||
RANAP_IE_t *list_ie;
|
||||
RANAP_RAB_SetupOrModifiedItemIEs_t item_ies;
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_INFO, "PS RAB-AssignmentResponse received, updating RABs\n");
|
||||
|
||||
list_ie = ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[i];
|
||||
if (!list_ie)
|
||||
continue;
|
||||
/* Multiple RABs may be set up, bump matching FSMs in list map->ps_rab_list. */
|
||||
for (i = 0; i < ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.count; i++) {
|
||||
RANAP_IE_t *list_ie;
|
||||
RANAP_RAB_SetupOrModifiedItemIEs_t item_ies;
|
||||
|
||||
rc = ranap_decode_rab_setupormodifieditemies_fromlist(&item_ies,
|
||||
&list_ie->value);
|
||||
if (rc < 0) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Failed to decode PS RAB-AssignmentResponse"
|
||||
" SetupOrModifiedItemIEs with list index %d\n", i);
|
||||
goto continue_cleanloop;
|
||||
}
|
||||
list_ie = ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[i];
|
||||
if (!list_ie)
|
||||
continue;
|
||||
|
||||
if (ps_rab_setup_access_remote(rab_ass, &item_ies.raB_SetupOrModifiedItem))
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Failed to apply PS RAB-AssignmentResponse"
|
||||
" SetupOrModifiedItemIEs with list index %d\n", i);
|
||||
rab_ass->rabs_count++;
|
||||
rc = ranap_decode_rab_setupormodifieditemies_fromlist(&item_ies,
|
||||
&list_ie->value);
|
||||
if (rc < 0) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Failed to decode PS RAB-AssignmentResponse"
|
||||
" SetupOrModifiedItemIEs with list index %d\n", i);
|
||||
goto continue_cleanloop;
|
||||
}
|
||||
|
||||
if (ps_rab_setup_access_remote(rab_ass, &item_ies.raB_SetupOrModifiedItem))
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Failed to apply PS RAB-AssignmentResponse"
|
||||
" SetupOrModifiedItemIEs with list index %d\n", i);
|
||||
rab_ass->rabs_count++;
|
||||
|
||||
continue_cleanloop:
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, &item_ies);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, &item_ies);
|
||||
}
|
||||
}
|
||||
|
||||
/* Got all RABs' state and updated their Access side GTP info in map->ps_rabs. For each RAB ID, the matching
|
||||
* ps_rab_fsm has been instructed to tell the UPF about the Access Remote GTP F-TEID. Each will call back with
|
||||
* PS_RAB_ASS_EV_RAB_ESTABLISHED or PS_RAB_ASS_EV_RAB_FAIL. */
|
||||
fi = rab_ass->fi;
|
||||
return ps_rab_ass_fsm_state_chg(PS_RAB_ASS_ST_WAIT_RABS_ESTABLISHED);
|
||||
/* release list */
|
||||
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_RELEASEDLIST_PRESENT) {
|
||||
/* Multiple RABs may be set up, assemble in list map->ps_rab_list. */
|
||||
RANAP_RAB_ReleasedList_t *r_list = &ies->raB_ReleasedList;
|
||||
for (i = 0; i < r_list->raB_ReleasedList_ies.list.count; i++) {
|
||||
RANAP_IE_t *released_list_ie = r_list->raB_ReleasedList_ies.list.array[i];
|
||||
RANAP_RAB_ReleasedItemIEs_t _rab_rel_item_ies = {};
|
||||
RANAP_RAB_ReleasedItemIEs_t *rab_rel_item_ies = &_rab_rel_item_ies;
|
||||
RANAP_RAB_ReleasedItem_t *rab_rel_item;
|
||||
uint8_t rab_id;
|
||||
struct ps_rab *rab;
|
||||
|
||||
if (!released_list_ie)
|
||||
goto no_rab;
|
||||
if (released_list_ie->id != RANAP_ProtocolIE_ID_id_RAB_ReleasedItem)
|
||||
goto no_rab;
|
||||
|
||||
rc = ranap_decode_rab_releaseditemies_fromlist(rab_rel_item_ies, &released_list_ie->value);
|
||||
if (rc < 0) {
|
||||
LOG_MAP(map, DLPFCP, LOGL_NOTICE, "Rx RAB ASS REQ (REL): Failed decoding ReleaseItem %u\n", i);
|
||||
goto no_rab;
|
||||
}
|
||||
|
||||
rab_rel_item = &rab_rel_item_ies->raB_ReleasedItem;
|
||||
/* The RAB-ID is defined as a bitstring with a size of 8 (1 byte),
|
||||
* See also RANAP-IEs.asn, RAB-ID ::= BIT STRING (SIZE (8)) */
|
||||
rab_id = rab_rel_item->rAB_ID.buf[0];
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_ReleasedItem, rab_rel_item_ies);
|
||||
|
||||
rab = ps_rab_get(map, rab_id);
|
||||
if (rab) {
|
||||
rab_ass->rabs_rel_count++;
|
||||
ps_rab_release(rab, rab_ass->fi);
|
||||
} else {
|
||||
LOG_MAP(map, DLPFCP, LOGL_NOTICE, "Rx RAB ASS REQ (REL) for unknown rab_id %u\n", rab_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rab_ass->rabs_count > 0 || rab_ass->rabs_rel_count > 0) {
|
||||
/* Got all RABs' state and updated their Access side GTP info in map->ps_rab_list.
|
||||
* For each RAB ID, the matching ps_rab_fsm has been instructed to tell the UPF about
|
||||
* the Access Remote GTP F-TEID. Each will call back with PS_RAB_ASS_EV_RAB_ESTABLISHED
|
||||
* or PS_RAB_ASS_EV_RAB_FAIL. */
|
||||
return ps_rab_ass_fsm_state_chg(rab_ass->fi, PS_RAB_ASS_ST_WAIT_RABS_ESTABLISHED);
|
||||
}
|
||||
|
||||
/* No Setup-or-modify nor Release RABs. Forward the message as is. */
|
||||
rc = map_sccp_dispatch(map, MAP_SCCP_EV_TX_DATA_REQUEST, ranap_msg);
|
||||
if (rc < 0) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Sending RANAP PS RAB-AssignmentResponse failed\n");
|
||||
goto no_rab;
|
||||
}
|
||||
/* The request message has been forwarded. We are done. */
|
||||
osmo_fsm_inst_term(rab_ass->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
return 0;
|
||||
|
||||
no_rab:
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ps_rab_ass_resp_send_if_ready(struct ps_rab_ass *rab_ass);
|
||||
static void ps_rab_ass_resp_send_if_ready_quick(struct ps_rab_ass *rab_ass)
|
||||
{
|
||||
/* some RABs are still pending, postpone going through the message until all are done. */
|
||||
if (rab_ass->rabs_done_count < rab_ass->rabs_count)
|
||||
return;
|
||||
if (rab_ass->rabs_rel_done_count < rab_ass->rabs_rel_count)
|
||||
return;
|
||||
ps_rab_ass_resp_send_if_ready(rab_ass);
|
||||
}
|
||||
|
||||
static void ps_rab_ass_fsm_wait_rabs_established(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
@@ -485,12 +549,13 @@ static void ps_rab_ass_fsm_wait_rabs_established(struct osmo_fsm_inst *fi, uint3
|
||||
switch (event) {
|
||||
case PS_RAB_ASS_EV_RAB_ESTABLISHED:
|
||||
rab_ass->rabs_done_count++;
|
||||
if (rab_ass->rabs_done_count < rab_ass->rabs_count) {
|
||||
/* some RABs are still pending, postpone going through the message until all are done. */
|
||||
return;
|
||||
}
|
||||
/* All RABs have succeeded, ready to forward */
|
||||
ps_rab_ass_resp_send_if_ready(rab_ass);
|
||||
ps_rab_ass_resp_send_if_ready_quick(rab_ass);
|
||||
return;
|
||||
|
||||
case PS_RAB_ASS_EV_RAB_RELEASED:
|
||||
rab_ass->rabs_rel_done_count++;
|
||||
ps_rab_ass_resp_send_if_ready_quick(rab_ass);
|
||||
return;
|
||||
|
||||
case PS_RAB_ASS_EV_RAB_FAIL:
|
||||
@@ -607,8 +672,8 @@ continue_cleanloop:
|
||||
if (rc < 0) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Sending RANAP PS RAB-AssignmentResponse failed\n");
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The request message has been forwarded. We are done. */
|
||||
osmo_fsm_inst_term(rab_ass->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
@@ -618,14 +683,14 @@ static void ps_rab_ass_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_
|
||||
struct ps_rab_ass *rab_ass = fi->priv;
|
||||
struct ps_rab *rab;
|
||||
|
||||
llist_for_each_entry(rab, &rab_ass->map->ps_rabs, entry) {
|
||||
llist_for_each_entry(rab, &rab_ass->map->ps_rab_list, entry) {
|
||||
if (rab->req_fi == fi)
|
||||
rab->req_fi = NULL;
|
||||
if (rab->resp_fi == fi)
|
||||
rab->resp_fi = NULL;
|
||||
}
|
||||
|
||||
/* remove from map->ps_rab_ass */
|
||||
/* remove from map->ps_rab_ass_list */
|
||||
llist_del(&rab_ass->entry);
|
||||
}
|
||||
|
||||
@@ -633,10 +698,10 @@ void hnbgw_gtpmap_release(struct hnbgw_context_map *map)
|
||||
{
|
||||
struct ps_rab_ass *rab_ass, *next;
|
||||
struct ps_rab *rab, *next2;
|
||||
llist_for_each_entry_safe(rab, next2, &map->ps_rabs, entry) {
|
||||
ps_rab_release(rab);
|
||||
llist_for_each_entry_safe(rab, next2, &map->ps_rab_list, entry) {
|
||||
ps_rab_release(rab, NULL);
|
||||
}
|
||||
llist_for_each_entry_safe(rab_ass, next, &map->ps_rab_ass, entry) {
|
||||
llist_for_each_entry_safe(rab_ass, next, &map->ps_rab_ass_list, entry) {
|
||||
osmo_fsm_inst_term(rab_ass->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
}
|
||||
@@ -664,6 +729,7 @@ static const struct osmo_fsm_state ps_rab_ass_fsm_states[] = {
|
||||
.action = ps_rab_ass_fsm_wait_rabs_established,
|
||||
.in_event_mask = 0
|
||||
| S(PS_RAB_ASS_EV_RAB_ESTABLISHED)
|
||||
| S(PS_RAB_ASS_EV_RAB_RELEASED)
|
||||
| S(PS_RAB_ASS_EV_RAB_FAIL)
|
||||
,
|
||||
},
|
||||
|
@@ -107,7 +107,7 @@ static struct ps_rab *ps_rab_alloc(struct hnbgw_context_map *map, uint8_t rab_id
|
||||
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&rab->use_count, PS_RAB_USE_ACTIVE, 1) == 0);
|
||||
|
||||
llist_add_tail(&rab->entry, &map->ps_rabs);
|
||||
llist_add_tail(&rab->entry, &map->ps_rab_list);
|
||||
return rab;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ struct ps_rab *ps_rab_find_by_seid(uint64_t seid, bool is_cp_seid)
|
||||
struct hnbgw_context_map *map;
|
||||
llist_for_each_entry(map, &hnb->map_list, hnb_list) {
|
||||
struct ps_rab *rab;
|
||||
llist_for_each_entry(rab, &map->ps_rabs, entry) {
|
||||
llist_for_each_entry(rab, &map->ps_rab_list, entry) {
|
||||
uint64_t rab_seid = is_cp_seid ? rab->cp_seid : rab->up_f_seid.seid;
|
||||
if (rab_seid == seid)
|
||||
return rab;
|
||||
@@ -154,7 +154,7 @@ static struct osmo_pfcp_msg *ps_rab_new_pfcp_msg_req(struct ps_rab *rab, enum os
|
||||
struct ps_rab *ps_rab_get(struct hnbgw_context_map *map, uint8_t rab_id)
|
||||
{
|
||||
struct ps_rab *rab;
|
||||
llist_for_each_entry(rab, &map->ps_rabs, entry) {
|
||||
llist_for_each_entry(rab, &map->ps_rab_list, entry) {
|
||||
if (rab->rab_id != rab_id)
|
||||
continue;
|
||||
return rab;
|
||||
@@ -173,7 +173,7 @@ void ps_rab_failure(struct ps_rab *rab)
|
||||
osmo_fsm_inst_dispatch(rab->req_fi, PS_RAB_ASS_EV_RAB_FAIL, rab);
|
||||
if (rab->resp_fi)
|
||||
osmo_fsm_inst_dispatch(rab->resp_fi, PS_RAB_ASS_EV_RAB_FAIL, rab);
|
||||
ps_rab_release(rab);
|
||||
ps_rab_release(rab, NULL);
|
||||
}
|
||||
|
||||
struct ps_rab *ps_rab_start(struct hnbgw_context_map *map, uint8_t rab_id,
|
||||
@@ -672,6 +672,8 @@ static int ps_rab_fsm_use_cb(struct osmo_use_count_entry *e, int32_t old_use_cou
|
||||
static void ps_rab_fsm_wait_use_count_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct ps_rab *rab = fi->priv;
|
||||
if (rab->resp_fi)
|
||||
osmo_fsm_inst_dispatch(rab->resp_fi, PS_RAB_ASS_EV_RAB_RELEASED, rab);
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&rab->use_count, PS_RAB_USE_ACTIVE, -1) == 0);
|
||||
}
|
||||
|
||||
@@ -692,7 +694,7 @@ static void ps_rab_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
|
||||
static void ps_rab_forget_map(struct ps_rab *rab)
|
||||
{
|
||||
/* remove from map->ps_rabs */
|
||||
/* remove from map->ps_rab_list */
|
||||
if (rab->map)
|
||||
llist_del(&rab->entry);
|
||||
rab->map = NULL;
|
||||
@@ -704,10 +706,12 @@ static void ps_rab_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_caus
|
||||
ps_rab_forget_map(rab);
|
||||
}
|
||||
|
||||
void ps_rab_release(struct ps_rab *rab)
|
||||
/* notify_fi can be NULL, in which case no event PS_RAB_ASS_EV_RAB_RELEASED event will be dispatched */
|
||||
void ps_rab_release(struct ps_rab *rab, struct osmo_fsm_inst *notify_fi)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = rab->fi;
|
||||
ps_rab_forget_map(rab);
|
||||
rab->resp_fi = notify_fi;
|
||||
switch (fi->state) {
|
||||
case PS_RAB_ST_RX_CORE_REMOTE_F_TEID:
|
||||
/* No session requested yet. Nothing to be deleted. */
|
||||
|
107
src/osmo-hnbgw/umts_cell_id.c
Normal file
107
src/osmo-hnbgw/umts_cell_id.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/* UMTS Cell ID */
|
||||
|
||||
/* (C) 2015,2024 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2016-2025 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/jhash.h>
|
||||
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
|
||||
#include <osmocom/hnbgw/umts_cell_id.h>
|
||||
|
||||
int umts_cell_id_to_str_buf(char *buf, size_t buflen, const struct umts_cell_id *ucid)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_APPEND_NOLEN(sb, osmo_plmn_name_buf, &ucid->plmn);
|
||||
OSMO_STRBUF_PRINTF(sb, "-L%u-R%u-S%u-C%u", ucid->lac, ucid->rac, ucid->sac, ucid->cid);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *umts_cell_id_to_str_c(void *ctx, const struct umts_cell_id *ucid)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", umts_cell_id_to_str_buf, ucid)
|
||||
}
|
||||
|
||||
const char *umts_cell_id_to_str(const struct umts_cell_id *ucid)
|
||||
{
|
||||
return umts_cell_id_to_str_c(OTC_SELECT, ucid);
|
||||
}
|
||||
|
||||
/* Useful to index a hash table by struct umts_cell_id. */
|
||||
uint32_t umts_cell_id_hash(const struct umts_cell_id *ucid)
|
||||
{
|
||||
return osmo_jhash(ucid, sizeof(*ucid), 0x423423);
|
||||
}
|
||||
|
||||
/* parse a string representation of an umts_cell_id into its decoded representation */
|
||||
int umts_cell_id_from_str(struct umts_cell_id *ucid, const char *instr)
|
||||
{
|
||||
int rc;
|
||||
char buf[4];
|
||||
const char *pos = instr;
|
||||
const char *end;
|
||||
|
||||
/* We want to use struct umts_cell_id as hashtable key. If it ever happens to contain any padding bytes, make
|
||||
* sure everything is deterministically zero. */
|
||||
memset(ucid, 0, sizeof(*ucid));
|
||||
|
||||
/* read MCC */
|
||||
end = strchr(pos, '-');
|
||||
if (!end || end <= pos || (end - pos) >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
osmo_strlcpy(buf, pos, end - pos + 1);
|
||||
if (osmo_mcc_from_str(buf, &ucid->plmn.mcc))
|
||||
return -EINVAL;
|
||||
pos = end + 1;
|
||||
|
||||
/* read MNC -- here the number of leading zeros matters. */
|
||||
end = strchr(pos, '-');
|
||||
if (!end || end == pos || (end - pos) >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
osmo_strlcpy(buf, pos, end - pos + 1);
|
||||
if (osmo_mnc_from_str(buf, &ucid->plmn.mnc, &ucid->plmn.mnc_3_digits))
|
||||
return -EINVAL;
|
||||
pos = end + 1;
|
||||
|
||||
/* parse the rest, where leading zeros do not matter */
|
||||
rc = sscanf(pos, "L%" SCNu16 "-R%" SCNu8 "-S%" SCNu16 "-C%" SCNu32 "",
|
||||
&ucid->lac, &ucid->rac, &ucid->sac, &ucid->cid);
|
||||
if (rc < 0)
|
||||
return -errno;
|
||||
|
||||
if (rc != 4)
|
||||
return -EINVAL;
|
||||
|
||||
if (ucid->lac == 0 || ucid->lac == 0xffff)
|
||||
return -EINVAL;
|
||||
|
||||
/* CellIdentity in the ASN.1 syntax is a bit-string of 28 bits length */
|
||||
if (ucid->cid >= (1 << 28))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
@@ -134,8 +134,23 @@ OsmoHNBGW(config)# sgsn ?
|
||||
OsmoHNBGW(config)# ### The config file has no 'msc' or 'sgsn', so defaults have been set up
|
||||
OsmoHNBGW(config)# show running-config
|
||||
...
|
||||
cs7 instance 0
|
||||
point-code 0.23.5
|
||||
...
|
||||
sccp-address addr-dyn-msc-default
|
||||
routing-indicator PC
|
||||
point-code 0.23.1
|
||||
subsystem-number 142
|
||||
sccp-address addr-dyn-sgsn-default
|
||||
routing-indicator PC
|
||||
point-code 0.23.4
|
||||
...
|
||||
msc 0
|
||||
name msc-0
|
||||
remote-addr addr-dyn-msc-default
|
||||
sgsn 0
|
||||
name sgsn-0
|
||||
remote-addr addr-dyn-sgsn-default
|
||||
...
|
||||
|
||||
OsmoHNBGW(config)# msc 1
|
||||
@@ -224,9 +239,15 @@ OsmoHNBGW(config)# ### Just by entering the nodes above, 'msc 1' and 'sgsn 1' no
|
||||
OsmoHNBGW(config)# show running-config
|
||||
...
|
||||
msc 0
|
||||
name msc-0
|
||||
remote-addr addr-dyn-msc-default
|
||||
msc 1
|
||||
name msc-1
|
||||
sgsn 0
|
||||
name sgsn-0
|
||||
remote-addr addr-dyn-sgsn-default
|
||||
sgsn 1
|
||||
name sgsn-1
|
||||
...
|
||||
|
||||
OsmoHNBGW(config)# ### Add {msc,sgsn}x{2,3}
|
||||
@@ -252,18 +273,28 @@ OsmoHNBGW(config-sgsn)# exit
|
||||
OsmoHNBGW(config)# show running-config
|
||||
...
|
||||
msc 0
|
||||
name msc-0
|
||||
remote-addr addr-dyn-msc-default
|
||||
msc 1
|
||||
name msc-1
|
||||
msc 2
|
||||
name msc-2
|
||||
remote-addr addr-msc2
|
||||
no allow-attach
|
||||
msc 3
|
||||
name msc-3
|
||||
remote-addr addr-msc3
|
||||
allow-emergency
|
||||
sgsn 0
|
||||
name sgsn-0
|
||||
remote-addr addr-dyn-sgsn-default
|
||||
sgsn 1
|
||||
name sgsn-1
|
||||
sgsn 2
|
||||
name sgsn-2
|
||||
remote-addr addr-sgsn2
|
||||
sgsn 3
|
||||
name sgsn-3
|
||||
remote-addr addr-sgsn3
|
||||
...
|
||||
|
||||
@@ -282,16 +313,26 @@ OsmoHNBGW(config-sgsn)# exit
|
||||
OsmoHNBGW(config)# show running-config
|
||||
...
|
||||
msc 0
|
||||
name msc-0
|
||||
remote-addr addr-dyn-msc-default
|
||||
msc 1
|
||||
name msc-1
|
||||
msc 2
|
||||
name msc-2
|
||||
remote-addr addr-msc2
|
||||
msc 3
|
||||
name msc-3
|
||||
remote-addr addr-msc4
|
||||
sgsn 0
|
||||
name sgsn-0
|
||||
remote-addr addr-dyn-sgsn-default
|
||||
sgsn 1
|
||||
name sgsn-1
|
||||
sgsn 2
|
||||
name sgsn-2
|
||||
remote-addr addr-sgsn4
|
||||
sgsn 3
|
||||
name sgsn-3
|
||||
remote-addr addr-sgsn3
|
||||
...
|
||||
|
||||
@@ -321,18 +362,26 @@ OsmoHNBGW(config-hnbgw)# show running-config
|
||||
hnbgw
|
||||
...
|
||||
msc 0
|
||||
name msc-0
|
||||
remote-addr addr-msc0
|
||||
msc 1
|
||||
name msc-1
|
||||
msc 2
|
||||
name msc-2
|
||||
remote-addr addr-msc2
|
||||
msc 3
|
||||
name msc-3
|
||||
remote-addr addr-msc4
|
||||
sgsn 0
|
||||
name sgsn-0
|
||||
remote-addr addr-sgsn0
|
||||
sgsn 1
|
||||
name sgsn-1
|
||||
sgsn 2
|
||||
name sgsn-2
|
||||
remote-addr addr-sgsn4
|
||||
sgsn 3
|
||||
name sgsn-3
|
||||
remote-addr addr-sgsn3
|
||||
...
|
||||
|
||||
@@ -348,3 +397,36 @@ hnbgw
|
||||
...
|
||||
msc 0
|
||||
...
|
||||
|
||||
OsmoHNBGW(config-hnbgw)# exit
|
||||
OsmoHNBGW(config)# ### Apply all SCCP changes:
|
||||
OsmoHNBGW(config)# apply sccp
|
||||
OsmoHNBGW(config)# show running-config
|
||||
...
|
||||
hnbgw
|
||||
...
|
||||
msc 0
|
||||
name msc-0
|
||||
remote-addr addr-msc0
|
||||
msc 1
|
||||
name msc-1
|
||||
remote-addr addr-dyn-msc-default
|
||||
msc 2
|
||||
name msc-2
|
||||
remote-addr addr-msc2
|
||||
msc 3
|
||||
name msc-3
|
||||
remote-addr addr-msc4
|
||||
sgsn 0
|
||||
name sgsn-0
|
||||
remote-addr addr-sgsn0
|
||||
sgsn 1
|
||||
name sgsn-1
|
||||
remote-addr addr-dyn-sgsn-default
|
||||
sgsn 2
|
||||
name sgsn-2
|
||||
remote-addr addr-sgsn4
|
||||
sgsn 3
|
||||
name sgsn-3
|
||||
remote-addr addr-sgsn3
|
||||
...
|
||||
|
@@ -24,15 +24,18 @@
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/ranap/iu_helpers.h>
|
||||
#include <osmocom/hnbgw/ranap_rab_ass.h>
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/ranap_common_cn.h>
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/ranap_rab_ass.h>
|
||||
|
||||
static void *msgb_ctx;
|
||||
extern void *talloc_asn1_ctx;
|
||||
|
||||
|
@@ -1,6 +1,9 @@
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/hnbgw/umts_cell_id.h>
|
||||
|
||||
struct test {
|
||||
const char *id_str;
|
||||
|
Reference in New Issue
Block a user