laforge submitted this change.

View Change


Approvals: laforge: Looks good to me, approved; Verified
Introduce concept of per-HNB persistent data structure

This allows us to add a new "hnb-policy closed", which means we do not
accept any random HNB connection anymore, but only those whose identity
is pre-configured explicitly using administrative means.

Furthermore, this new data structure will allow us (in future patches)
to keep persistent data like uptime / downtime of each HNB.

Related: SYS#6773
Change-Id: Ife89a7a206836bd89334d19d3cf8c92969dd74de
---
M include/osmocom/hnbgw/hnbgw.h
M include/osmocom/hnbgw/vty.h
M src/osmo-hnbgw/hnbgw.c
M src/osmo-hnbgw/hnbgw_hnbap.c
M src/osmo-hnbgw/hnbgw_rua.c
M src/osmo-hnbgw/hnbgw_vty.c
M tests/osmo-hnbgw.vty
7 files changed, 223 insertions(+), 10 deletions(-)

diff --git a/include/osmocom/hnbgw/hnbgw.h b/include/osmocom/hnbgw/hnbgw.h
index 62b548c..864d7ee 100644
--- a/include/osmocom/hnbgw/hnbgw.h
+++ b/include/osmocom/hnbgw/hnbgw.h
@@ -71,16 +71,34 @@
#define IUH_MSGB_SIZE 2048

struct umts_cell_id {
- uint16_t mcc; /*!< Mobile Country Code */
- uint16_t mnc; /*!< Mobile Network Code */
- uint16_t lac; /*!< Locaton Area Code */
- uint16_t rac; /*!< Routing Area Code */
+ uint16_t mcc; /*!< Mobile Country Code (0-999) */
+ uint16_t mnc; /*!< Mobile Network Code (0-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 */
};
const char *umts_cell_id_name(const struct umts_cell_id *ucid);
int umts_cell_id_from_str(struct umts_cell_id *ucid, const char *instr);

+/*! 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 (a->mcc != b->mcc)
+ return false;
+ if (a->mnc != b->mnc)
+ 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.
@@ -234,6 +252,22 @@

/* linked list of hnbgw_context_map */
struct llist_head map_list;
+
+ /*! pointer to the associated hnb persistent state */
+ struct hnb_persistent *persistent;
+};
+
+/* 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;
+ /*! 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;
};

struct ue_context {
@@ -260,6 +294,7 @@
bool hnbap_allow_tmsi;
/*! print hnb-id (true) or MCC-MNC-LAC-RAC-SAC (false) in logs */
bool log_prefix_hnb_id;
+ bool accept_all_hnb;
struct mgcp_client_conf *mgcp_client;
struct {
char *local_addr;
@@ -276,6 +311,8 @@
struct osmo_stream_srv_link *iuh;
/* list of struct hnb_context */
struct llist_head hnb_list;
+ /* list of struct hnb_persistent */
+ struct llist_head hnb_persistent_list;
/* list of struct ue_context */
struct llist_head ue_list;
/* next availble UE Context ID */
@@ -324,6 +361,10 @@
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_free(struct hnb_persistent *hnbp);
+
void hnbgw_vty_init(void);
int hnbgw_vty_go_parent(struct vty *vty);

diff --git a/include/osmocom/hnbgw/vty.h b/include/osmocom/hnbgw/vty.h
index da0c469..9134521 100644
--- a/include/osmocom/hnbgw/vty.h
+++ b/include/osmocom/hnbgw/vty.h
@@ -4,6 +4,7 @@

enum osmo_iuh_vty_node {
HNBGW_NODE = _LAST_OSMOVTY_NODE + 1,
+ HNB_NODE,
IUH_NODE,
IUCS_NODE,
IUPS_NODE,
diff --git a/src/osmo-hnbgw/hnbgw.c b/src/osmo-hnbgw/hnbgw.c
index 366568d..3709274 100644
--- a/src/osmo-hnbgw/hnbgw.c
+++ b/src/osmo-hnbgw/hnbgw.c
@@ -1,6 +1,6 @@
/* kitchen sink for OsmoHNBGW implementation */

-/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2015,2024 by Harald Welte <laforge@gnumonks.org>
* (C) 2016-2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
@@ -294,10 +294,49 @@
map->hnb_ctx = NULL;
}

+ /* remove back reference from hnb_persistent to context */
+ if (ctx->persistent)
+ ctx->persistent->ctx = NULL;
+
talloc_free(ctx);
}

+/***********************************************************************
+ * HNB Persistent Data
+ ***********************************************************************/

+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_name(id));
+
+ llist_add(&hnbp->list, &g_hnbgw->hnb_persistent_list);
+
+ return hnbp;
+}
+
+struct hnb_persistent *hnb_persistent_find_by_id(const struct umts_cell_id *id)
+{
+ struct hnb_persistent *hnbp;
+
+ llist_for_each_entry(hnbp, &g_hnbgw->hnb_persistent_list, list) {
+ if (umts_cell_id_equal(&hnbp->id, id))
+ return hnbp;
+ }
+
+ return NULL;
+}
+
+void hnb_persistent_free(struct hnb_persistent *hnbp)
+{
+ /* FIXME: check if in use? */
+ llist_del(&hnbp->list);
+ talloc_free(hnbp);
+}

/***********************************************************************
* SCTP Socket / stream handling
@@ -610,12 +649,14 @@
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.log_prefix_hnb_id = true;
+ g_hnbgw->config.accept_all_hnb = true;

/* Set zero PLMN to detect a missing PLMN when transmitting RESET */
g_hnbgw->config.plmn = (struct osmo_plmn_id){ 0, 0, false };

g_hnbgw->next_ue_ctx_id = 23;
INIT_LLIST_HEAD(&g_hnbgw->hnb_list);
+ INIT_LLIST_HEAD(&g_hnbgw->hnb_persistent_list);
INIT_LLIST_HEAD(&g_hnbgw->ue_list);
INIT_LLIST_HEAD(&g_hnbgw->sccp.users);

diff --git a/src/osmo-hnbgw/hnbgw_hnbap.c b/src/osmo-hnbgw/hnbgw_hnbap.c
index 06d1a9d..f9ad478 100644
--- a/src/osmo-hnbgw/hnbgw_hnbap.c
+++ b/src/osmo-hnbgw/hnbgw_hnbap.c
@@ -404,21 +404,24 @@

static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
{
+ struct hnb_persistent *hnbp;
struct hnb_context *hnb, *tmp;
HNBAP_HNBRegisterRequestIEs_t ies;
int rc;
struct osmo_plmn_id plmn;
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(ctx->conn);
+ char identity_str[256];
+ const char *cell_id_str;

rc = hnbap_decode_hnbregisterrequesties(&ies, in);
if (rc < 0) {
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Failure to decode HNB-REGISTER-REQ: rc=%d\n", rc);
return rc;
}
+ asn1_strncpy(identity_str, &ies.hnB_Identity.hNB_Identity_Info, sizeof(identity_str));

/* copy all identity parameters from the message to ctx */
- asn1_strncpy(ctx->identity_info, &ies.hnB_Identity.hNB_Identity_Info,
- sizeof(ctx->identity_info));
+ OSMO_STRLCPY_ARRAY(ctx->identity_info, identity_str);
ctx->id.lac = asn1str_to_u16(&ies.lac);
ctx->id.sac = asn1str_to_u16(&ies.sac);
ctx->id.rac = asn1str_to_u8(&ies.rac);
@@ -426,6 +429,19 @@
osmo_plmn_from_bcd(ies.plmNidentity.buf, &plmn);
ctx->id.mcc = plmn.mcc;
ctx->id.mnc = plmn.mnc;
+ cell_id_str = umts_cell_id_name(&ctx->id);
+
+ hnbp = hnb_persistent_find_by_id(&ctx->id);
+ if (!hnbp && g_hnbgw->config.accept_all_hnb)
+ hnbp = hnb_persistent_alloc(&ctx->id);
+
+ if (!hnbp) {
+ LOGHNB(ctx, DHNBAP, LOGL_NOTICE, "Rejecting unknonwn HNB with identity %s\n", identity_str);
+ hnbap_free_hnbregisterrequesties(&ies);
+ return hnbgw_tx_hnb_register_rej(ctx);
+ }
+ ctx->persistent = hnbp;
+

llist_for_each_entry_safe(hnb, tmp, &g_hnbgw->hnb_list, list) {
if (hnb->hnb_registered && ctx != hnb && memcmp(&ctx->id, &hnb->id, sizeof(ctx->id)) == 0) {
@@ -456,15 +472,14 @@

/* If new conn registering same HNB is from anoter remote addr+port, let's reject it to avoid
* misconfigurations or someone trying to impersonate an already working HNB: */
- LOGHNB(ctx, DHNBAP, LOGL_ERROR, "rejecting HNB-REGISTER-REQ with duplicate cell identity %s\n",
- umts_cell_id_name(&ctx->id));
+ LOGHNB(ctx, DHNBAP, LOGL_ERROR, "rejecting HNB-REGISTER-REQ with duplicate cell identity %s\n", cell_id_str);
hnbap_free_hnbregisterrequesties(&ies);
return hnbgw_tx_hnb_register_rej(ctx);
}
}

LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "HNB-REGISTER-REQ %s %s%s\n",
- ctx->identity_info, umts_cell_id_name(&ctx->id), ctx->hnb_registered ? " (re-connecting)" : "");
+ ctx->identity_info, cell_id_str, ctx->hnb_registered ? " (re-connecting)" : "");

/* The HNB is already registered, and we are seeing a new HNB Register Request. The HNB has restarted
* without us noticing. Clearly, the HNB does not expect any UE state to be active here, so discard any
diff --git a/src/osmo-hnbgw/hnbgw_rua.c b/src/osmo-hnbgw/hnbgw_rua.c
index d73f22d..76e3231 100644
--- a/src/osmo-hnbgw/hnbgw_rua.c
+++ b/src/osmo-hnbgw/hnbgw_rua.c
@@ -579,6 +579,10 @@
asn_dec_rval_t dec_ret;
int rc;

+ /* RUA is only processed after HNB registration, and as soon as the HNB is registered,
+ * it should have a persistent config associated with it */
+ OSMO_ASSERT(hnb->persistent);
+
/* decode and handle to _hnbgw_hnbap_rx() */

memset(pdu, 0, sizeof(*pdu));
diff --git a/src/osmo-hnbgw/hnbgw_vty.c b/src/osmo-hnbgw/hnbgw_vty.c
index cb34d7f..1870f5d 100644
--- a/src/osmo-hnbgw/hnbgw_vty.c
+++ b/src/osmo-hnbgw/hnbgw_vty.c
@@ -1,6 +1,7 @@
/* HNB-GW interface to quagga VTY */

/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * (C) 2024 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -53,6 +54,12 @@
return CMD_SUCCESS;
}

+static struct cmd_node hnb_node = {
+ HNB_NODE,
+ "%s(config-hnbgw-hnb)# ",
+ 1,
+};
+
static struct cmd_node iuh_node = {
IUH_NODE,
"%s(config-hnbgw-iuh)# ",
@@ -355,6 +362,19 @@
return CMD_SUCCESS;
}

+DEFUN(cfg_hnbgw_hnb_policy, cfg_hnbgw_hnb_policy_cmd,
+ "hnb-policy (accept-all|closed)",
+ "Configure the policy of which HNB connections to accept\n"
+ "Accept HNB of any identity\n"
+ "Accept only HNB whose identity is explicitly configured via VTY\n")
+{
+ if (!strcmp(argv[0], "accept-all"))
+ g_hnbgw->config.accept_all_hnb = true;
+ else
+ g_hnbgw->config.accept_all_hnb = false;
+ return CMD_SUCCESS;
+}
+
DEFUN_DEPRECATED(cfg_hnbgw_max_sccp_cr_payload_len, cfg_hnbgw_max_sccp_cr_payload_len_cmd,
"sccp cr max-payload-len <0-999999>",
"Configure SCCP behavior\n"
@@ -799,6 +819,58 @@
return CMD_SUCCESS;
}

+#define HNB_STR "hNodeB specific configuration\n"
+
+DEFUN(cfg_hnbgw_hnb, cfg_hnbgw_hnb_cmd,
+ "hnb UMTS_CELL_ID",
+ HNB_STR
+ "Identity of hNodeB in xxx-yyy-Llac-Rrac-Ssac-Ccid format\n")
+{
+ struct umts_cell_id ucid;
+ struct hnb_persistent *hnbp;
+
+ if (umts_cell_id_from_str(&ucid, argv[0])) {
+ vty_out(vty, "%% Invalid UMTS_CELL_ID '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ hnbp = hnb_persistent_find_by_id(&ucid);
+ if (!hnbp)
+ hnbp = hnb_persistent_alloc(&ucid);
+ if (!hnbp) {
+ vty_out(vty, "%% Could not create HNB '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ vty->index = hnbp;
+ vty->node = HNB_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_hnbgw_no_hnb, cfg_hnbgw_no_hnb_cmd,
+ "no hnb IDENTITY_INFO",
+ NO_STR "Remove configuration for specified hNodeB\n"
+ "Identity of hNodeB\n")
+{
+ struct umts_cell_id ucid;
+ struct hnb_persistent *hnbp;
+
+ if (umts_cell_id_from_str(&ucid, argv[0])) {
+ vty_out(vty, "%% Invalid UMTS_CELL_ID '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ hnbp = hnb_persistent_find_by_id(&ucid);
+ if (!hnbp) {
+ vty_out(vty, "%% Could not find any HNB for identity '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ hnb_persistent_free(hnbp);
+ return CMD_SUCCESS;
+}
+
#if ENABLE_PFCP

static struct cmd_node pfcp_node = {
@@ -889,8 +961,15 @@
cnpool_write_nri(vty, cnpool, false);
}

+static void write_one_hnbp(struct vty *vty, const struct hnb_persistent *hnbp)
+{
+ vty_out(vty, " hnb %s%s", hnbp->id_str, VTY_NEWLINE);
+}
+
static int config_write_hnbgw(struct vty *vty)
{
+ const struct hnb_persistent *hnbp;
+
vty_out(vty, "hnbgw%s", VTY_NEWLINE);

if (g_hnbgw->config.plmn.mcc)
@@ -903,8 +982,15 @@

vty_out(vty, " log-prefix %s%s", g_hnbgw->config.log_prefix_hnb_id ? "hnb-id" : "umts-cell-id",
VTY_NEWLINE);
+
+ if (!g_hnbgw->config.accept_all_hnb)
+ vty_out(vty, " hnb-policy closed%s", VTY_NEWLINE);
+
osmo_tdef_vty_groups_write(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);

@@ -1003,6 +1089,7 @@
install_element(HNBGW_NODE, &cfg_hnbgw_rnc_id_cmd);
install_element(HNBGW_NODE, &cfg_hnbgw_log_prefix_cmd);
install_element(HNBGW_NODE, &cfg_hnbgw_max_sccp_cr_payload_len_cmd);
+ install_element(HNBGW_NODE, &cfg_hnbgw_hnb_policy_cmd);

install_element(HNBGW_NODE, &cfg_hnbgw_iuh_cmd);
install_node(&iuh_node, config_write_hnbgw_iuh);
@@ -1027,6 +1114,10 @@
install_element(IUCS_NODE, &cfg_hnbgw_cnpool_remote_addr_cmd);
install_element(IUPS_NODE, &cfg_hnbgw_cnpool_remote_addr_cmd);

+ install_element(HNBGW_NODE, &cfg_hnbgw_hnb_cmd);
+ install_element(HNBGW_NODE, &cfg_hnbgw_no_hnb_cmd);
+ install_node(&hnb_node, NULL);
+
install_element_ve(&show_cnlink_cmd);
install_element_ve(&show_hnb_cmd);
install_element_ve(&show_one_hnb_cmd);
diff --git a/tests/osmo-hnbgw.vty b/tests/osmo-hnbgw.vty
index 41bcb73..8443f86 100644
--- a/tests/osmo-hnbgw.vty
+++ b/tests/osmo-hnbgw.vty
@@ -14,9 +14,12 @@
plmn <1-999> <0-999>
rnc-id <0-65535>
log-prefix (hnb-id|umts-cell-id)
+ hnb-policy (accept-all|closed)
iuh
iucs
iups
+ hnb UMTS_CELL_ID
+ no hnb IDENTITY_INFO
...

OsmoHNBGW(config-hnbgw)# plmn?

To view, visit change 36081. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: osmo-hnbgw
Gerrit-Branch: master
Gerrit-Change-Id: Ife89a7a206836bd89334d19d3cf8c92969dd74de
Gerrit-Change-Number: 36081
Gerrit-PatchSet: 4
Gerrit-Owner: laforge <laforge@osmocom.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge@osmocom.org>
Gerrit-Reviewer: osmith <osmith@sysmocom.de>
Gerrit-Reviewer: pespin <pespin@sysmocom.de>
Gerrit-MessageType: merged