mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw.git
synced 2025-11-02 13:03:26 +00:00
Compare commits
85 Commits
1.6.1
...
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 | ||
|
|
05dd0272d0 | ||
|
|
0b4374bb9f | ||
|
|
f5b5c75d2b | ||
|
|
ca09bc0c44 | ||
|
|
70e2ee8a0a | ||
|
|
edc7baa3b8 | ||
|
|
050704c562 | ||
|
|
d25fa3723b | ||
|
|
bad546b431 | ||
|
|
af0a6aeb39 | ||
|
|
78fd0b97a9 | ||
|
|
580411823d | ||
|
|
5d91358114 | ||
|
|
d8b9a53a44 | ||
|
|
804ebe1593 | ||
|
|
8897a7764a | ||
|
|
7f3a50c619 | ||
|
|
79c5600bb6 | ||
|
|
aa2cefb173 | ||
|
|
d37f3dc20c | ||
|
|
f063d6bab8 | ||
|
|
619b81e5dc | ||
|
|
a824ebd8c2 | ||
|
|
71ad913d99 | ||
|
|
dbbfefad2c | ||
|
|
167f7aaac8 | ||
|
|
4ed837cba8 | ||
|
|
7784eb4e3b | ||
|
|
1a15261931 | ||
|
|
fb450888b6 | ||
|
|
a3c0a00573 | ||
|
|
e6d87ff967 | ||
|
|
1ac1d1277b | ||
|
|
1d4811e38c | ||
|
|
89504050ea | ||
|
|
7958955624 | ||
|
|
ca6f6fded9 | ||
|
|
3140381337 | ||
|
|
6f0197878a | ||
|
|
3fb99d143a | ||
|
|
30068ad391 | ||
|
|
1eb2a759ad | ||
|
|
280f9e6ca9 | ||
|
|
2fc8abeecf | ||
|
|
71f3169140 | ||
|
|
61e278a452 | ||
|
|
4468d8728e |
@@ -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}()
|
||||
|
||||
22
configure.ac
22
configure.ac
@@ -49,22 +49,22 @@ AC_SEARCH_LIBS([sctp_recvmsg], [sctp], [
|
||||
LIBS=$old_LIBS
|
||||
|
||||
PKG_CHECK_MODULES(LIBASN1C, libasn1c >= 0.9.30)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.13.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 2.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 1.7.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.7.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 1.7.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.14.0)
|
||||
|
||||
# Enable PFCP support for GTP tunnel mapping via UPF
|
||||
AC_ARG_ENABLE([pfcp], [AS_HELP_STRING([--enable-pfcp], [Build with PFCP support, for GTP tunnel mapping via UPF])],
|
||||
[osmo_ac_pfcp="$enableval"],[osmo_ac_pfcp="no"])
|
||||
if test "x$osmo_ac_pfcp" = "xyes" ; then
|
||||
PKG_CHECK_MODULES(LIBOSMOPFCP, libosmo-pfcp >= 0.4.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOPFCP, libosmo-pfcp >= 0.5.0)
|
||||
AC_DEFINE(ENABLE_PFCP, 1, [Define to build with PFCP support])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_PFCP, test "x$osmo_ac_pfcp" = "xyes")
|
||||
|
||||
@@ -34,9 +34,9 @@ export LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
||||
osmo-build-dep.sh libosmo-abis
|
||||
osmo-build-dep.sh libosmo-netif
|
||||
osmo-build-dep.sh libosmo-sccp
|
||||
osmo-build-dep.sh libosmo-sigtran "" --disable-doxygen
|
||||
osmo-build-dep.sh libasn1c
|
||||
osmo-build-dep.sh osmo-iuh
|
||||
osmo-build-dep.sh osmo-mgw
|
||||
|
||||
36
debian/changelog
vendored
36
debian/changelog
vendored
@@ -1,3 +1,39 @@
|
||||
osmo-hnbgw (1.7.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Janosch Hofmeyr ]
|
||||
* drop config.vty tests from make check
|
||||
* coverity CID#358071
|
||||
* coverity CID#358072
|
||||
* coverity CID#358070
|
||||
* coverity CID#358069
|
||||
* on RUA Connect failure, respond with RUA Disconnect
|
||||
* on RUA DT for unknown context, respond with RUA Disconnect
|
||||
|
||||
[ Harald Welte ]
|
||||
* hnb_persistent: Use incrementing counter for rate_ctr + stat_item index
|
||||
* Revert "hnb_persistent: Use incrementing counter for rate_ctr + stat_item index"
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib/jenkins: libosmo-sccp -> libosmo-sigtran
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Log cn_disconnect.ind cause
|
||||
* Fix SCCP RLSD not sent upon rx RUA Disconnect due to error condition
|
||||
* ps_rab_ass_fsm: Fix uninitialized ptr access
|
||||
* Add PFCP stats item group
|
||||
* hnbgw: Avoid using struct osmo_pfcp_cp_peer fields directly
|
||||
* hnbgw: Fix wrong map object retrieved from hashtable
|
||||
* Use new libosmo-sigtran APIs to access osmo_ss7_instance
|
||||
* jenkins.sh: libosmo-netif no longer depends on libosmo-abis
|
||||
* jenkins.sh: No need to build libosmo-sigtran with doxygen
|
||||
* map_sccp: Fix crash during ev MAP_SCCP_EV_RAN_DISC in st MAP_SCCP_ST_WAIT_CC
|
||||
* peek_l3_ul_nas: Split parsing logic based on PS/CS ctx
|
||||
* peek_l3_ul_nas: Improve RAU req parsing checks
|
||||
* peek_l3_ul_nas: Improve GMM Attach Req parsing checks
|
||||
* peek_l3_ul_nas: Parse PTMSI MI from GMM RAU req
|
||||
|
||||
-- Oliver Smith <osmith@sysmocom.de> Wed, 12 Feb 2025 14:37:35 +0100
|
||||
|
||||
osmo-hnbgw (1.6.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
|
||||
18
debian/control
vendored
18
debian/control
vendored
@@ -13,15 +13,15 @@ Build-Depends: debhelper (>= 10),
|
||||
libtalloc-dev,
|
||||
libasn1c-dev (>= 0.9.30),
|
||||
libsctp-dev,
|
||||
libosmocore-dev (>= 1.10.0),
|
||||
libosmo-sigtran-dev (>= 1.9.0),
|
||||
libosmo-abis-dev (>= 1.6.0),
|
||||
libosmo-netif-dev (>= 1.5.0),
|
||||
libosmo-mgcp-client-dev (>= 1.13.0),
|
||||
libosmo-hnbap-dev (>= 1.6.0),
|
||||
libosmo-ranap-dev (>= 1.6.0),
|
||||
libosmo-rua-dev (>= 1.6.0),
|
||||
libosmo-pfcp-dev (>= 0.4.0),
|
||||
libosmocore-dev (>= 1.11.0),
|
||||
libosmo-sigtran-dev (>= 2.1.0),
|
||||
libosmo-abis-dev (>= 2.0.0),
|
||||
libosmo-netif-dev (>= 1.6.0),
|
||||
libosmo-mgcp-client-dev (>= 1.14.0),
|
||||
libosmo-hnbap-dev (>= 1.7.0),
|
||||
libosmo-ranap-dev (>= 1.7.0),
|
||||
libosmo-rua-dev (>= 1.7.0),
|
||||
libosmo-pfcp-dev (>= 0.5.0),
|
||||
libnftables-dev,
|
||||
osmo-gsm-manuals-dev (>= 1.6.0)
|
||||
Standards-Version: 3.9.8
|
||||
|
||||
@@ -13,7 +13,6 @@ hnbgw
|
||||
plmn 001 01
|
||||
iuh
|
||||
local-ip 0.0.0.0
|
||||
hnbap-allow-tmsi 1
|
||||
msc 0
|
||||
remote-addr my-msc
|
||||
sgsn 0
|
||||
|
||||
@@ -11,7 +11,6 @@ hnbgw
|
||||
plmn 001 01
|
||||
iuh
|
||||
local-ip 0.0.0.0
|
||||
hnbap-allow-tmsi 1
|
||||
mgw 0
|
||||
remote-ip 127.0.0.1
|
||||
local-port 2729
|
||||
|
||||
@@ -11,7 +11,6 @@ hnbgw
|
||||
plmn 001 01
|
||||
iuh
|
||||
local-ip 0.0.0.0
|
||||
hnbap-allow-tmsi 1
|
||||
mgw 0
|
||||
remote-ip 127.0.0.1
|
||||
local-port 2729
|
||||
|
||||
@@ -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)
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#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>
|
||||
|
||||
@@ -28,13 +30,17 @@
|
||||
* - The RANAP message shall be at msgb_l2().
|
||||
*/
|
||||
enum map_rua_fsm_event {
|
||||
/* Receiving a RUA Connect from HNB. */
|
||||
/* Receiving a RUA Connect from HNB.
|
||||
* Parameter: struct msgb *ranap_msg */
|
||||
MAP_RUA_EV_RX_CONNECT,
|
||||
/* Receiving some data from HNB via RUA, to forward via SCCP to CN. */
|
||||
/* Receiving some data from HNB via RUA, to forward via SCCP to CN.
|
||||
* Parameter: struct msgb *ranap_msg */
|
||||
MAP_RUA_EV_RX_DIRECT_TRANSFER,
|
||||
/* Receiving a RUA Disconnect from HNB. */
|
||||
/* Receiving a RUA Disconnect from HNB.
|
||||
* Parameter: struct msgb *ranap_msg (can be NULL) */
|
||||
MAP_RUA_EV_RX_DISCONNECT,
|
||||
/* SCCP has received some data from CN to forward via RUA to HNB. */
|
||||
/* SCCP has received some data from CN to forward via RUA to HNB.
|
||||
* Parameter: struct msgb *ranap_msg */
|
||||
MAP_RUA_EV_TX_DIRECT_TRANSFER,
|
||||
/* The CN side is disconnected (e.g. received an SCCP Released), that means we are going gracefully disconnect
|
||||
* RUA, too. */
|
||||
@@ -49,25 +55,41 @@ 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. */
|
||||
/* Receiving some data from CN via SCCP, to forward via RUA to HNB.
|
||||
* 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. */
|
||||
/* RUA has received some data from HNB to forward via SCCP to CN.
|
||||
* Parameter: struct msgb *ranap_msg. */
|
||||
MAP_SCCP_EV_TX_DATA_REQUEST,
|
||||
/* The RAN side received a Disconnect, that means we are going to expect SCCP to disconnect too.
|
||||
* CN should have received an Iu-ReleaseComplete with or before this, give CN a chance to send an SCCP RLSD;
|
||||
* after a timeout we will send a non-standard RLSD to the CN instead. */
|
||||
/* 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.
|
||||
* On SCCP, the Iu-ReleaseComplete should still be forwarded as N-Data SCCP Data Form 1),
|
||||
* and we will expect the CN to send an SCCP RLSD soon. Hence, give CN a chance to send an SCCP RLSD;
|
||||
* after a timeout we will send a non-standard RLSD to the CN instead.
|
||||
* - Under error conditions, cause!=Normal and there's no RANAP message.
|
||||
* In that case, we need to tear down the associated SCCP link towards CN with an RLSD,
|
||||
* which in turn will tear down the upper layer Iu conn.
|
||||
*
|
||||
* Parameter: bool rua_disconnect_err_condition, whether the disconnect
|
||||
* 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,
|
||||
};
|
||||
|
||||
@@ -108,6 +130,10 @@ enum rab_state {
|
||||
RAB_STATE_ACTIVE,
|
||||
RAB_STATE_REL_REQ,
|
||||
};
|
||||
extern const struct value_string hnbgw_rab_state_names[];
|
||||
static inline const char *hnbgw_rab_state_name(enum rab_state val)
|
||||
{ return get_value_string(hnbgw_rab_state_names, val); }
|
||||
|
||||
|
||||
struct hnbgw_context_map {
|
||||
/* entry in the per-CN list of mappings */
|
||||
@@ -127,12 +153,28 @@ struct hnbgw_context_map {
|
||||
/* FSM handling the RUA state for rua_ctx_id. */
|
||||
struct osmo_fsm_inst *rua_fi;
|
||||
|
||||
/* State context related to field rua_fi above: */
|
||||
struct {
|
||||
/* Whether RUA Disconnect received from HNB happened as a normal condition or an error/abnormal condition.
|
||||
* This is known based on cause and/or RANAP message included in the RUA
|
||||
* Disconnect message, and tells us whether we should immediately
|
||||
* terminate the related SCCP session or wait for CN to finish it.
|
||||
* Defaults to false, only set to true explicitly when needed. */
|
||||
bool rua_disconnect_err_condition;
|
||||
} rua_fi_ctx;
|
||||
|
||||
/* Pointer to CN, to transceive SCCP. */
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
/* SCCP User SAP connection ID used in SCCP messages to/from the cn_link. */
|
||||
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;
|
||||
@@ -158,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) */
|
||||
uint16_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,108 +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 cnlink->hnbgw_sccp_user->ss7->sccp;
|
||||
}
|
||||
|
||||
/* 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 ue_context {
|
||||
/*! Entry in the HNB-global list of UE */
|
||||
struct llist_head list;
|
||||
/*! Unique Context ID for this UE */
|
||||
uint32_t context_id;
|
||||
char imsi[16+1];
|
||||
uint32_t tmsi;
|
||||
/*! UE is serviced via this HNB */
|
||||
struct hnb_context *hnb;
|
||||
};
|
||||
|
||||
struct hnbgw {
|
||||
struct {
|
||||
const char *iuh_local_ip;
|
||||
@@ -479,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. */
|
||||
@@ -490,6 +146,8 @@ struct hnbgw {
|
||||
struct {
|
||||
struct osmo_pfcp_endpoint *ep;
|
||||
struct osmo_pfcp_cp_peer *cp_peer;
|
||||
/* Running counters for the PFCP conn */
|
||||
struct osmo_stat_item_group *statg;
|
||||
} pfcp;
|
||||
|
||||
struct osmo_timer_list hnb_store_rab_durations_timer;
|
||||
@@ -507,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);
|
||||
|
||||
@@ -540,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,25 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/hnbgw/hnbgw.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);
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
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);
|
||||
#include <osmocom/ranap/RANAP_CN-DomainIndicator.h>
|
||||
|
||||
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);
|
||||
struct hnbgw_context_map;
|
||||
|
||||
enum hnbgw_cnpool_ctr {
|
||||
/* TODO: basic counters completely missing
|
||||
@@ -31,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);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/* HNBAP, 3GPP TS 25.469 */
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
|
||||
struct hnb_context;
|
||||
|
||||
int hnbgw_hnbap_rx(struct hnb_context *hnb, struct msgb *msg);
|
||||
int hnbgw_hnbap_init(void);
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
enum hnbgw_upf_stats {
|
||||
HNBGW_UPF_STAT_ASSOCIATED,
|
||||
};
|
||||
#define HNBGW_UPF_STAT_SET(stat, val) osmo_stat_item_set(osmo_stat_item_group_get_item(g_hnbgw->pfcp.statg, (stat)), (val))
|
||||
|
||||
int hnbgw_pfcp_init(void);
|
||||
void hnbgw_pfcp_release(void);
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
/* RANAP, 3GPP TS 25.413 */
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
|
||||
struct osmo_scu_unitdata_param;
|
||||
|
||||
ranap_message *hnbgw_decode_ranap_cn_co(struct msgb *ranap_msg);
|
||||
|
||||
int hnbgw_ranap_rx_udt_ul(struct msgb *msg, uint8_t *data, size_t len);
|
||||
int hnbgw_ranap_rx_data_ul(struct hnbgw_context_map *map, struct msgb *ranap_msg);
|
||||
|
||||
int hnbgw_ranap_rx_udt_dl(struct hnbgw_cnlink *cnlink, const struct osmo_scu_unitdata_param *unitdata,
|
||||
const uint8_t *data, unsigned int len);
|
||||
int hnbgw_ranap_rx_data_dl(struct hnbgw_context_map *map, struct msgb *ranap_msg);
|
||||
int hnbgw_ranap_init(void);
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/* RUA, 3GPP TS 25.468 */
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/rua/RUA_Cause.h>
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
|
||||
struct hnb_context;
|
||||
|
||||
int hnbgw_rua_rx(struct hnb_context *hnb, struct msgb *msg);
|
||||
|
||||
@@ -12,4 +14,3 @@ int rua_tx_dt(struct hnb_context *hnb, int is_ps, uint32_t context_id,
|
||||
int rua_tx_disc(struct hnb_context *hnb, int is_ps, uint32_t context_id,
|
||||
const RUA_Cause_t *cause, const uint8_t *data, unsigned int len);
|
||||
|
||||
ranap_message *hnbgw_decode_ranap_co(struct msgb *ranap_msg);
|
||||
|
||||
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,22 +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);
|
||||
}
|
||||
|
||||
230
src/osmo-hnbgw/cnlink_paging.c
Normal file
230
src/osmo-hnbgw/cnlink_paging.c
Normal file
@@ -0,0 +1,230 @@
|
||||
/* RANAP Paging of HNB-GW */
|
||||
/* (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 <errno.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <asn1c/asn1helpers.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/ranap/iu_helpers.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
|
||||
/***************
|
||||
* This module manages the list of "struct cnlink_paging" items in (struct
|
||||
* hnbgw_cnlink *)->paging.
|
||||
* Every time a new RANAP Paging Cmd arrives from some cnlink,
|
||||
* cnlink_paging_add_ranap() is called to potentially store the paging command
|
||||
* for a while.
|
||||
* When a paging response is received from HNB, cnlink_find_by_paging_mi() is
|
||||
* called to obtain the cnlink it should be routed back to.
|
||||
*/
|
||||
|
||||
struct cnlink_paging {
|
||||
struct llist_head entry;
|
||||
|
||||
struct osmo_mobile_identity mi;
|
||||
struct osmo_mobile_identity mi2;
|
||||
time_t timestamp;
|
||||
};
|
||||
|
||||
static int cnlink_paging_destructor(struct cnlink_paging *p)
|
||||
{
|
||||
llist_del(&p->entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Return current timestamp in *timestamp, and the oldest still valid timestamp according to T3113 timeout. */
|
||||
static const char *cnlink_paging_gettime(time_t *timestamp_p, time_t *timeout_p)
|
||||
{
|
||||
struct timespec now;
|
||||
time_t timestamp;
|
||||
|
||||
/* get timestamp */
|
||||
if (osmo_clock_gettime(CLOCK_MONOTONIC, &now) != 0)
|
||||
return "cannot get timestamp";
|
||||
timestamp = now.tv_sec;
|
||||
|
||||
if (timestamp_p)
|
||||
*timestamp_p = timestamp;
|
||||
if (timeout_p)
|
||||
*timeout_p = timestamp - osmo_tdef_get(hnbgw_T_defs, 3113, OSMO_TDEF_S, 15);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *cnlink_paging_add(struct hnbgw_cnlink *cnlink, const struct osmo_mobile_identity *mi,
|
||||
const struct osmo_mobile_identity *mi2)
|
||||
{
|
||||
struct cnlink_paging *p, *p2;
|
||||
time_t timestamp;
|
||||
time_t timeout;
|
||||
const char *errmsg;
|
||||
|
||||
errmsg = cnlink_paging_gettime(×tamp, &timeout);
|
||||
if (errmsg)
|
||||
return errmsg;
|
||||
|
||||
/* Prune all paging records that are older than the configured timeout. */
|
||||
llist_for_each_entry_safe(p, p2, &cnlink->paging, entry) {
|
||||
if (p->timestamp >= timeout)
|
||||
continue;
|
||||
talloc_free(p);
|
||||
}
|
||||
|
||||
/* Add new entry */
|
||||
p = talloc_zero(cnlink, struct cnlink_paging);
|
||||
*p = (struct cnlink_paging){
|
||||
.timestamp = timestamp,
|
||||
.mi = *mi,
|
||||
.mi2 = *mi2,
|
||||
};
|
||||
llist_add_tail(&p->entry, &cnlink->paging);
|
||||
talloc_set_destructor(p, cnlink_paging_destructor);
|
||||
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_INFO, "Rx Paging from CN for %s %s\n",
|
||||
osmo_mobile_identity_to_str_c(OTC_SELECT, mi),
|
||||
osmo_mobile_identity_to_str_c(OTC_SELECT, mi2));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *omi_from_ranap_ue_id(struct osmo_mobile_identity *mi, const RANAP_PermanentNAS_UE_ID_t *ranap_mi)
|
||||
{
|
||||
if (!ranap_mi)
|
||||
return "null UE ID";
|
||||
|
||||
if (ranap_mi->present != RANAP_PermanentNAS_UE_ID_PR_iMSI)
|
||||
return talloc_asprintf(OTC_SELECT, "unsupported UE ID type %u in RANAP Paging", ranap_mi->present);
|
||||
|
||||
if (ranap_mi->choice.iMSI.size > sizeof(mi->imsi))
|
||||
return talloc_asprintf(OTC_SELECT, "invalid IMSI size %d > %zu",
|
||||
ranap_mi->choice.iMSI.size, sizeof(mi->imsi));
|
||||
|
||||
*mi = (struct osmo_mobile_identity){
|
||||
.type = GSM_MI_TYPE_IMSI,
|
||||
};
|
||||
ranap_bcd_decode(mi->imsi, sizeof(mi->imsi), ranap_mi->choice.iMSI.buf, ranap_mi->choice.iMSI.size);
|
||||
LOGP(DCN, LOGL_DEBUG, "ranap MI %s = %s\n", osmo_hexdump(ranap_mi->choice.iMSI.buf, ranap_mi->choice.iMSI.size),
|
||||
mi->imsi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *omi_from_ranap_temp_ue_id(struct osmo_mobile_identity *mi, const RANAP_TemporaryUE_ID_t *ranap_tmsi)
|
||||
{
|
||||
const OCTET_STRING_t *tmsi_str;
|
||||
|
||||
if (!ranap_tmsi)
|
||||
return "null UE ID";
|
||||
|
||||
switch (ranap_tmsi->present) {
|
||||
case RANAP_TemporaryUE_ID_PR_tMSI:
|
||||
tmsi_str = &ranap_tmsi->choice.tMSI;
|
||||
break;
|
||||
case RANAP_TemporaryUE_ID_PR_p_TMSI:
|
||||
tmsi_str = &ranap_tmsi->choice.p_TMSI;
|
||||
break;
|
||||
default:
|
||||
return talloc_asprintf(OTC_SELECT, "unsupported Temporary UE ID type %u in RANAP Paging", ranap_tmsi->present);
|
||||
}
|
||||
|
||||
*mi = (struct osmo_mobile_identity){
|
||||
.type = GSM_MI_TYPE_TMSI,
|
||||
.tmsi = asn1str_to_u32(tmsi_str),
|
||||
};
|
||||
LOGP(DCN, LOGL_DEBUG, "ranap temp UE ID = %s\n", osmo_mobile_identity_to_str_c(OTC_SELECT, mi));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *cnlink_paging_add_ranap(struct hnbgw_cnlink *cnlink, const RANAP_PagingIEs_t *paging_ies)
|
||||
{
|
||||
struct osmo_mobile_identity mi = {};
|
||||
struct osmo_mobile_identity mi2 = {};
|
||||
RANAP_CN_DomainIndicator_t domain;
|
||||
const char *errmsg;
|
||||
|
||||
domain = paging_ies->cN_DomainIndicator;
|
||||
errmsg = omi_from_ranap_ue_id(&mi, &paging_ies->permanentNAS_UE_ID);
|
||||
|
||||
if (!errmsg && (paging_ies->presenceMask & PAGINGIES_RANAP_TEMPORARYUE_ID_PRESENT))
|
||||
errmsg = omi_from_ranap_temp_ue_id(&mi2, &paging_ies->temporaryUE_ID);
|
||||
|
||||
LOG_CNLINK(cnlink, DCN, errmsg ? LOGL_NOTICE : LOGL_DEBUG,
|
||||
"Decoded Paging: %s %s %s%s%s\n",
|
||||
ranap_domain_name(domain),
|
||||
osmo_mobile_identity_to_str_c(OTC_SELECT, &mi),
|
||||
mi2.type ? osmo_mobile_identity_to_str_c(OTC_SELECT, &mi2) : "-",
|
||||
errmsg ? " -- MI error: " : "",
|
||||
errmsg ? : "");
|
||||
|
||||
if (errmsg)
|
||||
return errmsg;
|
||||
|
||||
return cnlink_paging_add(cnlink, &mi, &mi2);
|
||||
}
|
||||
|
||||
/* If this cnlink has a recent Paging for the given MI, return true and drop the Paging record.
|
||||
* Else return false. */
|
||||
static bool cnlink_match_paging_mi(struct hnbgw_cnlink *cnlink, const struct osmo_mobile_identity *mi, time_t timeout)
|
||||
{
|
||||
struct cnlink_paging *p, *p2;
|
||||
llist_for_each_entry_safe(p, p2, &cnlink->paging, entry) {
|
||||
if (p->timestamp < timeout) {
|
||||
talloc_free(p);
|
||||
continue;
|
||||
}
|
||||
if (osmo_mobile_identity_cmp(&p->mi, mi)
|
||||
&& osmo_mobile_identity_cmp(&p->mi2, mi))
|
||||
continue;
|
||||
talloc_free(p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct hnbgw_cnlink *cnlink_find_by_paging_mi(struct hnbgw_cnpool *cnpool, const struct osmo_mobile_identity *mi)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
time_t timeout = 0;
|
||||
const char *errmsg;
|
||||
|
||||
errmsg = cnlink_paging_gettime(NULL, &timeout);
|
||||
if (errmsg)
|
||||
LOGP(DCN, LOGL_ERROR, "%s\n", errmsg);
|
||||
|
||||
llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) {
|
||||
if (!cnlink_match_paging_mi(cnlink, mi, timeout))
|
||||
continue;
|
||||
return cnlink;
|
||||
}
|
||||
return 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);
|
||||
|
||||
@@ -111,7 +112,7 @@ int context_map_set_cnlink(struct hnbgw_context_map *map, struct hnbgw_cnlink *c
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
new_scu_conn_id = osmo_sccp_instance_next_conn_id(hsu->ss7->sccp);
|
||||
new_scu_conn_id = osmo_sccp_instance_next_conn_id(osmo_ss7_get_sccp(hsu->ss7));
|
||||
if (new_scu_conn_id < 0) {
|
||||
LOG_MAP(map, DCN, LOGL_ERROR, "Unable to allocate SCCP conn ID on %s\n", hsu->name);
|
||||
return new_scu_conn_id;
|
||||
@@ -125,7 +126,7 @@ int context_map_set_cnlink(struct hnbgw_context_map *map, struct hnbgw_cnlink *c
|
||||
|
||||
hash_add(hsu->hnbgw_context_map_by_conn_id, &map->hnbgw_sccp_user_entry, new_scu_conn_id);
|
||||
|
||||
LOGP(DRUA, LOGL_NOTICE, "New conn: %s '%s' RUA-%u <-> SCCP-%u %s%s%s %s l=%s<->r=%s\n",
|
||||
LOGP(DRUA, LOGL_INFO, "New conn: %s '%s' RUA-%u <-> SCCP-%u %s%s%s %s l=%s<->r=%s\n",
|
||||
osmo_sock_get_name2_c(OTC_SELECT, osmo_stream_srv_get_ofd(map->hnb_ctx->conn)->fd),
|
||||
hnb_context_name(map->hnb_ctx), map->rua_ctx_id,
|
||||
new_scu_conn_id,
|
||||
@@ -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,19 +24,12 @@
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_common_cn.h>
|
||||
|
||||
#if ENABLE_PFCP
|
||||
#include <osmocom/pfcp/pfcp_cp_peer.h>
|
||||
#endif
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
#include <osmocom/hnbgw/ps_rab_ass_fsm.h>
|
||||
#include <osmocom/hnbgw/kpi.h>
|
||||
#include <osmocom/hnbgw/hnbgw_ranap.h>
|
||||
|
||||
enum map_rua_fsm_state {
|
||||
MAP_RUA_ST_INIT,
|
||||
@@ -128,32 +121,6 @@ static int map_rua_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
}
|
||||
}
|
||||
|
||||
static int destruct_ranap_cn_rx_co_ies(ranap_message *ranap_message_p)
|
||||
{
|
||||
ranap_cn_rx_co_free(ranap_message_p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decode RANAP message with convenient memory freeing: just talloc_free() the returned pointer..
|
||||
* Allocate a ranap_message from OTC_SELECT, decode RANAP msgb into it, attach a talloc destructor that calls
|
||||
* ranap_cn_rx_co_free() upon talloc_free(), and return the decoded ranap_message. */
|
||||
ranap_message *hnbgw_decode_ranap_co(struct msgb *ranap_msg)
|
||||
{
|
||||
int rc;
|
||||
ranap_message *message;
|
||||
|
||||
if (!msg_has_l2_data(ranap_msg))
|
||||
return NULL;
|
||||
message = talloc_zero(OTC_SELECT, ranap_message);
|
||||
rc = ranap_cn_rx_co_decode2(message, msgb_l2(ranap_msg), msgb_l2len(ranap_msg));
|
||||
if (rc != 0) {
|
||||
talloc_free(message);
|
||||
return NULL;
|
||||
}
|
||||
talloc_set_destructor(message, destruct_ranap_cn_rx_co_ies);
|
||||
return message;
|
||||
}
|
||||
|
||||
/* Dispatch RANAP message to SCCP, if any. */
|
||||
static int handle_rx_rua(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
{
|
||||
@@ -166,37 +133,7 @@ static int handle_rx_rua(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
if (!msg_has_l2_data(ranap_msg))
|
||||
return 0;
|
||||
|
||||
ranap_message *message = hnbgw_decode_ranap_co(ranap_msg);
|
||||
if (message) {
|
||||
LOGPFSML(fi, LOGL_DEBUG, "rx from RUA: RANAP %s\n",
|
||||
get_value_string(ranap_procedure_code_vals, message->procedureCode));
|
||||
|
||||
kpi_ranap_process_ul(map, message);
|
||||
|
||||
if (!map->is_ps) {
|
||||
/* See if it is a RAB Assignment Response message from RUA to SCCP, where we need to change the user plane
|
||||
* information, for RTP mapping via MGW, or GTP mapping via UPF. */
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* mgw_fsm_handle_rab_ass_resp() takes ownership of prim->oph and (ranap) message */
|
||||
return mgw_fsm_handle_cs_rab_ass_resp(map, ranap_msg, message);
|
||||
}
|
||||
} else {
|
||||
#if ENABLE_PFCP
|
||||
if (hnb_gw_is_gtp_mapping_enabled()) {
|
||||
/* map->is_ps == true and PFCP is enabled in osmo-hnbgw.cfg */
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* ps_rab_ass_fsm takes ownership of prim->oph and RANAP message */
|
||||
return hnbgw_gtpmap_rx_rab_ass_resp(map, ranap_msg, message);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* It was not a RAB Assignment Response that needed to be intercepted. Forward as-is to SCCP. */
|
||||
return map_sccp_dispatch(map, MAP_SCCP_EV_TX_DATA_REQUEST, ranap_msg);
|
||||
return hnbgw_ranap_rx_data_ul(map, ranap_msg);
|
||||
}
|
||||
|
||||
static int forward_ranap_to_rua(struct hnbgw_context_map *map, struct msgb *ranap_msg)
|
||||
@@ -287,15 +224,21 @@ static void map_rua_connected_action(struct osmo_fsm_inst *fi, uint32_t event, v
|
||||
return;
|
||||
|
||||
case MAP_RUA_EV_RX_DISCONNECT:
|
||||
/* received Disconnect from RUA. forward any payload to SCCP, and change state. */
|
||||
if (!map_sccp_is_active(map)) {
|
||||
/* If, unlikely, the SCCP is already gone, changing to MAP_RUA_ST_DISCONNECTED frees the
|
||||
* hnbgw_context_map. Avoid a use-after-free. */
|
||||
map_rua_fsm_state_chg(MAP_RUA_ST_DISCONNECTED);
|
||||
return;
|
||||
/* 3GPP TS 25.468 9.1.5: RUA has disconnected.
|
||||
* - Under normal conditions (cause=Normal) the RUA Disconnect contains a RANAP Iu-ReleaseComplete.
|
||||
* On SCCP, the Iu-ReleaseComplete should still be forwarded as N-Data SCCP Data Form 1),
|
||||
* and we will expect the CN to send an SCCP RLSD soon.
|
||||
* - Under error conditions, cause!=Normal and there's no RANAP message.
|
||||
* In that case, we need to tear down the associated SCCP link towards CN,
|
||||
* which in turn will tear down the upper layer Iu conn.
|
||||
*/
|
||||
if (msg_has_l2_data(ranap_msg)) {
|
||||
/* Forward any payload to SCCP before Disconnect. */
|
||||
handle_rx_rua(fi, ranap_msg);
|
||||
} else {
|
||||
map->rua_fi_ctx.rua_disconnect_err_condition = true;
|
||||
}
|
||||
map_rua_fsm_state_chg(MAP_RUA_ST_DISCONNECTED);
|
||||
handle_rx_rua(fi, ranap_msg);
|
||||
return;
|
||||
|
||||
case MAP_RUA_EV_HNB_LINK_LOST:
|
||||
@@ -315,13 +258,13 @@ static void map_rua_connected_action(struct osmo_fsm_inst *fi, uint32_t event, v
|
||||
}
|
||||
}
|
||||
|
||||
static void map_rua_free_if_done(struct hnbgw_context_map *map, uint32_t sccp_event)
|
||||
static void map_rua_free_if_done(struct hnbgw_context_map *map, uint32_t sccp_event, void *ev_data)
|
||||
{
|
||||
/* From RUA's POV, we can now free the hnbgw_context_map.
|
||||
* If SCCP is still active, tell it to disconnect -- in that case the SCCP side will call context_map_free().
|
||||
* If SCCP is no longer active, free this map. */
|
||||
if (map_sccp_is_active(map))
|
||||
map_sccp_dispatch(map, sccp_event, NULL);
|
||||
map_sccp_dispatch(map, sccp_event, ev_data);
|
||||
else
|
||||
context_map_free(map);
|
||||
}
|
||||
@@ -329,21 +272,38 @@ static void map_rua_free_if_done(struct hnbgw_context_map *map, uint32_t sccp_ev
|
||||
static void map_rua_disconnected_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
map_rua_free_if_done(map, MAP_SCCP_EV_RAN_DISC);
|
||||
map_rua_free_if_done(map, MAP_SCCP_EV_RAN_DISC, (void *)map->rua_fi_ctx.rua_disconnect_err_condition);
|
||||
}
|
||||
|
||||
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_ERROR, "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)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
map_rua_free_if_done(map, MAP_SCCP_EV_RAN_LINK_LOST);
|
||||
map_rua_free_if_done(map, MAP_SCCP_EV_RAN_LINK_LOST, NULL);
|
||||
}
|
||||
|
||||
void map_rua_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
@@ -389,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,18 +27,11 @@
|
||||
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
|
||||
#if ENABLE_PFCP
|
||||
#include <osmocom/pfcp/pfcp_cp_peer.h>
|
||||
#endif
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/hnbgw_ranap.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
#include <osmocom/hnbgw/ps_rab_ass_fsm.h>
|
||||
#include <osmocom/hnbgw/kpi.h>
|
||||
|
||||
enum map_sccp_fsm_state {
|
||||
MAP_SCCP_ST_INIT,
|
||||
@@ -88,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);
|
||||
}
|
||||
@@ -125,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;
|
||||
}
|
||||
@@ -138,65 +129,48 @@ 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);
|
||||
}
|
||||
|
||||
static int destruct_ranap_ran_rx_co_ies(ranap_message *ranap_message_p)
|
||||
{
|
||||
ranap_ran_rx_co_free(ranap_message_p);
|
||||
return 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)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
int rc;
|
||||
|
||||
/* If the FSM instance has already terminated, don't dispatch anything. */
|
||||
if (fi->proc.terminating)
|
||||
@@ -206,70 +180,32 @@ static int handle_rx_sccp(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
if (!msg_has_l2_data(ranap_msg))
|
||||
return 0;
|
||||
|
||||
/* See if it is a RAB Assignment Request message from SCCP to RUA, where we need to change the user plane
|
||||
* information, for RTP mapping via MGW, or GTP mapping via UPF. */
|
||||
ranap_message *message;
|
||||
message = talloc_zero(OTC_SELECT, ranap_message);
|
||||
rc = ranap_ran_rx_co_decode(message, message, msgb_l2(ranap_msg), msgb_l2len(ranap_msg));
|
||||
if (rc == 0) {
|
||||
talloc_set_destructor(message, destruct_ranap_ran_rx_co_ies);
|
||||
return hnbgw_ranap_rx_data_dl(map, ranap_msg);
|
||||
}
|
||||
|
||||
LOGPFSML(fi, LOGL_DEBUG, "rx from SCCP: RANAP %s\n",
|
||||
get_value_string(ranap_procedure_code_vals, message->procedureCode));
|
||||
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);
|
||||
}
|
||||
|
||||
kpi_ranap_process_dl(map, message);
|
||||
|
||||
if (!map->is_ps) {
|
||||
/* Circuit-Switched. Set up mapping of RTP ports via MGW */
|
||||
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* mgw_fsm_alloc_and_handle_rab_ass_req() takes ownership of (ranap) message */
|
||||
return handle_cs_rab_ass_req(map, ranap_msg, message);
|
||||
case RANAP_ProcedureCode_id_Iu_Release:
|
||||
/* Any IU Release will terminate the MGW FSM, the message itsself is not passed to the
|
||||
* FSM code. It is just forwarded normally by map_rua_tx_dt() below. */
|
||||
mgw_fsm_release(map);
|
||||
break;
|
||||
}
|
||||
#if ENABLE_PFCP
|
||||
} else {
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* If a UPF is configured, handle the RAB Assignment via ps_rab_ass_fsm, and replace the
|
||||
* GTP F-TEIDs in the RAB Assignment message before passing it on to RUA. */
|
||||
if (hnb_gw_is_gtp_mapping_enabled()) {
|
||||
LOGP(DMAIN, LOGL_DEBUG,
|
||||
"RAB Assignment: setting up GTP tunnel mapping via UPF %s\n",
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, &g_hnbgw->pfcp.cp_peer->remote_addr));
|
||||
return hnbgw_gtpmap_rx_rab_ass_req(map, ranap_msg, message);
|
||||
}
|
||||
/* If no UPF is configured, directly forward the message as-is (no GTP mapping). */
|
||||
LOGP(DMAIN, LOGL_DEBUG, "RAB Assignment: no UPF configured, forwarding as-is\n");
|
||||
break;
|
||||
|
||||
case RANAP_ProcedureCode_id_Iu_Release:
|
||||
/* Any IU Release will terminate the MGW FSM, the message itsself is not passed to the
|
||||
* FSM code. It is just forwarded normally by map_rua_tx_dt() below. */
|
||||
hnbgw_gtpmap_release(map);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* It was not a RAB Assignment Request that needed to be intercepted. Forward as-is to RUA. */
|
||||
return map_rua_dispatch(map, MAP_RUA_EV_TX_DIRECT_TRANSFER, 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 msgb *ranap_msg = NULL;
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct msgb *ranap_msg = data;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case MAP_SCCP_EV_TX_DATA_REQUEST:
|
||||
ranap_msg = data;
|
||||
/* In the INIT state, the first MAP_SCCP_EV_TX_DATA_REQUEST will be the RANAP message received from the
|
||||
* RUA Connect message. Send the SCCP CR and transition to WAIT_CC. */
|
||||
if (tx_sccp_cr(fi, ranap_msg) == 0)
|
||||
@@ -277,18 +213,22 @@ static void map_sccp_init_action(struct osmo_fsm_inst *fi, uint32_t event, void
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RAN_LINK_LOST:
|
||||
case MAP_SCCP_EV_RAN_DISC:
|
||||
case MAP_SCCP_EV_USER_ABORT:
|
||||
case MAP_SCCP_EV_CN_LINK_LOST:
|
||||
/* 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;
|
||||
|
||||
case MAP_SCCP_EV_RAN_DISC:
|
||||
/* bool rua_disconnect_err_condition = !!data; */
|
||||
/* 3GPP TS 25.468 9.1.5: RUA has disconnected.
|
||||
* In this state we didn't send an SCCP CR yet, so nothing to be torn down on CN side. */
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
@@ -300,33 +240,41 @@ static void map_sccp_init_action(struct osmo_fsm_inst *fi, uint32_t event, void
|
||||
static void map_sccp_wait_cc_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct msgb *ranap_msg = data;
|
||||
struct msgb *ranap_msg = NULL;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case MAP_SCCP_EV_RX_CONNECTION_CONFIRM:
|
||||
ranap_msg = data;
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_CONNECTED);
|
||||
/* Usually doesn't but if the SCCP CC contained data, forward it to RUA */
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_TX_DATA_REQUEST:
|
||||
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_RAN_DISC:
|
||||
case MAP_SCCP_EV_USER_ABORT:
|
||||
case MAP_SCCP_EV_CN_LINK_LOST:
|
||||
/* 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;
|
||||
|
||||
case MAP_SCCP_EV_RAN_DISC:
|
||||
/* bool rua_disconnect_err_condition = !!data; */
|
||||
/* 3GPP TS 25.468 9.1.5: RUA has disconnected.
|
||||
* In this state we didn't send an SCCP CR yet, so nothing to be torn down on CN side. */
|
||||
map->please_disconnect = true;
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RX_RELEASED:
|
||||
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;
|
||||
@@ -339,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);
|
||||
}
|
||||
@@ -349,26 +303,41 @@ 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 msgb *ranap_msg = data;
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct msgb *ranap_msg = NULL;
|
||||
bool rua_disconnect_err_condition;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case MAP_SCCP_EV_RX_DATA_INDICATION:
|
||||
ranap_msg = data;
|
||||
/* forward RANAP from SCCP to RUA */
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_TX_DATA_REQUEST:
|
||||
ranap_msg = data;
|
||||
/* Someone (usually the RUA side) wants us to send a RANAP payload to CN via SCCP */
|
||||
tx_sccp_df1(fi, ranap_msg);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RAN_DISC:
|
||||
/* RUA has disconnected, and usually has sent an Iu-ReleaseComplete along with its RUA Disconnect. On
|
||||
* SCCP, the Iu-ReleaseComplete should still be forwarded as N-Data (SCCP Data Form 1), and we will
|
||||
* expect the CN to send an SCCP RLSD soon. */
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_WAIT_RLSD);
|
||||
tx_sccp_df1(fi, ranap_msg);
|
||||
rua_disconnect_err_condition = !!data;
|
||||
/* 3GPP TS 25.468 9.1.5: RUA has disconnected.
|
||||
* - Under normal conditions (cause=Normal) the RUA Disconnect
|
||||
* contained a RANAP Iu-ReleaseComplete which we already
|
||||
* handled here through MAP_SCCP_EV_TX_DATA_REQUEST.
|
||||
* On SCCP, We will expect the CN to send an SCCP RLSD soon.
|
||||
* - Under error conditions, cause!=Normal and there was no RANAP message.
|
||||
* In that case, we need to tear down the associated SCCP link towards CN,
|
||||
* which in turn will tear down the upper layer Iu conn.
|
||||
*/
|
||||
if (rua_disconnect_err_condition) {
|
||||
tx_sccp_rlsd(fi);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
} else {
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_WAIT_RLSD);
|
||||
}
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RAN_LINK_LOST:
|
||||
@@ -378,23 +347,20 @@ static void map_sccp_connected_action(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
/* The user is asking for disconnection, so there is no Iu Release in progress. Disconnect now. */
|
||||
case MAP_SCCP_EV_CN_LINK_LOST:
|
||||
/* 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:
|
||||
/* 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");
|
||||
ranap_msg = data;
|
||||
/* 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;
|
||||
|
||||
case MAP_SCCP_EV_RX_CONNECTION_CONFIRM:
|
||||
ranap_msg = data;
|
||||
/* Already connected. Unusual, but if there is data just forward it. */
|
||||
LOGPFSML(fi, LOGL_ERROR, "Already connected, but received SCCP CC again\n");
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
@@ -415,11 +381,12 @@ static void map_sccp_wait_rlsd_onenter(struct osmo_fsm_inst *fi, uint32_t prev_s
|
||||
|
||||
static void map_sccp_wait_rlsd_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct msgb *ranap_msg = data;
|
||||
struct msgb *ranap_msg = NULL;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case MAP_SCCP_EV_RX_RELEASED:
|
||||
ranap_msg = data;
|
||||
/* The CN sends the expected SCCP RLSD.
|
||||
* Usually there is no data, but if there is just forward it.
|
||||
* Usually RUA is already disconnected, but let the RUA FSM decide about that. */
|
||||
@@ -428,12 +395,13 @@ static void map_sccp_wait_rlsd_action(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RX_DATA_INDICATION:
|
||||
ranap_msg = data;
|
||||
/* RUA is probably already disconnected, but let the RUA FSM decide about that. */
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_TX_DATA_REQUEST:
|
||||
case MAP_SCCP_EV_RAN_DISC:
|
||||
ranap_msg = data;
|
||||
/* Normally, RUA would already disconnected, but since SCCP is officially still connected, we can still
|
||||
* forward messages there. Already waiting for CN to send the SCCP RLSD. If there is a message, forward
|
||||
* it, and just continue to time out on the SCCP RLSD. */
|
||||
@@ -441,6 +409,7 @@ static void map_sccp_wait_rlsd_action(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RX_CONNECTION_CONFIRM:
|
||||
ranap_msg = data;
|
||||
/* Already connected. Unusual, but if there is data just forward it. */
|
||||
LOGPFSML(fi, LOGL_ERROR, "Already connected, but received SCCP CC\n");
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
@@ -449,6 +418,7 @@ static void map_sccp_wait_rlsd_action(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
case MAP_SCCP_EV_RAN_LINK_LOST:
|
||||
case MAP_SCCP_EV_USER_ABORT:
|
||||
case MAP_SCCP_EV_CN_LINK_LOST:
|
||||
case MAP_SCCP_EV_RAN_DISC:
|
||||
/* Stop waiting for RLSD, send RLSD now. */
|
||||
tx_sccp_rlsd(fi);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
@@ -513,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
|
||||
@@ -19,6 +19,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/sctp.h>
|
||||
|
||||
@@ -38,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>
|
||||
@@ -54,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)
|
||||
@@ -89,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
|
||||
***********************************************************************/
|
||||
@@ -99,763 +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%hu-R%hu-S%hu-C%u", &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;
|
||||
@@ -975,6 +213,7 @@ void g_hnbgw_alloc(void *ctx)
|
||||
/* strdup so we can easily talloc_free in the VTY code */
|
||||
g_hnbgw->config.iuh_local_ip = talloc_strdup(g_hnbgw, HNBGW_LOCAL_IP_DEFAULT);
|
||||
g_hnbgw->config.iuh_local_port = IUH_DEFAULT_SCTP_PORT;
|
||||
g_hnbgw->config.hnbap_allow_tmsi = true;
|
||||
g_hnbgw->config.log_prefix_hnb_id = true;
|
||||
g_hnbgw->config.accept_all_hnb = true;
|
||||
|
||||
@@ -996,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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/* hnb-gw specific code for HNBAP */
|
||||
/* hnb-gw specific code for HNBAP, 3GPP TS 25.469 */
|
||||
|
||||
/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
@@ -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>
|
||||
|
||||
@@ -341,7 +343,7 @@ static int hnbgw_tx_ue_register_rej(struct hnb_context *hnb, HNBAP_UE_Identity_t
|
||||
return hnbgw_hnbap_tx(hnb, msg);
|
||||
}
|
||||
|
||||
static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Identity_t *ue_id)
|
||||
static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Identity_t *ue_id, uint32_t context_id)
|
||||
{
|
||||
HNBAP_UERegisterAccept_t accept_out;
|
||||
HNBAP_UERegisterAcceptIEs_t accept;
|
||||
@@ -393,7 +395,7 @@ static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Ident
|
||||
tmsi = ntohl(tmsi);
|
||||
LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "HNBAP register with TMSI %x\n", tmsi);
|
||||
|
||||
asn1_u24_to_bitstring(&accept.context_ID, &ctx_id, get_next_ue_ctx_id());
|
||||
asn1_u24_to_bitstring(&accept.context_ID, &ctx_id, context_id);
|
||||
|
||||
memset(&accept_out, 0, sizeof(accept_out));
|
||||
rc = hnbap_encode_ueregisteraccepties(&accept_out, &accept);
|
||||
@@ -446,7 +448,7 @@ static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Ident
|
||||
static int hnbgw_rx_hnb_deregister(struct hnb_context *ctx, ANY_t *in)
|
||||
{
|
||||
HNBAP_HNBDe_RegisterIEs_t ies;
|
||||
HNBAP_Cause_t cause;
|
||||
HNBAP_Cause_t cause = {};
|
||||
int rc;
|
||||
|
||||
rc = hnbap_decode_hnbde_registeries(&ies, in);
|
||||
@@ -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;
|
||||
@@ -475,12 +487,18 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
char identity_str[256];
|
||||
const char *cell_id_str;
|
||||
struct timespec tp;
|
||||
HNBAP_Cause_t cause;
|
||||
HNBAP_Cause_t cause = {};
|
||||
struct osmo_sockaddr cur_osa = {};
|
||||
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;
|
||||
@@ -579,7 +597,7 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
static int hnbgw_rx_ue_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
{
|
||||
HNBAP_UERegisterRequestIEs_t ies;
|
||||
HNBAP_Cause_t cause;
|
||||
HNBAP_Cause_t cause = {};
|
||||
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
|
||||
int rc;
|
||||
|
||||
@@ -616,7 +634,7 @@ static int hnbgw_rx_ue_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
case HNBAP_UE_Identity_PR_tMSILAI:
|
||||
case HNBAP_UE_Identity_PR_pTMSIRAI:
|
||||
if (g_hnbgw->config.hnbap_allow_tmsi) {
|
||||
rc = hnbgw_tx_ue_register_acc_tmsi(ctx, &ies.uE_Identity);
|
||||
rc = hnbgw_tx_ue_register_acc_tmsi(ctx, &ies.uE_Identity, get_next_ue_ctx_id());
|
||||
} else {
|
||||
cause.present = HNBAP_Cause_PR_radioNetwork;
|
||||
cause.choice.radioNetwork = HNBAP_CauseRadioNetwork_invalid_UE_identity;
|
||||
@@ -648,7 +666,7 @@ free_and_return_rc:
|
||||
static int hnbgw_rx_ue_deregister(struct hnb_context *ctx, ANY_t *in)
|
||||
{
|
||||
HNBAP_UEDe_RegisterIEs_t ies;
|
||||
HNBAP_Cause_t cause;
|
||||
HNBAP_Cause_t cause = {};
|
||||
int rc;
|
||||
uint32_t ctxid;
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/hnbgw/hnbgw_ranap.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
|
||||
@@ -50,10 +50,8 @@ static const struct tlv_definition gsm48_gmm_att_tlvdef = {
|
||||
},
|
||||
};
|
||||
|
||||
static void decode_gmm_tlv(struct osmo_mobile_identity *mi,
|
||||
struct osmo_routing_area_id *old_ra,
|
||||
int *nri,
|
||||
const uint8_t *tlv_data, size_t tlv_len, bool allow_hex)
|
||||
static void decode_gmm_tlv(struct osmo_mobile_identity *mi, int *nri,
|
||||
const uint8_t *tlv_data, size_t tlv_len, bool allow_hex)
|
||||
{
|
||||
struct tlv_parsed tp;
|
||||
struct tlv_p_entry *e;
|
||||
@@ -66,8 +64,14 @@ static void decode_gmm_tlv(struct osmo_mobile_identity *mi,
|
||||
*nri <<= 2;
|
||||
*nri |= e->val[1] >> 6;
|
||||
}
|
||||
|
||||
/* RAU Req: 9.4.14.5 P-TMSI (Iu mode only): "This IE shall be included by the MS." */
|
||||
e = TLVP_GET(&tp, GSM48_IE_GMM_ALLOC_PTMSI);
|
||||
if (mi && e)
|
||||
osmo_mobile_identity_decode(mi, e->val, e->len, allow_hex);
|
||||
}
|
||||
|
||||
/* Parse 3GPP TS 24.008 § 9.4.1 Attach request */
|
||||
static int mobile_identity_decode_from_gmm_att_req(struct osmo_mobile_identity *mi,
|
||||
struct osmo_routing_area_id *old_ra,
|
||||
int *nri,
|
||||
@@ -82,11 +86,16 @@ static int mobile_identity_decode_from_gmm_att_req(struct osmo_mobile_identity *
|
||||
uint8_t ms_ra_acc_cap_len;
|
||||
int rc;
|
||||
|
||||
if (l3_len < 26)
|
||||
return -ENOSPC;
|
||||
|
||||
/* MS network capability 10.5.5.12 */
|
||||
msnc_len = *cur++;
|
||||
if (l3_len < (msnc_len + (cur - l3_data)))
|
||||
return -ENOSPC;
|
||||
cur += msnc_len;
|
||||
|
||||
/* aTTACH Type 10.5.5.2 */
|
||||
/* aTTACH Type 10.5.5.2 + Ciphering key sequence number 10.5.1.2 */
|
||||
cur++;
|
||||
|
||||
/* DRX parameter 10.5.5.6 */
|
||||
@@ -95,10 +104,9 @@ static int mobile_identity_decode_from_gmm_att_req(struct osmo_mobile_identity *
|
||||
/* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */
|
||||
mi_len = *cur++;
|
||||
mi_data = cur;
|
||||
cur += mi_len;
|
||||
|
||||
if (cur >= end)
|
||||
if (l3_len < (mi_len + (cur - l3_data)))
|
||||
return -ENOSPC;
|
||||
cur += mi_len;
|
||||
|
||||
rc = osmo_mobile_identity_decode(mi, mi_data, mi_len, allow_hex);
|
||||
if (rc)
|
||||
@@ -112,15 +120,19 @@ static int mobile_identity_decode_from_gmm_att_req(struct osmo_mobile_identity *
|
||||
|
||||
/* MS Radio Access Capability 10.5.5.12a */
|
||||
ms_ra_acc_cap_len = *cur++;
|
||||
if (l3_len < (ms_ra_acc_cap_len + (cur - l3_data)))
|
||||
return -ENOSPC;
|
||||
cur += ms_ra_acc_cap_len;
|
||||
|
||||
if (cur > end)
|
||||
return -ENOSPC;
|
||||
if (l3_len == (cur - l3_data))
|
||||
return 0; /* No Optional TLV section */
|
||||
|
||||
decode_gmm_tlv(mi, old_ra, nri, cur, end - cur, allow_hex);
|
||||
/* Mobile identity = NULL: already obtained from Mandatory IE above.*/
|
||||
decode_gmm_tlv(NULL, nri, cur, end - cur, allow_hex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse 24.008 9.4.14 RAU Request */
|
||||
static int mobile_identity_decode_from_gmm_rau_req(struct osmo_mobile_identity *mi,
|
||||
struct osmo_routing_area_id *old_ra,
|
||||
int *nri,
|
||||
@@ -132,12 +144,15 @@ static int mobile_identity_decode_from_gmm_rau_req(struct osmo_mobile_identity *
|
||||
uint8_t ms_ra_acc_cap_len;
|
||||
int rc;
|
||||
|
||||
/* Update Type 10.5.5.18 */
|
||||
cur++;
|
||||
if (cur >= end)
|
||||
/* all mandatory fields + variable length MS Radio Cap (min value) would be 15 bytes.
|
||||
* But even short radio capabilities we should handle with 14 bytes */
|
||||
if (l3_len < 14)
|
||||
return -ENOSPC;
|
||||
|
||||
/* Old routing area identification 10.5.5.15 */
|
||||
/* V: Update Type 10.5.5.18 */
|
||||
cur++;
|
||||
|
||||
/* V: Old routing area identification 10.5.5.15 */
|
||||
rc = osmo_routing_area_id_decode(old_ra, cur, end - cur);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
@@ -145,71 +160,46 @@ static int mobile_identity_decode_from_gmm_rau_req(struct osmo_mobile_identity *
|
||||
if (cur >= end)
|
||||
return -ENOSPC;
|
||||
|
||||
/* MS Radio Access Capability 10.5.5.12a */
|
||||
/* LV: MS Radio Access Capability 10.5.5.12a */
|
||||
ms_ra_acc_cap_len = *cur++;
|
||||
if (l3_len < (ms_ra_acc_cap_len + (cur - l3_data)))
|
||||
return -ENOSPC;
|
||||
cur += ms_ra_acc_cap_len;
|
||||
|
||||
if (cur > end)
|
||||
return -ENOSPC;
|
||||
|
||||
decode_gmm_tlv(mi, old_ra, nri, cur, end - cur, allow_hex);
|
||||
if (l3_len == (cur - l3_data))
|
||||
return 0; /* No Optional TLV section */
|
||||
decode_gmm_tlv(mi, nri, cur, end - cur, allow_hex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int peek_l3_ul_nas(struct hnbgw_context_map *map, const uint8_t *nas_pdu, size_t len,
|
||||
const struct osmo_plmn_id *local_plmn)
|
||||
/* CS MM: Determine mobile identity, from_other_plmn, is_emerg. */
|
||||
static int peek_l3_ul_nas_cs(struct hnbgw_context_map *map, const uint8_t *nas_pdu, size_t len,
|
||||
const struct osmo_plmn_id *local_plmn)
|
||||
{
|
||||
const struct gsm48_hdr *gh;
|
||||
int8_t pdisc;
|
||||
uint8_t mtype;
|
||||
const struct gsm48_hdr *gh = (const struct gsm48_hdr *)nas_pdu;
|
||||
struct osmo_location_area_id old_lai;
|
||||
const struct gsm48_loc_upd_req *lu;
|
||||
struct gsm48_service_request *cm;
|
||||
struct osmo_location_area_id old_lai;
|
||||
struct osmo_routing_area_id old_ra = {};
|
||||
int nri = -1;
|
||||
|
||||
map->l3 = (struct hnbgw_l3_peek){
|
||||
.gmm_nri_container = -1,
|
||||
};
|
||||
osmo_mobile_identity_decode_from_l3_buf(&map->l3.mi, nas_pdu, len, false);
|
||||
|
||||
/* Get the mobile identity from CS MM -- the PS GMM happens further down.
|
||||
* This will return an error for GMM messages, ignore that. */
|
||||
if (!map->is_ps)
|
||||
osmo_mobile_identity_decode_from_l3_buf(&map->l3.mi, nas_pdu, len, false);
|
||||
|
||||
/* Get is_emerg and from_other_plmn */
|
||||
if (len < sizeof(*gh)) {
|
||||
LOGP(DCN, LOGL_ERROR, "Layer 3 message too short for header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gh = (void *)nas_pdu;
|
||||
pdisc = gsm48_hdr_pdisc(gh);
|
||||
mtype = gsm48_hdr_msg_type(gh);
|
||||
|
||||
map->l3.gsm48_pdisc = pdisc;
|
||||
map->l3.gsm48_msg_type = mtype;
|
||||
|
||||
/* Determine from_other_plmn and is_emerg */
|
||||
switch (pdisc) {
|
||||
switch (map->l3.gsm48_pdisc) {
|
||||
case GSM48_PDISC_MM:
|
||||
|
||||
switch (mtype) {
|
||||
/* Get is_emerg and from_other_plmn */
|
||||
switch (map->l3.gsm48_msg_type) {
|
||||
case GSM48_MT_MM_LOC_UPD_REQUEST:
|
||||
if (len < sizeof(*gh) + sizeof(*lu)) {
|
||||
LOGP(DCN, LOGL_ERROR, "LU Req message too short\n");
|
||||
break;
|
||||
}
|
||||
|
||||
lu = (struct gsm48_loc_upd_req *)gh->data;
|
||||
gsm48_decode_lai2(&lu->lai, &old_lai);
|
||||
|
||||
map->l3.from_other_plmn = (osmo_plmn_cmp(&old_lai.plmn, local_plmn) != 0);
|
||||
if (map->l3.from_other_plmn)
|
||||
LOGP(DRUA, LOGL_INFO, "LU from other PLMN: old LAI=%s my PLMN=%s\n",
|
||||
osmo_plmn_name_c(OTC_SELECT, &old_lai.plmn),
|
||||
osmo_plmn_name_c(OTC_SELECT, local_plmn));
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case GSM48_MT_MM_CM_SERV_REQ:
|
||||
if (len < sizeof(*gh) + sizeof(*cm)) {
|
||||
@@ -219,64 +209,88 @@ static int peek_l3_ul_nas(struct hnbgw_context_map *map, const uint8_t *nas_pdu,
|
||||
cm = (struct gsm48_service_request *)&gh->data[0];
|
||||
map->l3.is_emerg = (cm->cm_service_type == GSM48_CMSERV_EMERGENCY);
|
||||
LOGP(DRUA, LOGL_DEBUG, "CM Service is_emerg=%d\n", map->l3.is_emerg);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* PS GMM: Determine mobile identity, gmm_nri_container, from_other_plmn and is_emerg */
|
||||
static int peek_l3_ul_nas_ps(struct hnbgw_context_map *map, const uint8_t *nas_pdu, size_t len,
|
||||
const struct osmo_plmn_id *local_plmn)
|
||||
{
|
||||
struct osmo_routing_area_id old_ra = {};
|
||||
int nri = -1;
|
||||
|
||||
switch (map->l3.gsm48_pdisc) {
|
||||
case GSM48_PDISC_MM_GPRS:
|
||||
switch (mtype) {
|
||||
switch (map->l3.gsm48_msg_type) {
|
||||
case GSM48_MT_GMM_ATTACH_REQ:
|
||||
mobile_identity_decode_from_gmm_att_req(&map->l3.mi, &old_ra, &nri, nas_pdu, len, false);
|
||||
|
||||
LOGP(DRUA, LOGL_DEBUG, "GMM Attach Req mi=%s old_ra=%s nri:%d=0x%x\n",
|
||||
osmo_mobile_identity_to_str_c(OTC_SELECT, &map->l3.mi),
|
||||
osmo_rai_name2_c(OTC_SELECT, &old_ra),
|
||||
nri, nri);
|
||||
|
||||
if (old_ra.lac.plmn.mcc && osmo_plmn_cmp(&old_ra.lac.plmn, local_plmn)) {
|
||||
map->l3.from_other_plmn = true;
|
||||
LOGP(DRUA, LOGL_INFO, "GMM Attach Req from other PLMN: old RAI=%s my PLMN=%s\n",
|
||||
osmo_rai_name2_c(OTC_SELECT, &old_ra),
|
||||
osmo_plmn_name_c(OTC_SELECT, local_plmn));
|
||||
}
|
||||
|
||||
if (nri >= 0)
|
||||
map->l3.gmm_nri_container = nri;
|
||||
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case GSM48_MT_GMM_RA_UPD_REQ:
|
||||
mobile_identity_decode_from_gmm_rau_req(&map->l3.mi, &old_ra, &nri, nas_pdu, len, false);
|
||||
|
||||
LOGP(DRUA, LOGL_DEBUG, "GMM Routing Area Upd Req mi=%s old_ra=%s nri:%d=0x%x\n",
|
||||
osmo_mobile_identity_to_str_c(OTC_SELECT, &map->l3.mi),
|
||||
osmo_rai_name2_c(OTC_SELECT, &old_ra),
|
||||
nri, nri);
|
||||
|
||||
if (old_ra.lac.plmn.mcc && osmo_plmn_cmp(&old_ra.lac.plmn, local_plmn)) {
|
||||
map->l3.from_other_plmn = true;
|
||||
LOGP(DRUA, LOGL_INFO, "GMM Routing Area Upd Req from other PLMN: old RAI=%s my PLMN=%s\n",
|
||||
osmo_rai_name2_c(OTC_SELECT, &old_ra),
|
||||
osmo_plmn_name_c(OTC_SELECT, local_plmn));
|
||||
}
|
||||
|
||||
if (nri >= 0)
|
||||
map->l3.gmm_nri_container = nri;
|
||||
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int peek_l3_ul_nas(struct hnbgw_context_map *map, const uint8_t *nas_pdu, size_t len,
|
||||
const struct osmo_plmn_id *local_plmn)
|
||||
{
|
||||
const struct gsm48_hdr *gh = (const struct gsm48_hdr *)nas_pdu;
|
||||
|
||||
map->l3 = (struct hnbgw_l3_peek){
|
||||
.gmm_nri_container = -1,
|
||||
.mi = {
|
||||
.type = GSM_MI_TYPE_NONE,
|
||||
.tmsi = GSM_RESERVED_TMSI,
|
||||
},
|
||||
};
|
||||
|
||||
if (len < sizeof(*gh)) {
|
||||
LOGP(DCN, LOGL_ERROR, "Layer 3 message too short for header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
map->l3.gsm48_pdisc = gsm48_hdr_pdisc(gh);
|
||||
map->l3.gsm48_msg_type = gsm48_hdr_msg_type(gh);
|
||||
|
||||
if (map->is_ps)
|
||||
return peek_l3_ul_nas_ps(map, nas_pdu, len, local_plmn);
|
||||
return peek_l3_ul_nas_cs(map, nas_pdu, len, local_plmn);
|
||||
}
|
||||
|
||||
static int peek_l3_ul_initial_ue(struct hnbgw_context_map *map, const RANAP_InitialUE_MessageIEs_t *ies)
|
||||
{
|
||||
struct osmo_plmn_id local_plmn;
|
||||
@@ -300,7 +314,7 @@ static int peek_l3_ul_initial_ue(struct hnbgw_context_map *map, const RANAP_Init
|
||||
* This is relevant for CN pooling, to decide which CN link to map the RUA context to. */
|
||||
int hnbgw_peek_l3_ul(struct hnbgw_context_map *map, struct msgb *ranap_msg)
|
||||
{
|
||||
ranap_message *message = hnbgw_decode_ranap_co(ranap_msg);
|
||||
ranap_message *message = hnbgw_decode_ranap_cn_co(ranap_msg);
|
||||
if (!message) {
|
||||
LOGP(DCN, LOGL_ERROR, "Failed to decode RANAP PDU\n");
|
||||
return -EINVAL;
|
||||
|
||||
@@ -20,12 +20,27 @@
|
||||
*/
|
||||
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
#include <osmocom/pfcp/pfcp_endpoint.h>
|
||||
#include <osmocom/pfcp/pfcp_cp_peer.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/ps_rab_fsm.h>
|
||||
#include <osmocom/hnbgw/hnbgw_pfcp.h>
|
||||
|
||||
static const struct osmo_stat_item_desc hnbgw_upf_stat_item_description[] = {
|
||||
[HNBGW_UPF_STAT_ASSOCIATED] = { "pfcp_associated", "Associated to UPF through PFCP", OSMO_STAT_ITEM_NO_UNIT, 16, 0},
|
||||
};
|
||||
|
||||
static const struct osmo_stat_item_group_desc hnbgw_upf_statg_desc = {
|
||||
"upf",
|
||||
"UPF Peer Statistics",
|
||||
OSMO_STATS_CLASS_PEER,
|
||||
ARRAY_SIZE(hnbgw_upf_stat_item_description),
|
||||
hnbgw_upf_stat_item_description,
|
||||
};
|
||||
|
||||
static void pfcp_set_msg_ctx(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m, struct osmo_pfcp_msg *req)
|
||||
{
|
||||
@@ -62,6 +77,12 @@ static void pfcp_rx_msg(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m,
|
||||
}
|
||||
}
|
||||
|
||||
static void pfcp_cp_peer_assoc_cb(struct osmo_pfcp_cp_peer *cp_peer, bool associated)
|
||||
{
|
||||
LOGP(DLPFCP, LOGL_NOTICE, "PFCP Peer associated: %s\n", associated ? "true" : "false");
|
||||
HNBGW_UPF_STAT_SET(HNBGW_UPF_STAT_ASSOCIATED, associated ? 1 : 0);
|
||||
}
|
||||
|
||||
int hnbgw_pfcp_init(void)
|
||||
{
|
||||
struct osmo_pfcp_endpoint_cfg cfg;
|
||||
@@ -81,6 +102,12 @@ int hnbgw_pfcp_init(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_hnbgw->pfcp.statg = osmo_stat_item_group_alloc(g_hnbgw, &hnbgw_upf_statg_desc, 0);
|
||||
if (!g_hnbgw->pfcp.statg) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Failed creating UPF stats item group\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfg = (struct osmo_pfcp_endpoint_cfg){
|
||||
.set_msg_ctx_cb = pfcp_set_msg_ctx,
|
||||
.rx_msg_cb = pfcp_rx_msg,
|
||||
@@ -136,6 +163,11 @@ int hnbgw_pfcp_init(void)
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Cannot allocate PFCP CP Peer FSM\n");
|
||||
return -1;
|
||||
}
|
||||
if (osmo_pfcp_cp_peer_set_associated_cb(g_hnbgw->pfcp.cp_peer, pfcp_cp_peer_assoc_cb)) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Cannot Set PFCP CP Peer associated callback\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (osmo_pfcp_cp_peer_associate(g_hnbgw->pfcp.cp_peer)) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Cannot start PFCP CP Peer FSM\n");
|
||||
return -1;
|
||||
@@ -143,3 +175,10 @@ int hnbgw_pfcp_init(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hnbgw_pfcp_release(void)
|
||||
{
|
||||
if (!hnb_gw_is_gtp_mapping_enabled())
|
||||
return;
|
||||
osmo_stat_item_group_free(g_hnbgw->pfcp.statg);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* hnb-gw specific code for RANAP */
|
||||
/* hnb-gw specific code for RANAP, 3GPP TS 25.413 */
|
||||
|
||||
/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
@@ -18,10 +18,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include "config.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
@@ -29,12 +26,33 @@
|
||||
|
||||
#include "asn1helpers.h"
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
#include <osmocom/ranap/ranap_common_cn.h>
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/ranap/ranap_msg_factory.h>
|
||||
|
||||
#if ENABLE_PFCP
|
||||
#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>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
#include <osmocom/hnbgw/ps_rab_ass_fsm.h>
|
||||
#include <osmocom/hnbgw/kpi.h>
|
||||
|
||||
/*****************************************************************************
|
||||
* Processing of RANAP from the endpoint towards RAN (hNodeB), acting as CN
|
||||
*****************************************************************************/
|
||||
|
||||
static int ranap_tx_udt_dl_reset_ack(struct hnb_context *hnb, RANAP_CN_DomainIndicator_t domain)
|
||||
{
|
||||
struct msgb *msg;
|
||||
@@ -209,6 +227,455 @@ int hnbgw_ranap_rx_udt_ul(struct msgb *msg, uint8_t *data, size_t len)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int destruct_ranap_cn_rx_co_ies(ranap_message *ranap_message_p)
|
||||
{
|
||||
ranap_cn_rx_co_free(ranap_message_p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decode UL RANAP message with convenient memory freeing: just talloc_free() the returned pointer..
|
||||
* Allocate a ranap_message from OTC_SELECT, decode RANAP msgb into it, attach a talloc destructor that calls
|
||||
* ranap_cn_rx_co_free() upon talloc_free(), and return the decoded ranap_message. */
|
||||
ranap_message *hnbgw_decode_ranap_cn_co(struct msgb *ranap_msg)
|
||||
{
|
||||
int rc;
|
||||
ranap_message *message;
|
||||
|
||||
if (!msg_has_l2_data(ranap_msg))
|
||||
return NULL;
|
||||
message = talloc_zero(OTC_SELECT, ranap_message);
|
||||
rc = ranap_cn_rx_co_decode2(message, msgb_l2(ranap_msg), msgb_l2len(ranap_msg));
|
||||
if (rc != 0) {
|
||||
talloc_free(message);
|
||||
return NULL;
|
||||
}
|
||||
talloc_set_destructor(message, destruct_ranap_cn_rx_co_ies);
|
||||
return message;
|
||||
}
|
||||
|
||||
/* Process a received RANAP PDU through SCCP DATA.ind coming from CN (MSC/SGSN)
|
||||
* ranap_msg is owned by OTC_SELECT. */
|
||||
int hnbgw_ranap_rx_data_ul(struct hnbgw_context_map *map, struct msgb *ranap_msg)
|
||||
{
|
||||
OSMO_ASSERT(map);
|
||||
OSMO_ASSERT(msg_has_l2_data(ranap_msg));
|
||||
|
||||
ranap_message *message = hnbgw_decode_ranap_cn_co(ranap_msg);
|
||||
if (message) {
|
||||
LOG_MAP(map, DHNB, LOGL_DEBUG, "rx from RUA: RANAP %s\n",
|
||||
get_value_string(ranap_procedure_code_vals, message->procedureCode));
|
||||
|
||||
kpi_ranap_process_ul(map, message);
|
||||
|
||||
if (!map->is_ps) {
|
||||
/* See if it is a RAB Assignment Response message from RUA to SCCP, where we need to change the user plane
|
||||
* information, for RTP mapping via MGW, or GTP mapping via UPF. */
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* mgw_fsm_handle_rab_ass_resp() may take ownership of "ranap_msg" (prim->oph) and "message" */
|
||||
return mgw_fsm_handle_cs_rab_ass_resp(map, ranap_msg, message);
|
||||
}
|
||||
} else {
|
||||
#if ENABLE_PFCP
|
||||
if (hnb_gw_is_gtp_mapping_enabled()) {
|
||||
/* map->is_ps == true and PFCP is enabled in osmo-hnbgw.cfg */
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* ps_rab_ass_fsm() may take ownership of "ranap_msg" (prim->oph) and "message" */
|
||||
return hnbgw_gtpmap_rx_rab_ass_resp(map, ranap_msg, message);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* It was not a RAB Assignment Response that needed to be intercepted. Forward as-is to SCCP. */
|
||||
return map_sccp_dispatch(map, MAP_SCCP_EV_TX_DATA_REQUEST, ranap_msg);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Processing of RANAP from the endpoint towards CN (MSC/SGSN), acting as RAN
|
||||
*****************************************************************************/
|
||||
|
||||
static int cn_ranap_rx_reset_cmd(struct hnbgw_cnlink *cnlink,
|
||||
const struct osmo_scu_unitdata_param *unitdata,
|
||||
RANAP_InitiatingMessage_t *imsg)
|
||||
{
|
||||
RANAP_CN_DomainIndicator_t domain;
|
||||
RANAP_ResetIEs_t ies;
|
||||
int rc;
|
||||
|
||||
rc = ranap_decode_reseties(&ies, &imsg->value);
|
||||
domain = ies.cN_DomainIndicator;
|
||||
ranap_free_reseties(&ies);
|
||||
|
||||
if (rc) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Rx RESET: cannot decode IEs\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cnlink->pool->domain != domain) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Rx RESET indicates domain %s, but this is %s on domain %s\n",
|
||||
ranap_domain_name(domain), cnlink->name, ranap_domain_name(cnlink->pool->domain));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cnlink_rx_reset_cmd(cnlink);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct value_string ranap_paging_area_id_names[] = {
|
||||
{ RANAP_PagingAreaID_PR_NOTHING, "NOTHING" },
|
||||
{ RANAP_PagingAreaID_PR_lAI, "LAI" },
|
||||
{ RANAP_PagingAreaID_PR_rAI, "RAI" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static bool hnb_paging_area_id_match(const struct hnb_context *hnb,
|
||||
enum RANAP_PagingAreaID_PR t,
|
||||
const struct osmo_routing_area_id *rai)
|
||||
{
|
||||
switch (t) {
|
||||
case RANAP_PagingAreaID_PR_NOTHING:
|
||||
return true;
|
||||
case RANAP_PagingAreaID_PR_rAI:
|
||||
if (hnb->id.rac != rai->rac)
|
||||
return false;
|
||||
/* fall through */
|
||||
case RANAP_PagingAreaID_PR_lAI:
|
||||
if (hnb->id.lac != rai->lac.lac)
|
||||
return false;
|
||||
if (osmo_plmn_cmp(&hnb->id.plmn, &rai->lac.plmn))
|
||||
return false;
|
||||
/* fall through */
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int lai_from_RANAP_RANAP_LAI(struct osmo_location_area_id *lai, const RANAP_LAI_t *ranap_lai)
|
||||
{
|
||||
if (ranap_lai->pLMNidentity.size < 3)
|
||||
return -EINVAL;
|
||||
osmo_plmn_from_bcd(ranap_lai->pLMNidentity.buf, &lai->plmn);
|
||||
lai->lac = asn1str_to_u16(&ranap_lai->lAC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rai_from_RANAP_PagingAreaID(struct osmo_routing_area_id *rai, const RANAP_PagingAreaID_t *paid)
|
||||
{
|
||||
switch (paid->present) {
|
||||
case RANAP_PagingAreaID_PR_NOTHING:
|
||||
break;
|
||||
case RANAP_PagingAreaID_PR_lAI:
|
||||
return lai_from_RANAP_RANAP_LAI(&rai->lac, &paid->choice.lAI);
|
||||
case RANAP_PagingAreaID_PR_rAI:
|
||||
rai->rac = asn1str_to_u8(&paid->choice.rAI.rAC);
|
||||
return lai_from_RANAP_RANAP_LAI(&rai->lac, &paid->choice.rAI.lAI);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 3GPP TS 25.413 8.15 */
|
||||
static int cn_ranap_rx_paging_cmd(struct hnbgw_cnlink *cnlink,
|
||||
RANAP_InitiatingMessage_t *imsg,
|
||||
const uint8_t *data, unsigned int len)
|
||||
{
|
||||
RANAP_PagingIEs_t ies;
|
||||
RANAP_CN_DomainIndicator_t domain;
|
||||
const char *errmsg;
|
||||
struct hnb_context *hnb;
|
||||
bool is_ps = cnlink->pool->domain == DOMAIN_PS;
|
||||
bool forwarded = false;
|
||||
bool page_area_present;
|
||||
struct osmo_routing_area_id page_rai = {};
|
||||
|
||||
if (ranap_decode_pagingies(&ies, &imsg->value) < 0) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR,
|
||||
"Rx Paging from CN: decoding RANAP IEs failed\n");
|
||||
return -1;
|
||||
}
|
||||
domain = ies.cN_DomainIndicator;
|
||||
page_area_present = (ies.presenceMask & PAGINGIES_RANAP_PAGINGAREAID_PRESENT);
|
||||
|
||||
if (cnlink->pool->domain != domain) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR,
|
||||
"Rx Paging from CN: message indicates domain %s, but cnlink is on domain %s\n",
|
||||
ranap_domain_name(domain),
|
||||
ranap_domain_name(cnlink->pool->domain));
|
||||
goto free_ies_ret;
|
||||
}
|
||||
|
||||
if (page_area_present) {
|
||||
if (rai_from_RANAP_PagingAreaID(&page_rai, &ies.pagingAreaID) < 0) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR,
|
||||
"Rx Paging from CN: decoding RANAP IE Paging Area ID failed, broadcasting to all HNBs\n");
|
||||
/* fail over to broadcast... */
|
||||
page_area_present = false;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG,
|
||||
"Rx Paging from CN: %s PagingAreaID: %s %s\n",
|
||||
ranap_domain_name(domain),
|
||||
page_area_present ?
|
||||
get_value_string(ranap_paging_area_id_names, ies.pagingAreaID.present) :
|
||||
"NOT_PRESENT",
|
||||
osmo_rai_name2(&page_rai)
|
||||
);
|
||||
|
||||
llist_for_each_entry(hnb, &g_hnbgw->hnb_list, list) {
|
||||
if (!hnb->hnb_registered)
|
||||
continue;
|
||||
if (page_area_present &&
|
||||
!hnb_paging_area_id_match(hnb, ies.pagingAreaID.present, &page_rai))
|
||||
continue;
|
||||
|
||||
if (is_ps)
|
||||
HNBP_CTR_INC(hnb->persistent, HNB_CTR_PS_PAGING_ATTEMPTED);
|
||||
else
|
||||
HNBP_CTR_INC(hnb->persistent, HNB_CTR_CS_PAGING_ATTEMPTED);
|
||||
if (rua_tx_udt(hnb, data, len) == 0)
|
||||
forwarded = true;
|
||||
}
|
||||
|
||||
if (forwarded) {
|
||||
/* If Paging command was forwarded anywhere, store a record for it, to match paging response: */
|
||||
errmsg = cnlink_paging_add_ranap(cnlink, &ies);
|
||||
if (errmsg) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR,
|
||||
"Rx Paging from CN: %s. Skip storing paging record."
|
||||
" Later on, the Paging Response may be forwarded to the wrong CN peer.\n",
|
||||
errmsg);
|
||||
goto free_ies_ret;
|
||||
}
|
||||
}
|
||||
ranap_free_pagingies(&ies);
|
||||
return 0;
|
||||
|
||||
free_ies_ret:
|
||||
ranap_free_pagingies(&ies);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ranap_rx_udt_dl_initiating_msg(struct hnbgw_cnlink *cnlink,
|
||||
const struct osmo_scu_unitdata_param *unitdata,
|
||||
RANAP_InitiatingMessage_t *imsg,
|
||||
const uint8_t *data, unsigned int len)
|
||||
{
|
||||
switch (imsg->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_Reset:
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_RESET);
|
||||
return cn_ranap_rx_reset_cmd(cnlink, unitdata, imsg);
|
||||
case RANAP_ProcedureCode_id_Paging:
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_PAGING);
|
||||
return cn_ranap_rx_paging_cmd(cnlink, imsg, data, len);
|
||||
case RANAP_ProcedureCode_id_OverloadControl: /* Overload ind */
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_OVERLOAD_IND);
|
||||
break;
|
||||
case RANAP_ProcedureCode_id_ErrorIndication: /* Error ind */
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_ERROR_IND);
|
||||
break;
|
||||
case RANAP_ProcedureCode_id_ResetResource: /* request */
|
||||
case RANAP_ProcedureCode_id_InformationTransfer:
|
||||
case RANAP_ProcedureCode_id_DirectInformationTransfer:
|
||||
case RANAP_ProcedureCode_id_UplinkInformationExchange:
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_UNSUPPORTED);
|
||||
LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
|
||||
"Procedure %ld from CN, ignoring\n", imsg->procedureCode);
|
||||
break;
|
||||
default:
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_UNKNOWN);
|
||||
LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
|
||||
"Procedure %ld from CN, ignoring\n", imsg->procedureCode);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cn_ranap_rx_reset_ack(struct hnbgw_cnlink *cnlink,
|
||||
RANAP_SuccessfulOutcome_t *omsg)
|
||||
{
|
||||
RANAP_CN_DomainIndicator_t domain;
|
||||
RANAP_ResetAcknowledgeIEs_t ies;
|
||||
int rc;
|
||||
|
||||
rc = ranap_decode_resetacknowledgeies(&ies, &omsg->value);
|
||||
domain = ies.cN_DomainIndicator;
|
||||
ranap_free_resetacknowledgeies(&ies);
|
||||
|
||||
if (rc) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Rx RESET ACK: cannot decode IEs\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cnlink->pool->domain != domain) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Rx RESET ACK indicates domain %s, but this is %s on domain %s\n",
|
||||
ranap_domain_name(domain), cnlink->name, ranap_domain_name(cnlink->pool->domain));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cnlink_rx_reset_ack(cnlink);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ranap_rx_udt_dl_successful_msg(struct hnbgw_cnlink *cnlink,
|
||||
RANAP_SuccessfulOutcome_t *omsg)
|
||||
{
|
||||
switch (omsg->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_Reset: /* Reset acknowledge */
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_RESET);
|
||||
return cn_ranap_rx_reset_ack(cnlink, omsg);
|
||||
case RANAP_ProcedureCode_id_ResetResource: /* response */
|
||||
case RANAP_ProcedureCode_id_InformationTransfer:
|
||||
case RANAP_ProcedureCode_id_DirectInformationTransfer:
|
||||
case RANAP_ProcedureCode_id_UplinkInformationExchange:
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_UNSUPPORTED);
|
||||
LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
|
||||
"Procedure %ld from CN, ignoring\n", omsg->procedureCode);
|
||||
break;
|
||||
default:
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_UNKNOWN);
|
||||
LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
|
||||
"Procedure %ld from CN, ignoring\n", omsg->procedureCode);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _hnbgw_ranap_rx_udt_dl(struct hnbgw_cnlink *cnlink,
|
||||
const struct osmo_scu_unitdata_param *unitdata,
|
||||
RANAP_RANAP_PDU_t *pdu, const uint8_t *data, unsigned int len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (pdu->present) {
|
||||
case RANAP_RANAP_PDU_PR_initiatingMessage:
|
||||
rc = ranap_rx_udt_dl_initiating_msg(cnlink, unitdata, &pdu->choice.initiatingMessage, data, len);
|
||||
break;
|
||||
case RANAP_RANAP_PDU_PR_successfulOutcome:
|
||||
rc = ranap_rx_udt_dl_successful_msg(cnlink, &pdu->choice.successfulOutcome);
|
||||
break;
|
||||
case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
|
||||
LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
|
||||
"unsuccessful outcome procedure %ld from CN, ignoring\n",
|
||||
pdu->choice.unsuccessfulOutcome.procedureCode);
|
||||
rc = -ENOTSUP;
|
||||
break;
|
||||
default:
|
||||
LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
|
||||
"presence %u from CN, ignoring\n", pdu->present);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int hnbgw_ranap_rx_udt_dl(struct hnbgw_cnlink *cnlink, const struct osmo_scu_unitdata_param *unitdata,
|
||||
const uint8_t *data, unsigned int len)
|
||||
{
|
||||
RANAP_RANAP_PDU_t _pdu, *pdu = &_pdu;
|
||||
asn_dec_rval_t dec_ret;
|
||||
int rc;
|
||||
|
||||
memset(pdu, 0, sizeof(*pdu));
|
||||
dec_ret = aper_decode(NULL, &asn_DEF_RANAP_RANAP_PDU, (void **) &pdu,
|
||||
data, len, 0, 0);
|
||||
if (dec_ret.code != RC_OK) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Error in RANAP ASN.1 decode\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = _hnbgw_ranap_rx_udt_dl(cnlink, unitdata, pdu, data, len);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RANAP_PDU, pdu);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int destruct_ranap_ran_rx_co_ies(ranap_message *ranap_message_p)
|
||||
{
|
||||
ranap_ran_rx_co_free(ranap_message_p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decode DL RANAP message with convenient memory freeing: just talloc_free() the returned pointer..
|
||||
* Allocate a ranap_message from OTC_SELECT, decode RANAP msgb into it, attach a talloc destructor that calls
|
||||
* ranap_cn_rx_co_free() upon talloc_free(), and return the decoded ranap_message. */
|
||||
static ranap_message *hnbgw_decode_ranap_ran_co(struct msgb *ranap_msg)
|
||||
{
|
||||
int rc;
|
||||
ranap_message *message;
|
||||
|
||||
if (!msg_has_l2_data(ranap_msg))
|
||||
return NULL;
|
||||
message = talloc_zero(OTC_SELECT, ranap_message);
|
||||
rc = ranap_ran_rx_co_decode(NULL, message, msgb_l2(ranap_msg), msgb_l2len(ranap_msg));
|
||||
if (rc != 0) {
|
||||
talloc_free(message);
|
||||
return NULL;
|
||||
}
|
||||
talloc_set_destructor(message, destruct_ranap_ran_rx_co_ies);
|
||||
return message;
|
||||
}
|
||||
|
||||
/* Process a received RANAP PDU through SCCP DATA.ind coming from CN (MSC/SGSN)
|
||||
* ranap_msg is owned by OTC_SELECT. */
|
||||
int hnbgw_ranap_rx_data_dl(struct hnbgw_context_map *map, struct msgb *ranap_msg)
|
||||
{
|
||||
OSMO_ASSERT(map);
|
||||
OSMO_ASSERT(msg_has_l2_data(ranap_msg));
|
||||
|
||||
/* See if it is a RAB Assignment Request message from SCCP to RUA, where we need to change the user plane
|
||||
* information, for RTP mapping via MGW, or GTP mapping via UPF. */
|
||||
ranap_message *message = hnbgw_decode_ranap_ran_co(ranap_msg);
|
||||
if (message) {
|
||||
LOG_MAP(map, DCN, LOGL_DEBUG, "rx from SCCP: RANAP %s\n",
|
||||
get_value_string(ranap_procedure_code_vals, message->procedureCode));
|
||||
|
||||
kpi_ranap_process_dl(map, message);
|
||||
|
||||
if (!map->is_ps) {
|
||||
/* Circuit-Switched. Set up mapping of RTP ports via MGW */
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* mgw_fsm_alloc_and_handle_rab_ass_req() takes ownership of (ranap) message */
|
||||
return handle_cs_rab_ass_req(map, ranap_msg, message);
|
||||
case RANAP_ProcedureCode_id_Iu_Release:
|
||||
/* Any IU Release will terminate the MGW FSM, the message itsself is not passed to the
|
||||
* FSM code. It is just forwarded normally by map_rua_tx_dt() below. */
|
||||
mgw_fsm_release(map);
|
||||
break;
|
||||
}
|
||||
#if ENABLE_PFCP
|
||||
} else {
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* If a UPF is configured, handle the RAB Assignment via ps_rab_ass_fsm, and replace the
|
||||
* GTP F-TEIDs in the RAB Assignment message before passing it on to RUA. */
|
||||
if (hnb_gw_is_gtp_mapping_enabled()) {
|
||||
LOG_MAP(map, DCN, LOGL_DEBUG,
|
||||
"RAB Assignment: setting up GTP tunnel mapping via UPF %s\n",
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, osmo_pfcp_cp_peer_get_remote_addr(g_hnbgw->pfcp.cp_peer)));
|
||||
return hnbgw_gtpmap_rx_rab_ass_req(map, ranap_msg, message);
|
||||
}
|
||||
/* If no UPF is configured, directly forward the message as-is (no GTP mapping). */
|
||||
LOG_MAP(map, DCN, LOGL_DEBUG, "RAB Assignment: no UPF configured, forwarding as-is\n");
|
||||
break;
|
||||
|
||||
case RANAP_ProcedureCode_id_Iu_Release:
|
||||
/* Any IU Release will terminate the MGW FSM, the message itsself is not passed to the
|
||||
* FSM code. It is just forwarded normally by map_rua_tx_dt() below. */
|
||||
hnbgw_gtpmap_release(map);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* It was not a RAB Assignment Request that needed to be intercepted. Forward as-is to RUA. */
|
||||
return map_rua_dispatch(map, MAP_RUA_EV_TX_DIRECT_TRANSFER, ranap_msg);
|
||||
}
|
||||
|
||||
int hnbgw_ranap_init(void)
|
||||
{
|
||||
return 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* hnb-gw specific code for RUA (Ranap User Adaption) */
|
||||
/* hnb-gw specific code for RUA (Ranap User Adaption), 3GPP TS 25.468 */
|
||||
|
||||
/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
@@ -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>
|
||||
@@ -40,7 +42,6 @@
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/hnbap/HNBAP_CN-DomainIndicator.h>
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
|
||||
static int hnbgw_rua_tx(struct hnb_context *ctx, struct msgb *msg)
|
||||
{
|
||||
@@ -166,6 +167,20 @@ int rua_tx_disc(struct hnb_context *hnb, int is_ps, uint32_t context_id,
|
||||
return hnbgw_rua_tx(hnb, msg);
|
||||
}
|
||||
|
||||
/* Send Disconnect to RUA without RANAP data */
|
||||
static void rua_tx_disc_conn_fail(struct hnb_context *hnb, bool is_ps, uint32_t context_id)
|
||||
{
|
||||
RUA_Cause_t rua_cause = {
|
||||
.present = RUA_Cause_PR_radioNetwork,
|
||||
.choice.radioNetwork = RUA_CauseRadioNetwork_connect_failed,
|
||||
};
|
||||
|
||||
LOG_HNBP(hnb->persistent, LOGL_INFO, "Tx RUA Disconnect\n");
|
||||
|
||||
if (rua_tx_disc(hnb, is_ps, context_id, &rua_cause, NULL, 0))
|
||||
LOG_HNBP(hnb->persistent, LOGL_ERROR, "Failed to send Disconnect to RUA\n");
|
||||
}
|
||||
|
||||
static struct value_string rua_procedure_code_names[] = {
|
||||
{ RUA_ProcedureCode_id_Connect, "Connect" },
|
||||
{ RUA_ProcedureCode_id_DirectTransfer, "DirectTransfer" },
|
||||
@@ -228,7 +243,6 @@ static int rua_to_scu(struct hnb_context *hnb,
|
||||
struct msgb *ranap_msg = NULL;
|
||||
struct hnbgw_context_map *map = NULL;
|
||||
bool is_ps;
|
||||
int logl;
|
||||
|
||||
switch (cN_DomainIndicator) {
|
||||
case RUA_CN_DomainIndicator_cs_domain:
|
||||
@@ -253,14 +267,13 @@ static int rua_to_scu(struct hnb_context *hnb,
|
||||
}
|
||||
|
||||
map = context_map_find_by_rua_ctx_id(hnb, context_id, is_ps);
|
||||
logl = LOGL_ERROR;
|
||||
|
||||
switch (rua_procedure) {
|
||||
case RUA_ProcedureCode_id_Connect:
|
||||
/* A Connect message can only be the first message for an unused RUA context */
|
||||
if (map) {
|
||||
/* Already established this RUA context. But then how can it be a Connect message. */
|
||||
LOGHNB(hnb, DRUA, LOGL_ERROR, "rx RUA %s for already active RUA context %u\n",
|
||||
LOGHNB(hnb, DRUA, LOGL_NOTICE, "rx RUA %s for already active RUA context %u\n",
|
||||
rua_procedure_code_name(rua_procedure), context_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -270,6 +283,7 @@ static int rua_to_scu(struct hnb_context *hnb,
|
||||
LOGHNB(hnb, DRUA, LOGL_ERROR,
|
||||
"Failed to create context map for %s: rx RUA %s with %u bytes RANAP data\n",
|
||||
is_ps ? "IuPS" : "IuCS", rua_procedure_code_name(rua_procedure), data ? len : 0);
|
||||
rua_tx_disc_conn_fail(hnb, is_ps, context_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
@@ -278,13 +292,19 @@ static int rua_to_scu(struct hnb_context *hnb,
|
||||
/* For RUA Disconnect, do not spam the ERROR log. It is just a stray Disconnect, no harm done.
|
||||
* Context: some CN are known to rapidly tear down SCCP without waiting for RUA to disconnect gracefully
|
||||
* (IU Release Complete). Such CN would cause ERROR logging for each and every released context map. */
|
||||
logl = LOGL_DEBUG;
|
||||
/* fall thru */
|
||||
if (!map) {
|
||||
LOGHNB(hnb, DRUA, LOGL_DEBUG, "rx RUA %s for unknown RUA context %u\n",
|
||||
rua_procedure_code_name(rua_procedure), context_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Any message other than Connect must have a valid RUA context */
|
||||
if (!map) {
|
||||
LOGHNB(hnb, DRUA, logl, "rx RUA %s for unknown RUA context %u\n",
|
||||
LOGHNB(hnb, DRUA, LOGL_NOTICE, "rx RUA %s for unknown RUA context %u\n",
|
||||
rua_procedure_code_name(rua_procedure), context_id);
|
||||
rua_tx_disc_conn_fail(hnb, is_ps, context_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -334,7 +337,7 @@ DEFUN(cfg_hnbgw_iuh_hnbap_allow_tmsi, cfg_hnbgw_iuh_hnbap_allow_tmsi_cmd,
|
||||
"hnbap-allow-tmsi (0|1)",
|
||||
"Allow HNBAP UE Register messages with TMSI or PTMSI identity\n"
|
||||
"Only accept IMSI identity, reject TMSI or PTMSI\n"
|
||||
"Accept IMSI, TMSI or PTMSI as UE identity\n")
|
||||
"Accept IMSI, TMSI or PTMSI as UE identity (default)\n")
|
||||
{
|
||||
g_hnbgw->config.hnbap_allow_tmsi = (*argv[0] == '1');
|
||||
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",
|
||||
@@ -1039,8 +1054,8 @@ static int config_write_hnbgw_iuh(struct vty *vty)
|
||||
if (port && port != IUH_DEFAULT_SCTP_PORT)
|
||||
vty_out(vty, " local-port %u%s", port, VTY_NEWLINE);
|
||||
|
||||
if (g_hnbgw->config.hnbap_allow_tmsi)
|
||||
vty_out(vty, " hnbap-allow-tmsi 1%s", VTY_NEWLINE);
|
||||
if (!g_hnbgw->config.hnbap_allow_tmsi)
|
||||
vty_out(vty, " hnbap-allow-tmsi 0%s", VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -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,10 +24,18 @@
|
||||
|
||||
#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>
|
||||
|
||||
const struct value_string hnbgw_rab_state_names[] = {
|
||||
{ RAB_STATE_INACTIVE, "INACTIVE" },
|
||||
{ RAB_STATE_ACT_REQ, "ACT_REQ" },
|
||||
{ RAB_STATE_ACTIVE, "ACTIVE" },
|
||||
{ RAB_STATE_REL_REQ, "REL_REQ" },
|
||||
{}
|
||||
};
|
||||
|
||||
/***********************************************************************
|
||||
* DOWNLINK messages
|
||||
@@ -44,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;
|
||||
}
|
||||
}
|
||||
@@ -100,15 +106,20 @@ 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 %u\n", map->rab_state[rab_id]);
|
||||
"Unexpected RAB Activation/Modification Req for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -123,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)
|
||||
@@ -144,17 +154,19 @@ 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 %u\n", map->rab_state[rab_id]);
|
||||
"Unexpected RAB Release Req in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
break;
|
||||
}
|
||||
/* mark that RAB as release requested */
|
||||
@@ -241,15 +253,20 @@ 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 %u\n", map->rab_state[rab_id]);
|
||||
"Unexpected RAB Activation/Modification Conf for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -260,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 = {};
|
||||
@@ -285,10 +300,15 @@ 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 %u\n", map->rab_state[rab_id]);
|
||||
"Unexpected RAB Release Conf for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
break;
|
||||
}
|
||||
/* mark that RAB as released */
|
||||
@@ -327,16 +347,21 @@ 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 %u\n", map->rab_state[rab_id]);
|
||||
"Unexpected RAB Activation/Modification Failed for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -347,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 = {};
|
||||
@@ -373,16 +396,21 @@ 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 %u\n", map->rab_state[rab_id]);
|
||||
"Unexpected RAB Release Failed for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -846,9 +846,9 @@ int handle_cs_rab_ass_req(struct hnbgw_context_map *map, struct msgb *ranap_msg,
|
||||
* into account under the assumption that voice calls typically require a single RAB only. Nevertheless, we
|
||||
* will block all incoming RAB assignments that try to assign more (or less) than one RAB. */
|
||||
if (ranap_rab_ass_req_ies_get_count(&message->msg.raB_AssignmentRequestIEs) != 1) {
|
||||
LOGP(DMGW, LOGL_ERROR,
|
||||
"%s() rua_ctx_id=%d, RAB-AssignmentRequest with more than one RAB assignment -- abort!\n",
|
||||
__func__, map->rua_ctx_id);
|
||||
LOG_MAP(map, DMGW, LOGL_ERROR,
|
||||
"%s() RAB-AssignmentRequest with more than one RAB assignment -- abort!\n",
|
||||
__func__);
|
||||
tx_release_req(map);
|
||||
return -1;
|
||||
}
|
||||
@@ -886,9 +886,9 @@ int mgw_fsm_handle_cs_rab_ass_resp(struct hnbgw_context_map *map, struct msgb *r
|
||||
/* NOTE: This situation is a corner-case. We may end up here when the co-located MGW caused a problem
|
||||
* on the way between RANAP RAB Assignment Request and RANAP RAB Assignment Response. */
|
||||
|
||||
LOGP(DMGW, LOGL_ERROR,
|
||||
"%s() rua_ctx_id=%d, no MGW fsm -- sending Iu-Release-Request!\n",
|
||||
__func__, map->rua_ctx_id);
|
||||
LOG_MAP(map, DMGW, LOGL_ERROR,
|
||||
"%s() no MGW fsm -- sending Iu-Release-Request!\n",
|
||||
__func__);
|
||||
|
||||
/* Send a release request, to make sure that the MSC is aware of the problem. */
|
||||
tx_release_req(map);
|
||||
|
||||
@@ -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();
|
||||
@@ -369,5 +370,8 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* not reached */
|
||||
#if ENABLE_PFCP
|
||||
hnbgw_pfcp_release();
|
||||
#endif
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -294,7 +301,7 @@ static void ps_rab_ass_fsm_wait_local_f_teids(struct osmo_fsm_inst *fi, uint32_t
|
||||
/* See whether all information is in so that we can forward the modified RAB Assignment Request to RUA. */
|
||||
static void ps_rab_ass_req_check_local_f_teids(struct ps_rab_ass *rab_ass)
|
||||
{
|
||||
struct ps_rab *rab;
|
||||
struct ps_rab *rab = NULL;
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies = &rab_ass->ranap_rab_ass_req_message->msg.raB_AssignmentRequestIEs;
|
||||
int i;
|
||||
struct msgb *msg;
|
||||
@@ -346,6 +353,12 @@ continue_cleanloop:
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyItemFirst, &first);
|
||||
}
|
||||
|
||||
if (!rab) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Lookup PS RAB Assignment Request failed\n");
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send the modified RAB Assignment Request to the hNodeB, wait for the RAB Assignment Response */
|
||||
msg = ranap_rab_ass_req_encode(ies);
|
||||
if (!msg) {
|
||||
@@ -407,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_NOTICE, "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)
|
||||
{
|
||||
@@ -479,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:
|
||||
@@ -595,14 +666,14 @@ continue_cleanloop:
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_NOTICE, "Sending RANAP PS RAB-AssignmentResponse with mapped GTP info\n");
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_INFO, "Sending RANAP PS RAB-AssignmentResponse with mapped GTP info\n");
|
||||
|
||||
rc = map_sccp_dispatch(rab_ass->map, MAP_SCCP_EV_TX_DATA_REQUEST, msg);
|
||||
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);
|
||||
}
|
||||
@@ -612,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);
|
||||
}
|
||||
|
||||
@@ -627,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);
|
||||
}
|
||||
}
|
||||
@@ -658,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,
|
||||
@@ -320,7 +320,7 @@ static void ps_rab_fsm_wait_pfcp_est_resp_onenter(struct osmo_fsm_inst *fi, uint
|
||||
m->h.seid = 0;
|
||||
|
||||
/* Make a new CP-SEID, our local reference for the PFCP session. */
|
||||
rab->cp_seid = osmo_pfcp_next_seid(&g_hnbgw->pfcp.cp_peer->next_seid_state);
|
||||
rab->cp_seid = osmo_pfcp_cp_peer_next_seid(g_hnbgw->pfcp.cp_peer);
|
||||
cp_f_seid = (struct osmo_pfcp_ie_f_seid){
|
||||
.seid = rab->cp_seid,
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -43,7 +43,6 @@ DISTCLEANFILES = \
|
||||
if ENABLE_EXT_TESTS
|
||||
python-tests:
|
||||
$(MAKE) vty-test
|
||||
$(MAKE) config-tests
|
||||
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
else
|
||||
@@ -69,6 +68,11 @@ vty-test: $(top_builddir)/src/osmo-hnbgw/osmo-hnbgw
|
||||
$(U) $(srcdir)/$(VTY_TEST)
|
||||
|
||||
# Test each config/*.cfg file with the corresponding config/*.vty transcript test.
|
||||
#
|
||||
# To be invoked manually only: This is not part of 'make check' because the
|
||||
# output may change when libosmo-sccp changes its VTY appearance, which can
|
||||
# cause annoying test fallout.
|
||||
#
|
||||
# Each config test runs an osmo-hnbgw process to talk to, so they must not run concurrently.
|
||||
# To prevent 'make -j N' from running these tests in parallel, call a script with a linear for-loop.
|
||||
.PHONY: config-tests
|
||||
|
||||
@@ -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
|
||||
...
|
||||
|
||||
@@ -32,7 +32,6 @@ cs7 instance 1
|
||||
hnbgw
|
||||
iuh
|
||||
local-ip 0.0.0.0
|
||||
hnbap-allow-tmsi 1
|
||||
mgw 0
|
||||
remote-ip 127.0.0.1
|
||||
local-port 2729
|
||||
|
||||
@@ -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;
|
||||
@@ -68,7 +71,7 @@ struct test tests[] = {
|
||||
},
|
||||
|
||||
{
|
||||
.id_str = "999-999-L65534-R65535-S65535-C268435455",
|
||||
.id_str = "999-999-L65534-R255-S65535-C268435455",
|
||||
.id = {
|
||||
.plmn = {
|
||||
.mcc = 999,
|
||||
@@ -76,7 +79,7 @@ struct test tests[] = {
|
||||
.mnc_3_digits = true,
|
||||
},
|
||||
.lac = 65534,
|
||||
.rac = 65535,
|
||||
.rac = 255,
|
||||
.sac = 65535,
|
||||
.cid = (1 << 28) - 1,
|
||||
},
|
||||
@@ -92,7 +95,7 @@ struct test tests[] = {
|
||||
},
|
||||
/* TODO? There is no bounds checking on RAC and SAC.
|
||||
{
|
||||
.id_str = "001-001-L1-R65536-S1-C1",
|
||||
.id_str = "001-001-L1-R256-S1-C1",
|
||||
.expect_rc = -EINVAL,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
-> umts_cell_id_to_str_buf(): ok
|
||||
-> "001-99-L1-R1-S1-C1"
|
||||
umts_cell_id_equal(expected, parsed): ok
|
||||
"999-999-L65534-R65535-S65535-C268435455"
|
||||
"999-999-L65534-R255-S65535-C268435455"
|
||||
-> umts_cell_id_from_str(): ok
|
||||
-> umts_cell_id_to_str_buf(): ok
|
||||
-> "999-999-L65534-R65535-S65535-C268435455"
|
||||
-> "999-999-L65534-R255-S65535-C268435455"
|
||||
umts_cell_id_equal(expected, parsed): ok
|
||||
"1000-001-L1-R1-S1-C1"
|
||||
expected rc != 0: ok
|
||||
|
||||
Reference in New Issue
Block a user