Compare commits

..

9 Commits

Author SHA1 Message Date
Neels Hofmeyr aebe198913 drop legacy hack: do not start MGW endp in loopback mode
We used to tell osmo-mgw to create an IuUP endpoint in loopback mode, in
order to hack it into responding to an IuUP Initialization. The loopback
mode here in osmo-hnbgw is a leftover from that hack. Drop it.

Change-Id: I0eca75d7abf66f8b9fde9c68ec10d4265f64a189
2024-04-30 02:46:20 +02:00
Neels Hofmeyr ab401260ef nft_kpi: retrieve counters in a separate thread
Introduce an NFT thread which does:
- periodically run nftables command to read all counters
- parse the response
- update rate_ctr values.

The main thread still runs the rule addition / removal when a HNB
registers or deregisters. See the comment added in nft_kpi.c, starting
with "A more scalable solution...".

This patch requires the new osmo_stats_report_lock(), see 'Depends'.

Related: SYS#6773
Depends: libosmocore Ib335bea7d2a440ca284e6c439066f96456bf2c2d
Change-Id: I9dc54e6bc94c553f45adfa71ae8ad70be4afbc8f
2024-04-30 02:46:20 +02:00
Neels Hofmeyr 97ba7b152e nft_kpi: add rate_ctr gtpu:ue_bytes:ul,dl
So far we have the nftables based counters for total GTP-U bytes (UL,
DL), as well as a packet count.

Add another counter for the computed UE payload bytes:
  total_bytes - packets * (20 + 8 + 8)

Related: SYS#6773
Change-Id: Ib2f0a9252715ea4b2fe9c367aa65f771357768ca
2024-04-30 02:46:20 +02:00
Neels Hofmeyr 8dcaf21c68 jenkins.sh: add NFTABLES axis
Related: osmo-ci I9828b70708dbe466c37df6ffb87b04362f14c71c
Related: OS#6425
Change-Id: I331cce7b187cf427a5cbffb3aedb17054918bcc8
2024-04-30 02:46:20 +02:00
Neels Hofmeyr d7d4ca7b83 per-HNB GTP-U traffic counters via nft
Add optional feature: retrieve GTP-U traffic counters using nftables.

Add compile-time switch --enable-nftables, to build with/without
external dependency libnftables. Default is without, as before.

Add configure-time switch 'hnbgw' / 'nft-kpi' to enable use of nftables.
This requires osmo-hnbgw to be run with cap_net_admin.

The VTY config commands are always visible -- simplifies VTY testing.
Refuse to start osmo-hnbgw when requesting nft-kpi in the config but
when built without --enable-nftables.

When an hNodeB registers, set up nftables rules to count GTP-U packets
(UDP port 2152) to and from that hNodeB's address -- we are assuming
that it is the same address that Iuh is connecting from.

This is a "workaround" to get performance indicators per hNodeB, without
needing a UPF that supports URR.

This patch reads counters from the nftables response using "manual"
string parsing. See also Id4e7fa017c31945388a010d8581715d71482116b which
modifies this to full JSON parsing.

Tweaked-by: osmith
Related: SYS#6773
Depends: libosmocore I0df84b4bb8cb5d8434b735fa3a38e7f95be43e91
Change-Id: I35b7e97fd039e36633dfde1317170527c82f9f68
2024-04-30 02:46:20 +02:00
Max 7449635520 .deb/.rpm: add osmocom user during package install
Create osmocom user & group during package installation.
Fix the configuration dir/files permission to match.

Related: OS#4107
Tweaked-By: Oliver Smith <osmith@sysmocom.de>
Change-Id: Ife9433291ae03392ae114ebda418bce8cc93fe3b
2024-04-24 11:52:19 +02:00
Pau Espin 8fd95f3e74 hnbap: Avoid calling duplicate getpeername() for each registered HNB
Change-Id: I980de31d1296c3b956146a461609bec76ed3d430
2024-04-16 14:42:37 +02:00
Harald Welte cd58308915 counters: Distinguish between normal and abnormal release cause
It is interesting to know if a release was normal (as expected/requested
by the NAS layer, typically indicating a user-requested call end) or
abnormal (radio failure, pre-emption or whatever other event that the
user did not expect).

Related: SYS#6773
Change-Id: Idd2f845b7db448064b693ac1efdc8db006a47a11
2024-04-09 08:12:03 +00:00
Harald Welte e3cc5ddf1d HNBAP: Support IMSI identity type in hnbgw_tx_ue_register_rej()
Change-Id: I2e00968cbf686f78f5c9655e899963f2b84dd78b
2024-03-29 12:00:06 +01:00
10 changed files with 121 additions and 26 deletions

View File

@ -68,19 +68,32 @@ make %{?_smp_mflags}
%install
%make_install
%if 0%{?suse_version}
%preun
%if 0%{?suse_version}
%service_del_preun %{name}.service
%endif
%postun
%if 0%{?suse_version}
%service_del_postun %{name}.service
%endif
%pre
getent group osmocom >/dev/null || groupadd --system osmocom
getent passwd osmocom >/dev/null || useradd --system --gid osmocom --home-dir /var/lib/osmocom \
--shell /sbin/nologin --comment "Open Source Mobile Communications" osmocom
%if 0%{?suse_version}
%service_add_pre %{name}.service
%endif
%post
%if 0%{?suse_version}
%service_add_post %{name}.service
%endif
chown osmocom:osmocom /etc/osmocom/osmo-hnbgw.cfg
chmod 0660 /etc/osmocom/osmo-hnbgw.cfg
chown root:osmocom /etc/osmocom
chmod 2775 /etc/osmocom
%check
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)

View File

@ -9,6 +9,8 @@ Restart=always
LimitNOFILE=65536
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-hnbgw -c /etc/osmocom/osmo-hnbgw.cfg
RestartSec=2

2
debian/control vendored
View File

@ -32,7 +32,7 @@ Homepage: https://projects.osmocom.org/projects/osmo-hnbgw
Package: osmo-hnbgw
Architecture: any
Multi-Arch: foreign
Depends: ${misc:Depends}, ${shlibs:Depends}
Depends: ${misc:Depends}, ${shlibs:Depends}, adduser
Recommends: osmo-mgw
Description: OsmoHNBGW: Osmocom Home Node B Gateway

39
debian/postinst vendored Executable file
View File

@ -0,0 +1,39 @@
#!/bin/sh -e
# Create 'osmocom' user and group (if it doesn't exist yet) and adjust permissions
# of directories which are not automatically adjusted by systemd from previous (root-owned)
# install.
# N. B: the user is intentionally NOT removed during package uninstall:
# see https://wiki.debian.org/AccountHandlingInMaintainerScripts for reasoning.
chperms() {
# chperms <user> <group> <perms> <file>
if ! OVERRIDE=`dpkg-statoverride --list $4 2>&1`; then
if [ -e $4 ]; then
chown $1:$2 $4
chmod $3 $4
fi
fi
}
case "$1" in
configure)
if ! getent passwd osmocom > /dev/null; then
adduser --quiet \
--system \
--group \
--no-create-home \
--disabled-password \
--home /var/lib/osmocom \
--gecos "Open Source Mobile Communications" \
osmocom
fi
# Set permissions according to https://www.debian.org/doc/debian-policy/ch-files.html#s-permissions-owners
chperms osmocom osmocom 0660 /etc/osmocom/osmo-hnbgw.cfg
chperms root osmocom 2775 /etc/osmocom
;;
esac
# dh_installdeb(1) will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#

View File

@ -104,6 +104,8 @@ enum hnb_rate_ctr {
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,
@ -113,6 +115,8 @@ enum hnb_rate_ctr {
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,

View File

@ -400,11 +400,14 @@ const struct rate_ctr_desc hnb_ctr_description[] = {
[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", "PS RAB Release requested" },
"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", "CS RAB Release requested" },
"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" },
@ -417,9 +420,13 @@ const struct rate_ctr_desc hnb_ctr_description[] = {
"ranap:cs:rab_rel:fail", "CS RAB Release failed" },
[HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT] = {
"ranap:ps:rab_rel:implicit", "PS RAB Release implicit (during Iu Release)" },
"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", "CS RAB Release implicit (during Iu Release)" },
"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" },

View File

@ -211,6 +211,7 @@ static int hnbgw_tx_ue_register_rej(struct hnb_context *hnb, HNBAP_UE_Identity_t
{
HNBAP_UERegisterReject_t reject_out;
HNBAP_UERegisterRejectIEs_t reject;
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
struct msgb *msg;
int rc;
@ -271,6 +272,14 @@ static int hnbgw_tx_ue_register_rej(struct hnb_context *hnb, HNBAP_UE_Identity_t
ue_id->choice.pTMSIRAI.rAI.rAC.size);
break;
case HNBAP_UE_Identity_PR_iMSI:
ranap_bcd_decode(imsi, sizeof(imsi), ue_id->choice.iMSI.buf, ue_id->choice.iMSI.size);
LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "REJ UE_Id IMSI %s\n", imsi);
OCTET_STRING_fromBuf(&reject.uE_Identity.choice.iMSI,
(const char *)ue_id->choice.iMSI.buf, ue_id->choice.iMSI.size);
break;
default:
LOGHNB(hnb, DHNBAP, LOGL_ERROR, "Cannot compose UE Register Reject:"
" unsupported UE ID (present=%d)\n", ue_id->present);
@ -312,6 +321,9 @@ static int hnbgw_tx_ue_register_rej(struct hnb_context *hnb, HNBAP_UE_Identity_t
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
&reject.uE_Identity.choice.pTMSIRAI.rAI.rAC);
break;
case HNBAP_UE_Identity_PR_iMSI:
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
&reject.uE_Identity.choice.iMSI);
default:
/* should never happen after above switch() */
@ -466,6 +478,8 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
const char *cell_id_str;
struct timespec tp;
HNBAP_Cause_t cause;
struct osmo_sockaddr cur_osa = {};
socklen_t len = sizeof(cur_osa);
rc = hnbap_decode_hnbregisterrequesties(&ies, in);
if (rc < 0) {
@ -487,6 +501,15 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
ctx->id.mnc = plmn.mnc;
cell_id_str = umts_cell_id_name(&ctx->id);
if (getpeername(ofd->fd, &cur_osa.u.sa, &len) < 0) {
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "HNB-REGISTER-REQ %s: rejecting due to getpeername() error: %s\n",
cell_id_str, strerror(errno));
hnbap_free_hnbregisterrequesties(&ies);
cause.present = HNBAP_Cause_PR_radioNetwork;
cause.choice.radioNetwork = HNBAP_CauseRadioNetwork_hNB_parameter_mismatch;
return hnbgw_tx_hnb_register_rej(ctx, &cause);
}
hnbp = hnb_persistent_find_by_id(&ctx->id);
if (!hnbp && g_hnbgw->config.accept_all_hnb)
hnbp = hnb_persistent_alloc(&ctx->id);
@ -498,6 +521,7 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
cause.choice.radioNetwork = HNBAP_CauseRadioNetwork_unauthorised_HNB;
return hnbgw_tx_hnb_register_rej(ctx, &cause);
}
ctx->persistent = hnbp;
hnbp->ctx = ctx;
@ -511,22 +535,13 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
* fault (bug), and we release the old context to keep going... */
struct osmo_fd *other_fd = osmo_stream_srv_get_ofd(hnb->conn);
struct osmo_sockaddr other_osa = {};
struct osmo_sockaddr cur_osa = {};
socklen_t len = sizeof(other_osa);
if (getpeername(other_fd->fd, &other_osa.u.sa, &len) < 0) {
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "BUG! Found old registered HNB with invalid socket, releasing it\n");
hnb_context_release(hnb);
continue;
}
len = sizeof(cur_osa);
if (getpeername(ofd->fd, &cur_osa.u.sa, &len) < 0) {
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Error getpeername(): %s\n", strerror(errno));
if (osmo_sockaddr_cmp(&cur_osa, &other_osa) == 0) {
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "BUG! Found old registered HNB with same remote address, releasing it\n");
hnb_context_release(hnb);
continue;
}
} else if (osmo_sockaddr_cmp(&cur_osa, &other_osa) == 0) {
if (osmo_sockaddr_cmp(&cur_osa, &other_osa) == 0) {
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "BUG! Found old registered HNB with same remote address, releasing it\n");
hnb_context_release(hnb);
continue;

View File

@ -36,14 +36,26 @@
static void kpi_ranap_process_dl_iu_rel_cmd(struct hnbgw_context_map *map, const ranap_message *ranap)
{
struct hnb_persistent *hnbp = map->hnb_ctx->persistent;
const RANAP_Cause_t *cause;
OSMO_ASSERT(ranap->procedureCode == RANAP_ProcedureCode_id_Iu_Release);
cause = &ranap->msg.iu_ReleaseCommandIEs.cause;
/* When Iu is released, all RABs are released implicitly */
for (unsigned int i = 0; i < ARRAY_SIZE(map->rab_state); i++) {
unsigned int ctr_num;
switch (map->rab_state[i]) {
case RAB_STATE_ACTIVE:
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT : HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT);
if (cause->present == RANAP_Cause_PR_nAS ||
cause->choice.nAS == RANAP_CauseNAS_normal_release) {
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT;
} else {
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL;
}
if (!map->is_ps)
ctr_num++;
HNBP_CTR_INC(hnbp, ctr_num);
break;
}
}
@ -106,15 +118,12 @@ static void kpi_ranap_process_dl_rab_ass_req(struct hnbgw_context_map *map, rana
if (ies->presenceMask & RAB_ASSIGNMENTREQUESTIES_RANAP_RAB_RELEASELIST_PRESENT) {
RANAP_RAB_ReleaseList_t *r_list = &ies->raB_ReleaseList;
/* increment number of released RABs, we don't need to do that individually during iteration */
HNBP_CTR_ADD(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_REQ : HNB_CTR_RANAP_CS_RAB_REL_REQ,
r_list->raB_ReleaseList_ies.list.count);
for (unsigned int i = 0; i < r_list->raB_ReleaseList_ies.list.count; i++) {
RANAP_IE_t *release_list_ie = r_list->raB_ReleaseList_ies.list.array[i];
RANAP_RAB_ReleaseItemIEs_t _rab_rel_item_ies = {};
RANAP_RAB_ReleaseItemIEs_t *rab_rel_item_ies = &_rab_rel_item_ies;
RANAP_RAB_ReleaseItem_t *rab_rel_item;
unsigned int ctr_num;
uint8_t rab_id;
if (!release_list_ie)
@ -133,6 +142,15 @@ static void kpi_ranap_process_dl_rab_ass_req(struct hnbgw_context_map *map, rana
switch (map->rab_state[rab_id]) {
case RAB_STATE_ACTIVE:
if (rab_rel_item->cause.present == RANAP_Cause_PR_nAS &&
rab_rel_item->cause.choice.nAS == RANAP_CauseNAS_normal_release) {
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_REQ;
} else {
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL;
}
if (!map->is_ps)
ctr_num++;
HNBP_CTR_INC(hnbp, ctr_num);
break;
default:
LOG_MAP(map, DRANAP, LOGL_NOTICE,

View File

@ -175,7 +175,7 @@ static void mgw_fsm_crcx_hnb_onenter(struct osmo_fsm_inst *fi, uint32_t prev_sta
mgw_info = (struct mgcp_conn_peer) {
.call_id = (map->rua_ctx_id << 8) | mgw_fsm_priv->rab_id,
.ptime = 20,
.conn_mode = MGCP_CONN_LOOPBACK,
.conn_mode = MGCP_CONN_RECV_SEND,
};
mgw_info.codecs[0] = CODEC_IUFP;
mgw_info.codecs_len = 1;

View File

@ -358,9 +358,6 @@ static void hnb_update_counters(struct hnb_persistent *hnbp, bool ul, int64_t pa
ul ? HNB_CTR_GTPU_TOTAL_BYTES_UL : HNB_CTR_GTPU_TOTAL_BYTES_DL,
&val->total_bytes, bytes);
LOGP(DLGLOBAL, LOGL_ERROR, "XXX %s(): packets=%ld btes=%ld ueb=%ld\n", __func__,
packets, bytes, bytes - packets * 36);
/* Assuming an IP header of 20 bytes, derive the GTP-U payload size:
*
* [...] \ \