pespin has submitted this change. (
https://gerrit.osmocom.org/c/osmo-hnbgw/+/40254?usp=email )
Change subject: Split hnb_context,hnb_persistent,umts_cell_id to their own files
......................................................................
Split hnb_context,hnb_persistent,umts_cell_id to their own files
It's really disturbing having to cope with all HNB related code while
for instance focusing on CN side of things.
Properly split the HNB related code to its own files so it can be left
out easily when needed, or stuff found more easily when required.
It also makes it easier to extend it later.
umts_cell_id is split to its own module since it's needed both in
hnb_context and hnb_persitent, and it's really independent of everything
else in the code.
Change-Id: I4ff65d14f94d99a57316c2483c92244078bb5dd9
---
M include/osmocom/hnbgw/Makefile.am
M include/osmocom/hnbgw/context_map.h
A include/osmocom/hnbgw/hnb.h
A include/osmocom/hnbgw/hnb_persistent.h
M include/osmocom/hnbgw/hnbgw.h
M include/osmocom/hnbgw/hnbgw_hnbap.h
M include/osmocom/hnbgw/hnbgw_rua.h
A include/osmocom/hnbgw/umts_cell_id.h
M src/osmo-hnbgw/Makefile.am
M src/osmo-hnbgw/context_map.c
M src/osmo-hnbgw/context_map_rua.c
M src/osmo-hnbgw/context_map_sccp.c
A src/osmo-hnbgw/hnb.c
A src/osmo-hnbgw/hnb_persistent.c
M src/osmo-hnbgw/hnbgw.c
M src/osmo-hnbgw/hnbgw_hnbap.c
M src/osmo-hnbgw/hnbgw_ranap.c
M src/osmo-hnbgw/hnbgw_rua.c
M src/osmo-hnbgw/hnbgw_vty.c
M src/osmo-hnbgw/kpi_dtap.c
M src/osmo-hnbgw/kpi_ranap.c
M src/osmo-hnbgw/nft_kpi.c
M src/osmo-hnbgw/osmo_hnbgw_main.c
A src/osmo-hnbgw/umts_cell_id.c
M tests/ranap_rab_ass/ranap_rab_ass_test.c
M tests/umts_cell_id/umts_cell_id_test.c
26 files changed, 1,224 insertions(+), 1,006 deletions(-)
Approvals:
laforge: Looks good to me, but someone else must approve
Jenkins Builder: Verified
osmith: Looks good to me, approved
diff --git a/include/osmocom/hnbgw/Makefile.am b/include/osmocom/hnbgw/Makefile.am
index ebc26ce..04fc713 100644
--- a/include/osmocom/hnbgw/Makefile.am
+++ b/include/osmocom/hnbgw/Makefile.am
@@ -1,6 +1,8 @@
noinst_HEADERS = \
cnlink.h \
context_map.h \
+ hnb.h \
+ hnb_persistent.h \
hnbgw.h \
hnbgw_cn.h \
hnbgw_hnbap.h \
@@ -15,5 +17,6 @@
ps_rab_fsm.h \
ranap_rab_ass.h \
tdefs.h \
+ umts_cell_id.h \
vty.h \
$(NULL)
diff --git a/include/osmocom/hnbgw/context_map.h b/include/osmocom/hnbgw/context_map.h
index 80b7e2b..a885f5b 100644
--- a/include/osmocom/hnbgw/context_map.h
+++ b/include/osmocom/hnbgw/context_map.h
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/utils.h>
+#include <osmocom/hnbgw/hnb.h>
#include <osmocom/hnbgw/hnbgw.h>
#include <osmocom/gsm/gsm48.h>
diff --git a/include/osmocom/hnbgw/hnb.h b/include/osmocom/hnbgw/hnb.h
new file mode 100644
index 0000000..9c210e6
--- /dev/null
+++ b/include/osmocom/hnbgw/hnb.h
@@ -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);
diff --git a/include/osmocom/hnbgw/hnb_persistent.h
b/include/osmocom/hnbgw/hnb_persistent.h
new file mode 100644
index 0000000..c4379de
--- /dev/null
+++ b/include/osmocom/hnbgw/hnb_persistent.h
@@ -0,0 +1,181 @@
+#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_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,
+};
+
+#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);
diff --git a/include/osmocom/hnbgw/hnbgw.h b/include/osmocom/hnbgw/hnbgw.h
index b253d7f..eb9c61c 100644
--- a/include/osmocom/hnbgw/hnbgw.h
+++ b/include/osmocom/hnbgw/hnbgw.h
@@ -23,7 +23,6 @@
#include <osmocom/hnbgw/hnbgw_cn.h>
#define STORE_UPTIME_INTERVAL 10 /* seconds */
-#define HNB_STORE_RAB_DURATIONS_INTERVAL 1 /* seconds */
enum {
DMAIN,
@@ -39,15 +38,6 @@
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
@@ -57,11 +47,6 @@
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. */
@@ -84,123 +69,6 @@
#define IUH_MSGB_SIZE 2048
-enum hnb_rate_ctr {
- HNB_CTR_IUH_ESTABLISHED,
- HNB_CTR_RANAP_PS_ERR_IND_UL,
- HNB_CTR_RANAP_CS_ERR_IND_UL,
- HNB_CTR_RANAP_PS_RESET_REQ_UL,
- HNB_CTR_RANAP_CS_RESET_REQ_UL,
-
- HNB_CTR_RANAP_PS_RAB_ACT_REQ,
- HNB_CTR_RANAP_CS_RAB_ACT_REQ,
-
- HNB_CTR_RANAP_PS_RAB_ACT_CNF,
- HNB_CTR_RANAP_CS_RAB_ACT_CNF,
-
- HNB_CTR_RANAP_PS_RAB_ACT_FAIL,
- HNB_CTR_RANAP_CS_RAB_ACT_FAIL,
-
- HNB_CTR_RANAP_PS_RAB_MOD_REQ,
- HNB_CTR_RANAP_CS_RAB_MOD_REQ,
-
- HNB_CTR_RANAP_PS_RAB_MOD_CNF,
- HNB_CTR_RANAP_CS_RAB_MOD_CNF,
-
- HNB_CTR_RANAP_PS_RAB_MOD_FAIL,
- HNB_CTR_RANAP_CS_RAB_MOD_FAIL,
-
- HNB_CTR_RANAP_PS_RAB_REL_REQ,
- HNB_CTR_RANAP_CS_RAB_REL_REQ,
- HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL,
- HNB_CTR_RANAP_CS_RAB_REL_REQ_ABNORMAL,
-
- HNB_CTR_RANAP_PS_RAB_REL_CNF,
- HNB_CTR_RANAP_CS_RAB_REL_CNF,
-
- HNB_CTR_RANAP_PS_RAB_REL_FAIL,
- HNB_CTR_RANAP_CS_RAB_REL_FAIL,
-
- HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT,
- HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT,
- HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL,
- HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT_ABNORMAL,
-
- HNB_CTR_RUA_ERR_IND,
-
- HNB_CTR_RUA_PS_CONNECT_UL,
- HNB_CTR_RUA_CS_CONNECT_UL,
-
- HNB_CTR_RUA_PS_DISCONNECT_UL,
- HNB_CTR_RUA_CS_DISCONNECT_UL,
- HNB_CTR_RUA_PS_DISCONNECT_DL,
- HNB_CTR_RUA_CS_DISCONNECT_DL,
-
- HNB_CTR_RUA_PS_DT_UL,
- HNB_CTR_RUA_CS_DT_UL,
- HNB_CTR_RUA_PS_DT_DL,
- HNB_CTR_RUA_CS_DT_DL,
-
- HNB_CTR_RUA_UDT_UL,
- HNB_CTR_RUA_UDT_DL,
-
- HNB_CTR_PS_PAGING_ATTEMPTED,
- HNB_CTR_CS_PAGING_ATTEMPTED,
-
- HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL,
-
- HNB_CTR_DTAP_CS_LU_REQ,
- HNB_CTR_DTAP_CS_LU_ACC,
- HNB_CTR_DTAP_CS_LU_REJ,
-
- HNB_CTR_DTAP_PS_ATT_REQ,
- HNB_CTR_DTAP_PS_ATT_ACK,
- HNB_CTR_DTAP_PS_ATT_REJ,
-
- HNB_CTR_DTAP_PS_RAU_REQ,
- HNB_CTR_DTAP_PS_RAU_ACK,
- HNB_CTR_DTAP_PS_RAU_REJ,
-
- HNB_CTR_GTPU_PACKETS_UL,
- HNB_CTR_GTPU_TOTAL_BYTES_UL,
- HNB_CTR_GTPU_UE_BYTES_UL,
- HNB_CTR_GTPU_PACKETS_DL,
- HNB_CTR_GTPU_TOTAL_BYTES_DL,
- HNB_CTR_GTPU_UE_BYTES_DL,
-};
-
-enum hnb_stat {
- HNB_STAT_UPTIME_SECONDS,
-};
-
-struct umts_cell_id {
- struct osmo_plmn_id plmn; /*!< Mobile Country Code and Mobile Network Code (000-00 to
999-999) */
- uint16_t lac; /*!< Locaton Area Code (1-65534) */
- uint8_t rac; /*!< Routing Area Code (0-255) */
- uint16_t sac; /*!< Service Area Code */
- uint32_t cid; /*!< Cell ID */
-};
-int umts_cell_id_to_str_buf(char *buf, size_t buflen, const struct umts_cell_id *ucid);
-char *umts_cell_id_to_str_c(void *ctx, const struct umts_cell_id *ucid);
-const char *umts_cell_id_to_str(const struct umts_cell_id *ucid);
-int umts_cell_id_from_str(struct umts_cell_id *ucid, const char *instr);
-uint32_t umts_cell_id_hash(const struct umts_cell_id *ucid);
-
-/*! are both given umts_cell_id euqal? */
-static inline bool umts_cell_id_equal(const struct umts_cell_id *a, const struct
umts_cell_id *b)
-{
- if (osmo_plmn_cmp(&a->plmn, &b->plmn))
- return false;
- if (a->lac != b->lac)
- return false;
- if (a->rac != b->rac)
- return false;
- if (a->sac != b->sac)
- return false;
- if (a->cid != b->cid)
- return false;
- return true;
-}
-
struct hnbgw_context_map;
static inline bool cnlink_is_cs(const struct hnbgw_cnlink *cnlink)
@@ -213,86 +81,6 @@
return cnlink && cnlink->pool->domain == DOMAIN_PS;
}
-/* The lifecycle of the hnb_context object is the same as its conn */
-struct hnb_context {
- /*! Entry in HNB-global list of HNB */
- struct llist_head list;
- /*! SCTP socket + write queue for Iuh to this specific HNB */
- struct osmo_stream_srv *conn;
- /*! copied from HNB-Identity-Info IE */
- char identity_info[256];
- /*! copied from Cell Identity IE */
- struct umts_cell_id id;
-
- /*! SCTP stream ID for HNBAP */
- uint16_t hnbap_stream;
- /*! SCTP stream ID for RUA */
- uint16_t rua_stream;
-
- /*! True if a HNB-REGISTER-REQ from this HNB has been accepted. */
- bool hnb_registered;
-
- /* linked list of hnbgw_context_map */
- struct llist_head map_list;
-
- /*! pointer to the associated hnb persistent state. Always present after HNB-Register
*/
- struct hnb_persistent *persistent;
-};
-
-#define HNBP_CTR(hnbp, x) rate_ctr_group_get_ctr((hnbp)->ctrs, x)
-#define HNBP_CTR_INC(hnbp, x) rate_ctr_inc(HNBP_CTR(hnbp, x))
-#define HNBP_CTR_ADD(hnbp, x, y) rate_ctr_add2((hnbp)->ctrs, x, y)
-
-#define HNBP_STAT(hbp, x) osmo_stat_item_group_get_item((hnbp)->statg, x)
-#define HNBP_STAT_SET(hnbp, x, val) osmo_stat_item_set(HNBP_STAT(hnbp, x), val)
-
-/* persistent data for one HNB. This continues to exist even as conn / hnb_context is
deleted on disconnect */
-struct hnb_persistent {
- /*! Entry in HNBGW-global list of hnb_persistent */
- struct llist_head list;
- /*! Entry in hash table g_hnbgw->hnb_persistent_by_id. */
- struct hlist_node node_by_id;
- /*! back-pointer to hnb_context. Can be NULL if no context at this point */
- struct hnb_context *ctx;
-
- /*! unique cell identity; copied from HNB REGISTER REQ */
- struct umts_cell_id id;
- /*! stringified version of the cell identiy above (for printing/naming) */
- const char *id_str;
-
- /*! copied from HNB-Identity-Info IE */
- time_t updowntime;
-
- struct rate_ctr_group *ctrs;
- struct osmo_stat_item_group *statg;
-
- /* State that the main thread needs in order to know what was requested from the nft
worker threads and what
- * still needs to be requested. */
- struct {
- /* Whether a persistent named counter was added in nftables for this cell id. */
- bool persistent_counter_added;
-
- /* The last hNodeB GTP-U address we asked the nft maintenance thread to set up.
- * osmo_sockaddr_str_is_nonzero(addr_remote) == false when no rules were added yet, and
when
- * we asked the nft maintenance thread to remove the rules for this hNodeB because it
has
- * disconnected. */
- struct osmo_sockaddr_str addr_remote;
-
- /* the nft handles needed to clean up the UL and DL rules when the hNodeB disconnects,
- * and the last seen counter value gotten from nft. */
- struct {
- struct nft_kpi_handle h;
- struct nft_kpi_val v;
- } ul;
- struct {
- struct nft_kpi_handle h;
- struct nft_kpi_val v;
- } dl;
- } nft_kpi;
-
- struct osmo_timer_list disconnected_timeout;
-};
-
struct hnbgw {
struct {
const char *iuh_local_ip;
@@ -375,22 +163,8 @@
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);
@@ -408,6 +182,4 @@
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);
diff --git a/include/osmocom/hnbgw/hnbgw_hnbap.h b/include/osmocom/hnbgw/hnbgw_hnbap.h
index 2f0c5b4..a78657e 100644
--- a/include/osmocom/hnbgw/hnbgw_hnbap.h
+++ b/include/osmocom/hnbgw/hnbgw_hnbap.h
@@ -3,5 +3,7 @@
#include <osmocom/hnbgw/hnbgw.h>
+struct hnb_context;
+
int hnbgw_hnbap_rx(struct hnb_context *hnb, struct msgb *msg);
int hnbgw_hnbap_init(void);
diff --git a/include/osmocom/hnbgw/hnbgw_rua.h b/include/osmocom/hnbgw/hnbgw_rua.h
index 2b74be9..583756d 100644
--- a/include/osmocom/hnbgw/hnbgw_rua.h
+++ b/include/osmocom/hnbgw/hnbgw_rua.h
@@ -4,6 +4,8 @@
#include <osmocom/hnbgw/hnbgw.h>
#include <osmocom/rua/RUA_Cause.h>
+struct hnb_context;
+
int hnbgw_rua_rx(struct hnb_context *hnb, struct msgb *msg);
int rua_tx_udt(struct hnb_context *hnb, const uint8_t *data, unsigned int len);
diff --git a/include/osmocom/hnbgw/umts_cell_id.h b/include/osmocom/hnbgw/umts_cell_id.h
new file mode 100644
index 0000000..5bab1fc
--- /dev/null
+++ b/include/osmocom/hnbgw/umts_cell_id.h
@@ -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;
+}
diff --git a/src/osmo-hnbgw/Makefile.am b/src/osmo-hnbgw/Makefile.am
index df2bb23..c0798a7 100644
--- a/src/osmo-hnbgw/Makefile.am
+++ b/src/osmo-hnbgw/Makefile.am
@@ -32,6 +32,8 @@
$(NULL)
libhnbgw_la_SOURCES = \
+ hnb.c \
+ hnb_persistent.c \
hnbgw.c \
hnbgw_hnbap.c \
hnbgw_l3.c \
@@ -50,6 +52,7 @@
kpi_dtap.c \
kpi_ranap.c \
tdefs.c \
+ umts_cell_id.c \
nft_kpi.c \
$(NULL)
diff --git a/src/osmo-hnbgw/context_map.c b/src/osmo-hnbgw/context_map.c
index 5a6d79b..cc68ec6 100644
--- a/src/osmo-hnbgw/context_map.c
+++ b/src/osmo-hnbgw/context_map.c
@@ -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>
diff --git a/src/osmo-hnbgw/context_map_rua.c b/src/osmo-hnbgw/context_map_rua.c
index e84ce65..6315985 100644
--- a/src/osmo-hnbgw/context_map_rua.c
+++ b/src/osmo-hnbgw/context_map_rua.c
@@ -24,6 +24,7 @@
#include <osmocom/core/utils.h>
#include <osmocom/core/fsm.h>
+#include <osmocom/hnbgw/hnb.h>
#include <osmocom/hnbgw/hnbgw.h>
#include <osmocom/hnbgw/context_map.h>
#include <osmocom/hnbgw/tdefs.h>
diff --git a/src/osmo-hnbgw/context_map_sccp.c b/src/osmo-hnbgw/context_map_sccp.c
index 5b5e77f..f060ad9 100644
--- a/src/osmo-hnbgw/context_map_sccp.c
+++ b/src/osmo-hnbgw/context_map_sccp.c
@@ -27,6 +27,7 @@
#include <osmocom/sigtran/sccp_helpers.h>
+#include <osmocom/hnbgw/hnb.h>
#include <osmocom/hnbgw/hnbgw_cn.h>
#include <osmocom/hnbgw/context_map.h>
#include <osmocom/hnbgw/hnbgw_ranap.h>
diff --git a/src/osmo-hnbgw/hnb.c b/src/osmo-hnbgw/hnb.c
new file mode 100644
index 0000000..9938092
--- /dev/null
+++ b/src/osmo-hnbgw/hnb.c
@@ -0,0 +1,401 @@
+/* HNB related code */
+
+/* (C) 2015,2024 by Harald Welte <laforge(a)gnumonks.org>
+ * (C) 2016-2025 by sysmocom s.f.m.c. GmbH <info(a)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;
+}
diff --git a/src/osmo-hnbgw/hnb_persistent.c b/src/osmo-hnbgw/hnb_persistent.c
new file mode 100644
index 0000000..bcd6c4b
--- /dev/null
+++ b/src/osmo-hnbgw/hnb_persistent.c
@@ -0,0 +1,379 @@
+/* HNB persistent related code */
+
+/* (C) 2015,2024 by Harald Welte <laforge(a)gnumonks.org>
+ * (C) 2016-2025 by sysmocom s.f.m.c. GmbH <info(a)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_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) */
+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);
+}
diff --git a/src/osmo-hnbgw/hnbgw.c b/src/osmo-hnbgw/hnbgw.c
index c5ee2c1..c46ddad 100644
--- a/src/osmo-hnbgw/hnbgw.c
+++ b/src/osmo-hnbgw/hnbgw.c
@@ -1,7 +1,7 @@
/* kitchen sink for OsmoHNBGW implementation */
/* (C) 2015,2024 by Harald Welte <laforge(a)gnumonks.org>
- * (C) 2016-2023 by sysmocom s.f.m.c. GmbH <info(a)sysmocom.de>
+ * (C) 2016-2025 by sysmocom s.f.m.c. GmbH <info(a)sysmocom.de>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -40,6 +40,8 @@
#include <osmocom/pfcp/pfcp_proto.h>
#endif
+#include <osmocom/hnbgw/hnb.h>
+#include <osmocom/hnbgw/hnb_persistent.h>
#include <osmocom/hnbgw/hnbgw.h>
#include <osmocom/hnbgw/hnbgw_hnbap.h>
#include <osmocom/hnbgw/hnbgw_rua.h>
@@ -56,25 +58,17 @@
{}
};
-/* update the active RAB duration rate_ctr for given HNB */
-static void hnb_store_rab_durations(struct hnb_context *hnb)
+
+/* timer call-back: Update the HNB_STAT_UPTIME_SECONDS stat item of each hnb_persistent
*/
+static void hnbgw_store_hnb_uptime(void *data)
{
- struct hnbgw_context_map *map;
- struct timespec now;
- uint64_t elapsed_cs_rab_ms = 0;
+ struct hnb_persistent *hnbp;
- osmo_clock_gettime(CLOCK_MONOTONIC, &now);
-
- /* iterate over all context_maps (subscribers) */
- llist_for_each_entry(map, &hnb->map_list, hnb_list) {
- /* skip any PS maps, we care about CS RABs only here */
- if (map->is_ps)
- continue;
- elapsed_cs_rab_ms += mgw_fsm_get_elapsed_ms(map, &now);
+ llist_for_each_entry(hnbp, &g_hnbgw->hnb_persistent_list, list) {
+ HNBP_STAT_SET(hnbp, HNB_STAT_UPTIME_SECONDS, hnbp->ctx != NULL ?
hnbp_get_updowntime(hnbp) : 0);
}
- /* Export to rate countes. */
- rate_ctr_add(HNBP_CTR(hnb->persistent, HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL),
elapsed_cs_rab_ms);
+ osmo_timer_schedule(&g_hnbgw->store_uptime_timer, STORE_UPTIME_INTERVAL, 0);
}
static void hnbgw_store_hnb_rab_durations(void *data)
@@ -91,7 +85,6 @@
osmo_timer_schedule(&g_hnbgw->hnb_store_rab_durations_timer,
HNB_STORE_RAB_DURATIONS_INTERVAL, 0);
}
-
/***********************************************************************
* UE Context
***********************************************************************/
@@ -101,764 +94,6 @@
return g_hnbgw->next_ue_ctx_id++;
}
-/***********************************************************************
- * HNB Context
- ***********************************************************************/
-
-/* look-up HNB context by id. Used from CTRL */
-static struct hnb_context *hnb_context_by_id(uint32_t cid)
-{
- struct hnb_context *hnb;
-
- llist_for_each_entry(hnb, &g_hnbgw->hnb_list, list) {
- if (hnb->id.cid == cid)
- return hnb;
- }
-
- return NULL;
-}
-
-/* look-up HNB context by identity_info. Used from VTY */
-struct hnb_context *hnb_context_by_identity_info(const char *identity_info)
-{
- struct hnb_context *hnb;
-
- llist_for_each_entry(hnb, &g_hnbgw->hnb_list, list) {
- if (strcmp(identity_info, hnb->identity_info) == 0)
- return hnb;
- }
-
- return NULL;
-}
-
-static int hnb_read_cb(struct osmo_stream_srv *conn);
-static int hnb_closed_cb(struct osmo_stream_srv *conn);
-
-static struct hnb_context *hnb_context_alloc(struct osmo_stream_srv_link *link, int
new_fd)
-{
- struct hnb_context *ctx;
-
- ctx = talloc_zero(g_hnbgw, struct hnb_context);
- if (!ctx)
- return NULL;
- INIT_LLIST_HEAD(&ctx->map_list);
-
- ctx->conn = osmo_stream_srv_create(g_hnbgw, link, new_fd, hnb_read_cb, hnb_closed_cb,
ctx);
- if (!ctx->conn) {
- LOGP(DMAIN, LOGL_INFO, "error while creating connection\n");
- talloc_free(ctx);
- return NULL;
- }
-
- llist_add_tail(&ctx->list, &g_hnbgw->hnb_list);
- return ctx;
-}
-
-int umts_cell_id_to_str_buf(char *buf, size_t buflen, const struct umts_cell_id *ucid)
-{
- struct osmo_strbuf sb = { .buf = buf, .len = buflen };
- OSMO_STRBUF_APPEND_NOLEN(sb, osmo_plmn_name_buf, &ucid->plmn);
- OSMO_STRBUF_PRINTF(sb, "-L%u-R%u-S%u-C%u", ucid->lac, ucid->rac,
ucid->sac, ucid->cid);
- return sb.chars_needed;
-}
-
-char *umts_cell_id_to_str_c(void *ctx, const struct umts_cell_id *ucid)
-{
- OSMO_NAME_C_IMPL(ctx, 64, "ERROR", umts_cell_id_to_str_buf, ucid)
-}
-
-const char *umts_cell_id_to_str(const struct umts_cell_id *ucid)
-{
- return umts_cell_id_to_str_c(OTC_SELECT, ucid);
-}
-
-/* Useful to index a hash table by struct umts_cell_id. */
-uint32_t umts_cell_id_hash(const struct umts_cell_id *ucid)
-{
- return osmo_jhash(ucid, sizeof(*ucid), 0x423423);
-}
-
-/* parse a string representation of an umts_cell_id into its decoded representation */
-int umts_cell_id_from_str(struct umts_cell_id *ucid, const char *instr)
-{
- int rc;
- char buf[4];
- const char *pos = instr;
- const char *end;
-
- /* We want to use struct umts_cell_id as hashtable key. If it ever happens to contain
any padding bytes, make
- * sure everything is deterministically zero. */
- memset(ucid, 0, sizeof(*ucid));
-
- /* read MCC */
- end = strchr(pos, '-');
- if (!end || end <= pos || (end - pos) >= sizeof(buf))
- return -EINVAL;
- osmo_strlcpy(buf, pos, end - pos + 1);
- if (osmo_mcc_from_str(buf, &ucid->plmn.mcc))
- return -EINVAL;
- pos = end + 1;
-
- /* read MNC -- here the number of leading zeros matters. */
- end = strchr(pos, '-');
- if (!end || end == pos || (end - pos) >= sizeof(buf))
- return -EINVAL;
- osmo_strlcpy(buf, pos, end - pos + 1);
- if (osmo_mnc_from_str(buf, &ucid->plmn.mnc, &ucid->plmn.mnc_3_digits))
- return -EINVAL;
- pos = end + 1;
-
- /* parse the rest, where leading zeros do not matter */
- rc = sscanf(pos, "L%" SCNu16 "-R%" SCNu8 "-S%" SCNu16
"-C%" SCNu32 "",
- &ucid->lac, &ucid->rac, &ucid->sac, &ucid->cid);
- if (rc < 0)
- return -errno;
-
- if (rc != 4)
- return -EINVAL;
-
- if (ucid->lac == 0 || ucid->lac == 0xffff)
- return -EINVAL;
-
- /* CellIdentity in the ASN.1 syntax is a bit-string of 28 bits length */
- if (ucid->cid >= (1 << 28))
- return -EINVAL;
-
- return 0;
-}
-
-const char *hnb_context_name(struct hnb_context *ctx)
-{
- char *result;
- if (!ctx)
- return "NULL";
-
- if (ctx->conn) {
- char hostbuf_r[INET6_ADDRSTRLEN];
- char portbuf_r[6];
- int fd = osmo_stream_srv_get_ofd(ctx->conn)->fd;
-
- /* get remote addr */
- if (osmo_sock_get_ip_and_port(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r,
sizeof(portbuf_r), false) == 0)
- result = talloc_asprintf(OTC_SELECT, "%s:%s", hostbuf_r, portbuf_r);
- else
- result = "?";
- } else {
- result = "disconnected";
- }
-
- if (g_hnbgw->config.log_prefix_hnb_id)
- result = talloc_asprintf(OTC_SELECT, "%s %s", result,
ctx->identity_info);
- else
- result = talloc_asprintf(OTC_SELECT, "%s %s", result,
umts_cell_id_to_str(&ctx->id));
- return result;
-}
-
-void hnb_context_release_ue_state(struct hnb_context *ctx)
-{
- struct hnbgw_context_map *map, *map2;
-
- /* deactivate all context maps */
- llist_for_each_entry_safe(map, map2, &ctx->map_list, hnb_list) {
- context_map_hnb_released(map);
- /* hnbgw_context_map will remove itself from lists when it is ready. */
- }
-}
-
-void hnb_context_release(struct hnb_context *ctx)
-{
- struct hnbgw_context_map *map;
-
- LOGHNB(ctx, DMAIN, LOGL_INFO, "Releasing HNB context\n");
-
- if (ctx->persistent) {
- struct timespec tp;
- int rc;
- rc = osmo_clock_gettime(CLOCK_MONOTONIC, &tp);
- ctx->persistent->updowntime = (rc < 0) ? 0 : tp.tv_sec;
- }
-
- /* remove from the list of HNB contexts */
- llist_del(&ctx->list);
-
- hnb_context_release_ue_state(ctx);
-
- if (ctx->conn) { /* we own a conn, we must free it: */
- LOGHNB(ctx, DMAIN, LOGL_INFO, "Closing HNB SCTP connection %s\n",
- osmo_sock_get_name2(osmo_stream_srv_get_ofd(ctx->conn)->fd));
- /* Avoid our closed_cb calling hnb_context_release() again: */
- osmo_stream_srv_set_data(ctx->conn, NULL);
- osmo_stream_srv_destroy(ctx->conn);
- } /* else: we are called from closed_cb, so conn is being freed separately */
-
- /* hnbgw_context_map are still listed in ctx->map_list, but we are freeing ctx.
Remove all entries from the
- * list, but keep the hnbgw_context_map around for graceful release. They are also
listed under
- * hnbgw_cnlink->map_list, and will remove themselves when ready. */
- while ((map = llist_first_entry_or_null(&ctx->map_list, struct hnbgw_context_map,
hnb_list))) {
- llist_del(&map->hnb_list);
- map->hnb_ctx = NULL;
- }
-
- /* remove back reference from hnb_persistent to context */
- if (ctx->persistent)
- hnb_persistent_deregistered(ctx->persistent);
-
- talloc_free(ctx);
-}
-
-/***********************************************************************
- * HNB Persistent Data
- ***********************************************************************/
-
-const struct rate_ctr_desc hnb_ctr_description[] = {
- [HNB_CTR_IUH_ESTABLISHED] = {
- "iuh:established", "Number of times Iuh link was established" },
-
- [HNB_CTR_RANAP_PS_ERR_IND_UL] = {
- "ranap:ps:error_ind:ul", "Received ERROR Indications in Uplink (PS
Domain)" },
- [HNB_CTR_RANAP_CS_ERR_IND_UL] = {
- "ranap:cs:error_ind:ul", "Received ERROR Indications in Uplink (PS
Domain)" },
-
- [HNB_CTR_RANAP_PS_RESET_REQ_UL] = {
- "ranap:ps:reset_req:ul", "Received RESET Requests in Uplink (PS
Domain)" },
- [HNB_CTR_RANAP_CS_RESET_REQ_UL] = {
- "ranap:cs:reset_req:ul", "Received RESET Requests in Uplink (CS
Domain)" },
-
-
- [HNB_CTR_RANAP_PS_RAB_ACT_REQ] = {
- "ranap:ps:rab_act:req", "PS RAB Activations requested" },
- [HNB_CTR_RANAP_CS_RAB_ACT_REQ] = {
- "ranap:cs:rab_act:req", "CS RAB Activations requested" },
-
- [HNB_CTR_RANAP_PS_RAB_ACT_CNF] = {
- "ranap:ps:rab_act:cnf", "PS RAB Activations confirmed" },
- [HNB_CTR_RANAP_CS_RAB_ACT_CNF] = {
- "ranap:cs:rab_act:cnf", "CS RAB Activations confirmed" },
-
- [HNB_CTR_RANAP_PS_RAB_ACT_FAIL] = {
- "ranap:ps:rab_act:fail", "PS RAB Activations failed" },
- [HNB_CTR_RANAP_CS_RAB_ACT_FAIL] = {
- "ranap:cs:rab_act:fail", "CS RAB Activations failed" },
-
-
- [HNB_CTR_RANAP_PS_RAB_MOD_REQ] = {
- "ranap:ps:rab_mod:req", "PS RAB Modifications requested" },
- [HNB_CTR_RANAP_CS_RAB_MOD_REQ] = {
- "ranap:cs:rab_mod:req", "CS RAB Modifications requested" },
-
- [HNB_CTR_RANAP_PS_RAB_MOD_CNF] = {
- "ranap:ps:rab_mod:cnf", "PS RAB Modifications confirmed" },
- [HNB_CTR_RANAP_CS_RAB_MOD_CNF] = {
- "ranap:cs:rab_mod:cnf", "CS RAB Modifications confirmed" },
-
- [HNB_CTR_RANAP_PS_RAB_MOD_FAIL] = {
- "ranap:ps:rab_mod:fail", "PS RAB Modifications failed" },
- [HNB_CTR_RANAP_CS_RAB_MOD_FAIL] = {
- "ranap:cs:rab_mod:fail", "CS RAB Modifications failed" },
-
- [HNB_CTR_RANAP_PS_RAB_REL_REQ] = {
- "ranap:ps:rab_rel:req:normal", "PS RAB Release requested (by CN),
normal" },
- [HNB_CTR_RANAP_CS_RAB_REL_REQ] = {
- "ranap:cs:rab_rel:req:normal", "CS RAB Release requested (by CN),
normal" },
- [HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL] = {
- "ranap:ps:rab_rel:req:abnormal", "PS RAB Release requested (by CN),
abnormal" },
- [HNB_CTR_RANAP_CS_RAB_REL_REQ_ABNORMAL] = {
- "ranap:cs:rab_rel:req:abnormal", "CS RAB Release requested (by CN),
abnormal" },
-
- [HNB_CTR_RANAP_PS_RAB_REL_CNF] = {
- "ranap:ps:rab_rel:cnf", "PS RAB Release confirmed" },
- [HNB_CTR_RANAP_CS_RAB_REL_CNF] = {
- "ranap:cs:rab_rel:cnf", "CS RAB Release confirmed" },
-
- [HNB_CTR_RANAP_PS_RAB_REL_FAIL] = {
- "ranap:ps:rab_rel:fail", "PS RAB Release failed" },
- [HNB_CTR_RANAP_CS_RAB_REL_FAIL] = {
- "ranap:cs:rab_rel:fail", "CS RAB Release failed" },
-
- [HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT] = {
- "ranap:ps:rab_rel:implicit:normal", "PS RAB Release implicit (during Iu
Release), normal" },
- [HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT] = {
- "ranap:cs:rab_rel:implicit:normal", "CS RAB Release implicit (during Iu
Release), normal" },
- [HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL] = {
- "ranap:ps:rab_rel:implicit:abnormal", "PS RAB Release implicit (during
Iu Release), abnormal" },
- [HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT_ABNORMAL] = {
- "ranap:cs:rab_rel:implicit:abnormal", "CS RAB Release implicit (during
Iu Release), abnormal" },
-
- [HNB_CTR_RUA_ERR_IND] = {
- "rua:error_ind", "Received RUA Error Indications" },
-
- [HNB_CTR_RUA_PS_CONNECT_UL] = {
- "rua:ps:connect:ul", "Received RUA Connect requests (PS Domain)"
},
- [HNB_CTR_RUA_CS_CONNECT_UL] = {
- "rua:cs:connect:ul", "Received RUA Connect requests (CS Domain)"
},
-
- [HNB_CTR_RUA_PS_DISCONNECT_UL] = {
- "rua:ps:disconnect:ul", "Received RUA Disconnect requests in uplink (PS
Domain)" },
- [HNB_CTR_RUA_CS_DISCONNECT_UL] = {
- "rua:cs:disconnect:ul", "Received RUA Disconnect requests in uplink (CS
Domain)" },
- [HNB_CTR_RUA_PS_DISCONNECT_DL] = {
- "rua:ps:disconnect:dl", "Transmitted RUA Disconnect requests in downlink
(PS Domain)" },
- [HNB_CTR_RUA_CS_DISCONNECT_DL] = {
- "rua:cs:disconnect:dl", "Transmitted RUA Disconnect requests in downlink
(CS Domain)" },
-
- [HNB_CTR_RUA_PS_DT_UL] = {
- "rua:ps:direct_transfer:ul", "Received RUA DirectTransfer in uplink (PS
Domain)" },
- [HNB_CTR_RUA_CS_DT_UL] = {
- "rua:cs:direct_transfer:ul", "Received RUA DirectTransfer in uplink (CS
Domain)" },
- [HNB_CTR_RUA_PS_DT_DL] = {
- "rua:ps:direct_transfer:dl", "Transmitted RUA DirectTransfer in downlink
(PS Domain)" },
- [HNB_CTR_RUA_CS_DT_DL] = {
- "rua:cs:direct_transfer:dl", "Transmitted RUA DirectTransfer in downlink
(CS Domain)" },
-
- [HNB_CTR_RUA_UDT_UL] = {
- "rua:unit_data:ul", "Received RUA UnitData (UDT) in uplink" },
- [HNB_CTR_RUA_UDT_DL] = {
- "rua:unit_data:dl", "Transmitted RUA UnitData (UDT) in downlink"
},
-
- [HNB_CTR_PS_PAGING_ATTEMPTED] = {
- "paging:ps:attempted", "Transmitted PS Paging requests" },
- [HNB_CTR_CS_PAGING_ATTEMPTED] = {
- "paging:cs:attempted", "Transmitted CS Paging requests" },
-
- [HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL] = {
- "rab:cs:active_milliseconds:total", "Cumulative number of milliseconds
of CS RAB activity" },
-
- [HNB_CTR_DTAP_CS_LU_REQ] = { "dtap:cs:location_update:req", "CS Location
Update Requests" },
- [HNB_CTR_DTAP_CS_LU_ACC] = { "dtap:cs:location_update:accept", "CS
Location Update Accepts" },
- [HNB_CTR_DTAP_CS_LU_REJ] = { "dtap:cs:location_update:reject", "CS
Location Update Rejects" },
-
- [HNB_CTR_DTAP_PS_ATT_REQ] = { "dtap:ps:attach:req", "PS Attach
Requests" },
- [HNB_CTR_DTAP_PS_ATT_ACK] = { "dtap:ps:attach:accept", "PS Attach
Accepts" },
- [HNB_CTR_DTAP_PS_ATT_REJ] = { "dtap:ps:attach:reject", "PS Attach
Rejects" },
-
- [HNB_CTR_DTAP_PS_RAU_REQ] = { "dtap:ps:routing_area_update:req", "PS
Routing Area Update Requests" },
- [HNB_CTR_DTAP_PS_RAU_ACK] = { "dtap:ps:routing_area_update:accept", "PS
Routing Area Update Accepts" },
- [HNB_CTR_DTAP_PS_RAU_REJ] = { "dtap:ps:routing_area_update:reject", "PS
Routing Area Update Rejects" },
-
- [HNB_CTR_GTPU_PACKETS_UL] = {
- "gtpu:packets:ul",
- "Count of GTP-U packets received from the HNB",
- },
- [HNB_CTR_GTPU_TOTAL_BYTES_UL] = {
- "gtpu:total_bytes:ul",
- "Count of total GTP-U bytes received from the HNB, including the GTP-U/UDP/IP
headers",
- },
- [HNB_CTR_GTPU_UE_BYTES_UL] = {
- "gtpu:ue_bytes:ul",
- "Assuming an IP header length of 20 bytes, GTP-U bytes received from the HNB,
excluding the GTP-U/UDP/IP headers",
- },
- [HNB_CTR_GTPU_PACKETS_DL] = {
- "gtpu:packets:dl",
- "Count of GTP-U packets sent to the HNB",
- },
- [HNB_CTR_GTPU_TOTAL_BYTES_DL] = {
- "gtpu:total_bytes:dl",
- "Count of total GTP-U bytes sent to the HNB, including the GTP-U/UDP/IP
headers",
- },
- [HNB_CTR_GTPU_UE_BYTES_DL] = {
- "gtpu:ue_bytes:dl",
- "Assuming an IP header length of 20 bytes, GTP-U bytes sent to the HNB, excluding
the GTP-U/UDP/IP headers",
- },
-
-};
-
-const struct rate_ctr_group_desc hnb_ctrg_desc = {
- "hnb",
- "hNodeB",
- OSMO_STATS_CLASS_GLOBAL,
- ARRAY_SIZE(hnb_ctr_description),
- hnb_ctr_description,
-};
-
-const struct osmo_stat_item_desc hnb_stat_desc[] = {
- [HNB_STAT_UPTIME_SECONDS] = { "uptime:seconds", "Seconds of uptime",
"s", 60, 0 },
-};
-
-const struct osmo_stat_item_group_desc hnb_statg_desc = {
- .group_name_prefix = "hnb",
- .group_description = "hNodeB",
- .class_id = OSMO_STATS_CLASS_GLOBAL,
- .num_items = ARRAY_SIZE(hnb_stat_desc),
- .item_desc = hnb_stat_desc,
-};
-
-static void hnb_persistent_disconnected_timeout_cb(void *data)
-{
- hnb_persistent_free(data);
-}
-
-static void hnb_persistent_disconnected_timeout_schedule(struct hnb_persistent *hnbp)
-{
- unsigned long period_s = osmo_tdef_get(hnbgw_T_defs, -35, OSMO_TDEF_S, 60*60*24*7);
- if (period_s < 1) {
- LOG_HNBP(hnbp, LOGL_INFO,
- "timer X35 is zero, not setting a disconnected timeout for this hnb-persistent
instance.\n");
- return;
- }
- /* It is fine if the timer is already active, osmo_timer_del() is done implicitly by the
osmo_timer API. */
- osmo_timer_setup(&hnbp->disconnected_timeout,
hnb_persistent_disconnected_timeout_cb, hnbp);
- osmo_timer_schedule(&hnbp->disconnected_timeout, period_s, 0);
-}
-
-struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id)
-{
- struct hnb_persistent *hnbp = talloc_zero(g_hnbgw, struct hnb_persistent);
- if (!hnbp)
- return NULL;
-
- hnbp->id = *id;
- hnbp->id_str = talloc_strdup(hnbp, umts_cell_id_to_str(id));
- hnbp->ctrs = rate_ctr_group_alloc(hnbp, &hnb_ctrg_desc, 0);
- if (!hnbp->ctrs)
- goto out_free;
- rate_ctr_group_set_name(hnbp->ctrs, hnbp->id_str);
- hnbp->statg = osmo_stat_item_group_alloc(hnbp, &hnb_statg_desc, 0);
- if (!hnbp->statg)
- goto out_free_ctrs;
- osmo_stat_item_group_set_name(hnbp->statg, hnbp->id_str);
-
- llist_add(&hnbp->list, &g_hnbgw->hnb_persistent_list);
- hash_add(g_hnbgw->hnb_persistent_by_id, &hnbp->node_by_id,
umts_cell_id_hash(&hnbp->id));
-
- if (g_hnbgw->nft_kpi.active)
- nft_kpi_hnb_persistent_add(hnbp);
-
- /* Normally the disconnected timer runs only when the hNodeB is not currently connected
on Iuh. This here is paranoia:
- * In case we have to HNBAP HNB Register Reject, the disconnected timer should be active
on this unused hnbp.
- * On success, hnb_persistent_registered() will stop the disconnected timer directly
after this. */
- hnb_persistent_disconnected_timeout_schedule(hnbp);
-
- return hnbp;
-
-out_free_ctrs:
- rate_ctr_group_free(hnbp->ctrs);
-out_free:
- talloc_free(hnbp);
- return NULL;
-}
-
-struct hnb_persistent *hnb_persistent_find_by_id(const struct umts_cell_id *id)
-{
- struct hnb_persistent *hnbp;
- uint32_t id_hash = umts_cell_id_hash(id);
- hash_for_each_possible (g_hnbgw->hnb_persistent_by_id, hnbp, node_by_id, id_hash) {
- if (umts_cell_id_equal(&hnbp->id, id))
- return hnbp;
- }
- return NULL;
-}
-
-/* Read the peer's remote IP address from the Iuh conn's fd, and set up GTP-U
counters for that remote address. */
-static void hnb_persistent_update_remote_addr(struct hnb_persistent *hnbp)
-{
- socklen_t socklen;
- struct osmo_sockaddr osa;
- struct osmo_sockaddr_str remote_str;
- int fd;
-
- fd = osmo_stream_srv_get_fd(hnbp->ctx->conn);
- if (fd < 0) {
- LOG_HNBP(hnbp, LOGL_ERROR, "no active socket fd, cannot set up traffic
counters\n");
- return;
- }
-
- socklen = sizeof(struct osmo_sockaddr);
- if (getpeername(fd, &osa.u.sa, &socklen)) {
- LOG_HNBP(hnbp, LOGL_ERROR, "cannot read remote address, cannot set up traffic
counters\n");
- return;
- }
- if (osmo_sockaddr_str_from_osa(&remote_str, &osa)) {
- LOG_HNBP(hnbp, LOGL_ERROR, "cannot parse remote address, cannot set up traffic
counters\n");
- return;
- }
-
- /* We got the remote address from the Iuh link (RUA), and now we are blatantly assuming
that the hNodeB has its
- * GTP endpoint on the same IP address, just with UDP port 2152 (the fixed GTP port as
per 3GPP spec). */
- remote_str.port = 2152;
-
- if (nft_kpi_hnb_start(hnbp, &remote_str))
- LOG_HNBP(hnbp, LOGL_ERROR, "failed to set up traffic counters\n");
-}
-
-/* Whenever HNBAP registers a HNB, hnbgw_hnbap.c calls this function to let the
hnb_persistent update its state to the
- * (new) remote address being active. When calling this function, a hnbp->ctx should
be present, with an active
- * osmo_stream_srv conn. */
-void hnb_persistent_registered(struct hnb_persistent *hnbp)
-{
- if (!hnbp->ctx) {
- LOG_HNBP(hnbp, LOGL_ERROR, "hnb_persistent_registered() invoked, but there is no
hnb_ctx\n");
- return;
- }
-
- /* The hNodeB is now connected, i.e. not disconnected. */
- osmo_timer_del(&hnbp->disconnected_timeout);
-
- /* start counting traffic */
- if (g_hnbgw->nft_kpi.active)
- hnb_persistent_update_remote_addr(hnbp);
-}
-
-/* Whenever a HNB is regarded as no longer registered (HNBAP HNB De-Register, or the Iuh
link drops), this function is
- * called to to let the hnb_persistent update its state to the hNodeB being disconnected.
Clear the ctx->persistent and
- * hnbp->ctx relations; do not delete the hnb_persistent instance. */
-void hnb_persistent_deregistered(struct hnb_persistent *hnbp)
-{
- /* clear out cross references of hnb_context and hnb_persistent */
- if (hnbp->ctx) {
- if (hnbp->ctx->persistent == hnbp)
- hnbp->ctx->persistent = NULL;
- hnbp->ctx = NULL;
- }
-
- /* stop counting traffic */
- nft_kpi_hnb_stop(hnbp);
-
- /* The hNodeB is now disconnected. Clear out hnb_persistent when the disconnected
timeout has passed. */
- hnb_persistent_disconnected_timeout_schedule(hnbp);
-}
-
-void hnb_persistent_free(struct hnb_persistent *hnbp)
-{
- /* FIXME: check if in use? */
- osmo_timer_del(&hnbp->disconnected_timeout);
- nft_kpi_hnb_stop(hnbp);
- nft_kpi_hnb_persistent_remove(hnbp);
- osmo_stat_item_group_free(hnbp->statg);
- rate_ctr_group_free(hnbp->ctrs);
- llist_del(&hnbp->list);
- hash_del(&hnbp->node_by_id);
- talloc_free(hnbp);
-}
-
-/* return the amount of time the HNB is up (hnbp->ctx != NULL) or down (hnbp->ctx
== NULL) */
-static unsigned long long hnbp_get_updowntime(const struct hnb_persistent *hnbp)
-{
- struct timespec tp;
-
- if (!hnbp->updowntime)
- return 0;
-
- if (osmo_clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
- return 0;
-
- return difftime(tp.tv_sec, hnbp->updowntime);
-}
-
-unsigned long long hnb_get_updowntime(const struct hnb_context *ctx)
-{
- if (!ctx->persistent)
- return 0;
- return hnbp_get_updowntime(ctx->persistent);
-}
-
-/* timer call-back: Update the HNB_STAT_UPTIME_SECONDS stat item of each hnb_persistent
*/
-static void hnbgw_store_hnb_uptime(void *data)
-{
- struct hnb_persistent *hnbp;
-
- llist_for_each_entry(hnbp, &g_hnbgw->hnb_persistent_list, list) {
- HNBP_STAT_SET(hnbp, HNB_STAT_UPTIME_SECONDS, hnbp->ctx != NULL ?
hnbp_get_updowntime(hnbp) : 0);
- }
-
- osmo_timer_schedule(&g_hnbgw->store_uptime_timer, STORE_UPTIME_INTERVAL, 0);
-}
-
-/***********************************************************************
- * SCTP Socket / stream handling
- ***********************************************************************/
-
-static int hnb_read_cb(struct osmo_stream_srv *conn)
-{
- struct hnb_context *hnb = osmo_stream_srv_get_data(conn);
- struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
- struct msgb *msg = msgb_alloc(IUH_MSGB_SIZE, "Iuh rx");
- int rc;
-
- if (!msg)
- return -ENOMEM;
-
- OSMO_ASSERT(hnb);
- /* we store a reference to the HomeNodeB in the msg->dest for the
- * benefit of various downstream processing functions */
- msg->dst = hnb;
-
- rc = osmo_stream_srv_recv(conn, msg);
- /* Notification received */
- if (msgb_sctp_msg_flags(msg) & OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION) {
- union sctp_notification *notif = (union sctp_notification *)msgb_data(msg);
- rc = 0;
- switch (notif->sn_header.sn_type) {
- case SCTP_ASSOC_CHANGE:
- switch (notif->sn_assoc_change.sac_state) {
- case SCTP_COMM_LOST:
- LOGHNB(hnb, DMAIN, LOGL_NOTICE,
- "sctp_recvmsg(%s) = SCTP_COMM_LOST, closing conn\n",
- osmo_sock_get_name2(ofd->fd));
- osmo_stream_srv_destroy(conn);
- rc = -EBADF;
- break;
- case SCTP_RESTART:
- LOGHNB(hnb, DMAIN, LOGL_NOTICE, "HNB SCTP conn RESTARTed, marking as
HNBAP-unregistered\n");
- hnb->hnb_registered = false;
- hnb_context_release_ue_state(hnb);
- /* The tx queue may be quite full after an SCTP RESTART: (SYS#6113)
- * The link may have been flaky (a possible reason for the peer restarting the conn)
and
- * hence the kernel socket Tx queue may be full (no ACKs coming back) and our own
userspace
- * queue may contain plenty of oldish messages to be sent. Since the HNB will
re-register after
- * this, we simply drop all those old messages: */
- osmo_stream_srv_clear_tx_queue(conn);
- break;
- }
- break;
- case SCTP_SHUTDOWN_EVENT:
- LOGHNB(hnb, DMAIN, LOGL_NOTICE,
- "sctp_recvmsg(%s) = SCTP_SHUTDOWN_EVENT, closing conn\n",
- osmo_sock_get_name2(ofd->fd));
- osmo_stream_srv_destroy(conn);
- rc = -EBADF;
- break;
- }
- goto out;
- } else if (rc == -EAGAIN) {
- /* Older versions of osmo_stream_srv_recv() not supporting
- * msgb_sctp_msg_flags() may still return -EAGAIN when an sctp
- * notification is received. */
- rc = 0;
- goto out;
- } else if (rc < 0) {
- LOGHNB(hnb, DMAIN, LOGL_ERROR, "Error during sctp_recvmsg(%s)\n",
- osmo_sock_get_name2(ofd->fd));
- osmo_stream_srv_destroy(conn);
- rc = -EBADF;
- goto out;
- } else if (rc == 0) {
- LOGHNB(hnb, DMAIN, LOGL_NOTICE, "Connection closed sctp_recvmsg(%s) = 0\n",
- osmo_sock_get_name2(ofd->fd));
- osmo_stream_srv_destroy(conn);
- rc = -EBADF;
- goto out;
- } else {
- msgb_put(msg, rc);
- }
-
- switch (msgb_sctp_ppid(msg)) {
- case IUH_PPI_HNBAP:
- hnb->hnbap_stream = msgb_sctp_stream(msg);
- rc = hnbgw_hnbap_rx(hnb, msg);
- break;
- case IUH_PPI_RUA:
- if (!hnb->hnb_registered) {
- LOGHNB(hnb, DMAIN, LOGL_NOTICE, "Discarding RUA as HNB is not
registered\n");
- goto out;
- }
- hnb->rua_stream = msgb_sctp_stream(msg);
- rc = hnbgw_rua_rx(hnb, msg);
- break;
- case IUH_PPI_SABP:
- case IUH_PPI_RNA:
- case IUH_PPI_PUA:
- LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unimplemented SCTP PPID=%lu received\n",
msgb_sctp_ppid(msg));
- rc = 0;
- break;
- default:
- LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unknown SCTP PPID=%lu received\n",
msgb_sctp_ppid(msg));
- rc = 0;
- break;
- }
-
-out:
- msgb_free(msg);
- return rc;
-}
-
-static int hnb_closed_cb(struct osmo_stream_srv *conn)
-{
- struct hnb_context *hnb = osmo_stream_srv_get_data(conn);
- if (!hnb)
- return 0; /* hnb_context is being freed, nothing do be done */
-
- /* hnb: conn became broken, let's release the associated hnb.
- * conn object is being freed after closed_cb(), so unassign it from hnb
- * if available to avoid it freeing it again: */
- hnb->conn = NULL;
- hnb_context_release(hnb);
-
- return 0;
-}
-
-/*! call-back when the listen FD has something to read */
-int hnbgw_rua_accept_cb(struct osmo_stream_srv_link *srv, int fd)
-{
- struct hnb_context *ctx;
-
- LOGP(DMAIN, LOGL_INFO, "New HNB SCTP connection %s\n",
- osmo_sock_get_name2(fd));
-
- ctx = hnb_context_alloc(srv, fd);
- if (!ctx)
- return -ENOMEM;
-
- return 0;
-}
-
-CTRL_CMD_DEFINE_RO(hnb_info, "info");
-static int get_hnb_info(struct ctrl_cmd *cmd, void *data)
-{
- struct hnb_context *hnb = data;
-
- cmd->reply = talloc_strdup(cmd, hnb->identity_info);
-
- return CTRL_CMD_REPLY;
-}
-
-CTRL_CMD_DEFINE_RO(hnbs, "num-hnb");
-static int get_hnbs(struct ctrl_cmd *cmd, void *data)
-{
- cmd->reply = talloc_asprintf(cmd, "%u",
llist_count(&g_hnbgw->hnb_list));
-
- return CTRL_CMD_REPLY;
-}
-
-int hnb_ctrl_cmds_install(void)
-{
- int rc = 0;
-
- rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_hnbs);
- rc |= ctrl_cmd_install(CTRL_NODE_HNB, &cmd_hnb_info);
-
- return rc;
-}
-
-int hnb_ctrl_node_lookup(void *data, vector vline, int *node_type, void **node_data, int
*i)
-{
- const char *token = vector_slot(vline, *i);
- struct hnb_context *hnb;
- long num;
-
- switch (*node_type) {
- case CTRL_NODE_ROOT:
- if (strcmp(token, "hnb") != 0)
- return 0;
-
- (*i)++;
-
- if (!ctrl_parse_get_num(vline, *i, &num))
- return -ERANGE;
-
- hnb = hnb_context_by_id(num);
- if (!hnb)
- return -ENODEV;
-
- *node_data = hnb;
- *node_type = CTRL_NODE_HNB;
- break;
- default:
- return 0;
- }
-
- return 1;
-}
-
int hnbgw_mgw_setup(void)
{
struct mgcp_client *mgcp_client_single;
diff --git a/src/osmo-hnbgw/hnbgw_hnbap.c b/src/osmo-hnbgw/hnbgw_hnbap.c
index 5874457..b291513 100644
--- a/src/osmo-hnbgw/hnbgw_hnbap.c
+++ b/src/osmo-hnbgw/hnbgw_hnbap.c
@@ -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>
diff --git a/src/osmo-hnbgw/hnbgw_ranap.c b/src/osmo-hnbgw/hnbgw_ranap.c
index 6dc1f06..efa46d4 100644
--- a/src/osmo-hnbgw/hnbgw_ranap.c
+++ b/src/osmo-hnbgw/hnbgw_ranap.c
@@ -39,6 +39,8 @@
#include <osmocom/pfcp/pfcp_cp_peer.h>
#endif
+#include <osmocom/hnbgw/hnb.h>
+#include <osmocom/hnbgw/hnb_persistent.h>
#include <osmocom/hnbgw/hnbgw.h>
#include <osmocom/hnbgw/hnbgw_rua.h>
#include <osmocom/hnbgw/hnbgw_cn.h>
diff --git a/src/osmo-hnbgw/hnbgw_rua.c b/src/osmo-hnbgw/hnbgw_rua.c
index 7a51c8d..40e65f4 100644
--- a/src/osmo-hnbgw/hnbgw_rua.c
+++ b/src/osmo-hnbgw/hnbgw_rua.c
@@ -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>
diff --git a/src/osmo-hnbgw/hnbgw_vty.c b/src/osmo-hnbgw/hnbgw_vty.c
index d99a998..e2c3d8f 100644
--- a/src/osmo-hnbgw/hnbgw_vty.c
+++ b/src/osmo-hnbgw/hnbgw_vty.c
@@ -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>
diff --git a/src/osmo-hnbgw/kpi_dtap.c b/src/osmo-hnbgw/kpi_dtap.c
index 8d47208..36c9c91 100644
--- a/src/osmo-hnbgw/kpi_dtap.c
+++ b/src/osmo-hnbgw/kpi_dtap.c
@@ -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>
diff --git a/src/osmo-hnbgw/kpi_ranap.c b/src/osmo-hnbgw/kpi_ranap.c
index 3f2c1b3..c1c77bf 100644
--- a/src/osmo-hnbgw/kpi_ranap.c
+++ b/src/osmo-hnbgw/kpi_ranap.c
@@ -24,6 +24,7 @@
#include <osmocom/ranap/ranap_common_ran.h>
+#include <osmocom/hnbgw/hnb_persistent.h>
#include <osmocom/hnbgw/hnbgw_cn.h>
#include <osmocom/hnbgw/context_map.h>
#include <osmocom/hnbgw/kpi.h>
diff --git a/src/osmo-hnbgw/nft_kpi.c b/src/osmo-hnbgw/nft_kpi.c
index 9dd8608..f8671fc 100644
--- a/src/osmo-hnbgw/nft_kpi.c
+++ b/src/osmo-hnbgw/nft_kpi.c
@@ -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"
diff --git a/src/osmo-hnbgw/osmo_hnbgw_main.c b/src/osmo-hnbgw/osmo_hnbgw_main.c
index 2034490..845002f 100644
--- a/src/osmo-hnbgw/osmo_hnbgw_main.c
+++ b/src/osmo-hnbgw/osmo_hnbgw_main.c
@@ -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>
diff --git a/src/osmo-hnbgw/umts_cell_id.c b/src/osmo-hnbgw/umts_cell_id.c
new file mode 100644
index 0000000..2e2af2d
--- /dev/null
+++ b/src/osmo-hnbgw/umts_cell_id.c
@@ -0,0 +1,107 @@
+/* UMTS Cell ID */
+
+/* (C) 2015,2024 by Harald Welte <laforge(a)gnumonks.org>
+ * (C) 2016-2025 by sysmocom s.f.m.c. GmbH <info(a)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;
+}
diff --git a/tests/ranap_rab_ass/ranap_rab_ass_test.c
b/tests/ranap_rab_ass/ranap_rab_ass_test.c
index 76a3430..a656776 100644
--- a/tests/ranap_rab_ass/ranap_rab_ass_test.c
+++ b/tests/ranap_rab_ass/ranap_rab_ass_test.c
@@ -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;
diff --git a/tests/umts_cell_id/umts_cell_id_test.c
b/tests/umts_cell_id/umts_cell_id_test.c
index 18c176a..0dbdd84 100644
--- a/tests/umts_cell_id/umts_cell_id_test.c
+++ b/tests/umts_cell_id/umts_cell_id_test.c
@@ -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;
--
To view, visit
https://gerrit.osmocom.org/c/osmo-hnbgw/+/40254?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: osmo-hnbgw
Gerrit-Branch: master
Gerrit-Change-Id: I4ff65d14f94d99a57316c2483c92244078bb5dd9
Gerrit-Change-Number: 40254
Gerrit-PatchSet: 2
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>