mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw.git
synced 2025-11-04 14:03:34 +00:00
Compare commits
39 Commits
osmith/wip
...
1.6.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c500f92822 | ||
|
|
a253f668ff | ||
|
|
ca7c36d39e | ||
|
|
ee47106dc3 | ||
|
|
19bd64c77f | ||
|
|
f901dc0f60 | ||
|
|
8adeeb4609 | ||
|
|
1ccb1832f7 | ||
|
|
0081cd7b00 | ||
|
|
610aae4d65 | ||
|
|
3ef65760ff | ||
|
|
00655d45dd | ||
|
|
15e552f232 | ||
|
|
e29816eccc | ||
|
|
fc0e505330 | ||
|
|
3da951d5c3 | ||
|
|
2a4af66669 | ||
|
|
62da064eda | ||
|
|
938b5ac777 | ||
|
|
a503bd4eb2 | ||
|
|
da8a042ead | ||
|
|
b3b2e2b20d | ||
|
|
9da60749ca | ||
|
|
02d1d51966 | ||
|
|
9a7e520a95 | ||
|
|
20b710bfc2 | ||
|
|
64a2debb9a | ||
|
|
e4bd96a841 | ||
|
|
eccff1abe8 | ||
|
|
a121f82f33 | ||
|
|
a5974d7906 | ||
|
|
0d2d966c15 | ||
|
|
de99af3aaa | ||
|
|
ba81785b71 | ||
|
|
da7d33e284 | ||
|
|
7449635520 | ||
|
|
8fd95f3e74 | ||
|
|
cd58308915 | ||
|
|
e3cc5ddf1d |
@@ -19,7 +19,6 @@ SUBDIRS = \
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
contrib/osmo-hnbgw.spec.in \
|
||||
debian \
|
||||
git-version-gen \
|
||||
osmoappdesc.py \
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
|
||||
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
|
||||
# In short:
|
||||
# according to https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release
|
||||
# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
|
||||
# LIBVERSION=c:r:a
|
||||
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
|
||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
|
||||
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:a.
|
||||
# If any interfaces have been added since the last public release: c:r:a + 1.
|
||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
|
||||
#library what description / commit summary line
|
||||
osmo-iuh >1.5.0 proper decoding of X.213 IPv4 address len=7
|
||||
#library what description / commit summary line
|
||||
|
||||
34
configure.ac
34
configure.ac
@@ -49,27 +49,37 @@ AC_SEARCH_LIBS([sctp_recvmsg], [sctp], [
|
||||
LIBS=$old_LIBS
|
||||
|
||||
PKG_CHECK_MODULES(LIBASN1C, libasn1c >= 0.9.30)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.12.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.13.0)
|
||||
|
||||
# Enable PFCP support for GTP tunnel mapping via UPF
|
||||
AC_ARG_ENABLE([pfcp], [AS_HELP_STRING([--enable-pfcp], [Build with PFCP support, for GTP tunnel mapping via UPF])],
|
||||
[osmo_ac_pfcp="$enableval"],[osmo_ac_pfcp="no"])
|
||||
if test "x$osmo_ac_pfcp" = "xyes" ; then
|
||||
PKG_CHECK_MODULES(LIBOSMOPFCP, libosmo-pfcp >= 0.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOPFCP, libosmo-pfcp >= 0.4.0)
|
||||
AC_DEFINE(ENABLE_PFCP, 1, [Define to build with PFCP support])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_PFCP, test "x$osmo_ac_pfcp" = "xyes")
|
||||
AC_SUBST(osmo_ac_pfcp)
|
||||
|
||||
# Enable libnftables support for traffic counters using nft
|
||||
AC_ARG_ENABLE([nftables], [AS_HELP_STRING([--enable-nftables], [Build with libnftables support, for traffic counters using nft])],
|
||||
[osmo_ac_nftables="$enableval"],[osmo_ac_nftables="no"])
|
||||
if test "x$osmo_ac_nftables" = "xyes" ; then
|
||||
PKG_CHECK_MODULES(LIBNFTABLES, libnftables >= 1.0.2)
|
||||
AC_DEFINE(ENABLE_NFTABLES, 1, [Define to build with libnftables support])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_NFTABLES, test "x$osmo_ac_nftables" = "xyes")
|
||||
AC_SUBST(osmo_ac_nftables)
|
||||
|
||||
dnl checks for header files
|
||||
AC_HEADER_STDC
|
||||
|
||||
@@ -230,11 +240,11 @@ AC_OUTPUT(
|
||||
tests/Makefile
|
||||
tests/atlocal
|
||||
tests/ranap_rab_ass/Makefile
|
||||
tests/umts_cell_id/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
doc/manuals/Makefile
|
||||
doc/charts/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
contrib/osmo-hnbgw.spec
|
||||
Makefile)
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
# environment variables:
|
||||
# * PFCP: configure PFCP support if set to "1" (default)
|
||||
# * WITH_MANUALS: build manual PDFs if set to "1"
|
||||
# * NFTABLES: configure nftables support if set to "1" (default)
|
||||
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
|
||||
#
|
||||
PFCP=${PFCP:-1}
|
||||
NFTABLES=${NFTABLES:-1}
|
||||
|
||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
|
||||
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
|
||||
@@ -45,6 +47,9 @@ if [ "$PFCP" = "1" ]; then
|
||||
osmo-build-dep.sh libosmo-pfcp
|
||||
CONFIG="$CONFIG --enable-pfcp"
|
||||
fi
|
||||
if [ "$NFTABLES" = "1" ]; then
|
||||
CONFIG="$CONFIG --enable-nftables"
|
||||
fi
|
||||
if [ "$WITH_MANUALS" = "1" ]; then
|
||||
CONFIG="$CONFIG --enable-manuals"
|
||||
fi
|
||||
@@ -59,7 +64,7 @@ set -x
|
||||
|
||||
cd "$base"
|
||||
autoreconf --install --force
|
||||
./configure --enable-sanitize --enable-external-tests $CONFIG
|
||||
./configure --enable-sanitize --enable-external-tests --enable-werror $CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
#
|
||||
# spec file for package osmo-hnbgw
|
||||
#
|
||||
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
# upon. The license for this file, and modifications and additions to the
|
||||
# file, is the same license as for the pristine package itself (unless the
|
||||
# license for the pristine package is not an Open Source License, in which
|
||||
# case the license is the MIT License). An "Open Source License" is a
|
||||
# license that conforms to the Open Source Definition (Version 1.9)
|
||||
# published by the Open Source Initiative.
|
||||
|
||||
## Disable LTO for now since it breaks compilation of the tests
|
||||
## https://osmocom.org/issues/4113
|
||||
%define _lto_cflags %{nil}
|
||||
|
||||
Name: osmo-hnbgw
|
||||
Version: @VERSION@
|
||||
Release: 0
|
||||
Summary: OsmoHNBGW: Osmocom's Base Station Controller for 2G CS mobile networks
|
||||
License: AGPL-3.0-or-later AND GPL-2.0-or-later
|
||||
Group: Hardware/Mobile
|
||||
URL: https://osmocom.org/projects/osmohnbgw
|
||||
Source: %{name}-%{version}.tar.xz
|
||||
BuildRequires: automake >= 1.9
|
||||
BuildRequires: libtool >= 2
|
||||
BuildRequires: lksctp-tools-devel
|
||||
BuildRequires: pkgconfig >= 0.20
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: pkgconfig(libcrypto) >= 0.9.5
|
||||
BuildRequires: pkgconfig(libosmo-mgcp-client) >= 1.12.0
|
||||
BuildRequires: pkgconfig(libosmo-netif) >= 1.4.0
|
||||
BuildRequires: pkgconfig(libosmo-sigtran) >= 1.8.0
|
||||
BuildRequires: pkgconfig(libosmoabis) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmotrau) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.9.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.9.0
|
||||
BuildRequires: pkgconfig(libosmogb) >= 1.9.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.9.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.9.0
|
||||
BuildRequires: pkgconfig(libosmo-hnbap) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmo-ranap) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmo-rua) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmo-pfcp) >= 0.3.0
|
||||
BuildRequires: pkgconfig(talloc)
|
||||
BuildRequires: pkgconfig(libasn1c) >= 0.9.30
|
||||
%{?systemd_requires}
|
||||
|
||||
%description
|
||||
OsmoHNBGW: Osmocom's Home NodeB for 3G mobile networks.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
echo "%{version}" >.tarball-version
|
||||
autoreconf -fi
|
||||
%configure \
|
||||
--docdir=%{_docdir}/%{name} \
|
||||
--with-systemdsystemunitdir=%{_unitdir} \
|
||||
--enable-pfcp
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
%make_install
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%preun
|
||||
%service_del_preun %{name}.service
|
||||
|
||||
%postun
|
||||
%service_del_postun %{name}.service
|
||||
|
||||
%pre
|
||||
%service_add_pre %{name}.service
|
||||
|
||||
%post
|
||||
%service_add_post %{name}.service
|
||||
%endif
|
||||
|
||||
%check
|
||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
|
||||
%files
|
||||
%license COPYING
|
||||
%doc AUTHORS README.md
|
||||
%{_bindir}/osmo-hnbgw
|
||||
%dir %{_docdir}/%{name}/examples
|
||||
%dir %{_docdir}/%{name}/examples/osmo-hnbgw
|
||||
%{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw.cfg
|
||||
%{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw-cs7.cfg
|
||||
%{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw-pfcp.cfg
|
||||
%{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw-cnpool.cfg
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-hnbgw.cfg
|
||||
%{_unitdir}/%{name}.service
|
||||
|
||||
%changelog
|
||||
@@ -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
|
||||
|
||||
|
||||
113
debian/changelog
vendored
113
debian/changelog
vendored
@@ -1,3 +1,116 @@
|
||||
osmo-hnbgw (1.6.1) unstable; urgency=medium
|
||||
|
||||
[ Neels Janosch Hofmeyr ]
|
||||
* drop config.vty tests from make check
|
||||
* coverity CID#358071
|
||||
* coverity CID#358072
|
||||
* coverity CID#358070
|
||||
* coverity CID#358069
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* ps_rab_ass_fsm: Fix uninitialized ptr access
|
||||
* hnbgw: Fix wrong map object retrieved from hashtable
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 08 Nov 2024 16:10:50 +0100
|
||||
|
||||
osmo-hnbgw (1.6.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* hnbgw_cn: Remove assert hit due to wrong assumption
|
||||
* Increase default X31 val from 5 to 15 seconds
|
||||
* context_map_sccp: Fix assert hit due to missing ev handling
|
||||
* tests/ranap_rab_ass: Test RAB-Ass.req with X.213 IPv4 address len=7
|
||||
* mgw_fsm: Assume IuUP HNB IP addr = Iuh during MGCP CRCX on RAN side
|
||||
* mgw_fsm: Modify RAB on HNB if IuUP local IP addr at MGW changes during MDCX
|
||||
* hnbap: Avoid calling duplicate getpeername() for each registered HNB
|
||||
|
||||
[ Neels Janosch Hofmeyr ]
|
||||
* X31: fix vty doc
|
||||
* allow (second) CS RAB Assignment Request without RTP info
|
||||
* systemd,manual: set LimitNOFILE=65536
|
||||
* rua: validate correct RUA ctx state per RUA message type
|
||||
* rua: move from ERROR to DEBUG log: stray RUA Disconnect
|
||||
* pfcp: fix modification of wrong FAR ID
|
||||
* tweak vty doc: "UDP port"
|
||||
* add tests/pfcp_cfg.vty.with_pfcp
|
||||
* pfcp: fix missing vty_write of pfcp local-port
|
||||
* pfcp: implement sending Network Instance IEs
|
||||
* fix null deref on hnb_context_release
|
||||
* drop legacy hack: do not start MGW endp in loopback mode
|
||||
* fixup: compilation error: unused var in map_rua_init_action()
|
||||
* fix rate_ctr leak in hnb_persistent_free()
|
||||
* per-HNB GTP-U traffic counters via nft
|
||||
* add hnb_persistent hashtable: optimize lookup by cell id
|
||||
* fix stat_item leak in hnb_persistent_free()
|
||||
* use osmo_jhash for the hnb_persistent hashtable
|
||||
* rename to umts_cell_id_to_str()
|
||||
* umts_cell_id: add umts_cell_id_to_str_buf()
|
||||
* add umts_cell_id_test.c
|
||||
* 3-digit MNC: use osmo_plmn_id in struct umts_cell_id
|
||||
* add LOG_HNBP() for hnb_persistent
|
||||
* hnb_persistent: introduce disconnected timeout
|
||||
* prevent use-after-free after FSM instance termination
|
||||
* improve HNBAP error logging
|
||||
* nft-kpi: log errors of counter retrieval
|
||||
* nft-kpi: remove X34 drifting: adjust delay by elapsed time
|
||||
* drop list of HNBAP UE Context
|
||||
* dbg log: nft kpi: clarify nr of rate ctrs vs nr of hnbp
|
||||
* fix MGCP compat with osmo-mgw <= 1.12.2: CRCX in recvonly
|
||||
|
||||
[ Andreas Eversberg ]
|
||||
* Use uniform log format for default config files
|
||||
|
||||
[ Harald Welte ]
|
||||
* Display RANAP state during 'show cnlink'
|
||||
* Fix license headers: Should have been AGPLv3+, not GPLv2+
|
||||
* [cosmetic] re-order hnbgw.c to group code in major blocks
|
||||
* umts_cell_id_name: Use 3-digit MCC and 2/3-digit MNC based on VTY config
|
||||
* osmo_hnbgw_main: Install our usual SIGUSR1/SIGUSR2/SIGABRT handlers
|
||||
* Introduce umts_cell_id_from_str() as inverse of umts_cell_id_name()
|
||||
* Introduce concept of per-HNB persistent data structure
|
||||
* Set persistent->ctx pointer on HNB REGISTER REQ
|
||||
* New per-hnb rate_ctr and stat_item groups; track uptime
|
||||
* Various new per-hnb RANAP and RUA counters
|
||||
* stats: Introduce CNLINK counter macros
|
||||
* stats: Introduce CNPOOL counter macro
|
||||
* stats: Introduce basic counters for RANAP unit-data from CN links
|
||||
* cosmetic: Fix comment in mgw_fsm.c: One RAB per context_map, not hnb!
|
||||
* cosmetic: talk about cs_rab_ass_* when working in CS domain
|
||||
* cosmetic: Rename hnbgw_rx_ranap and friends to *_rx_ranap_udt_ul
|
||||
* rename hnbgw_peek_l3 to hnbgw_peek_l3_ul as it's uplink only
|
||||
* don't forward paging requests to HNB's not yet registered
|
||||
* vty: Print the uptime during 'show hnb' output
|
||||
* Introduce counter for per-hnb cumulative active CS RAB duration
|
||||
* stats: Add per-hnb paging:{cs,ps}:attempted counter
|
||||
* cosmetic: align downlink RANAP unitdata function names with uplink
|
||||
* mgw_fsm: Add some OSMO_ASSERT() to ensure only CS maps passed
|
||||
* RAB activation/modification/release statistics
|
||||
* context_map_{rua,sccp}: Re-order to always process KPIs
|
||||
* generalize hnbgw_tx_ue_register_rej_tmsi() to hnbgw_tx_ue_register_rej()
|
||||
* HNBAP: Use proper cause values during HNB-REGISTER-REQ processing
|
||||
* HNBAP: Send HNB-REGISTER-REJ on ASN.1 decoding error
|
||||
* HNBAP: Always respond to UE-REGISTER-REQUEST
|
||||
* HNBAP: Make sure to respond with correct "reject"
|
||||
* HNBAP: Transmit ErrorIndication in more situations
|
||||
* HNBAP: use GSM23003_IMSI_MAX_DIGITS instead of magic number
|
||||
* HNBAP: Support IMSI identity type in hnbgw_tx_ue_register_rej()
|
||||
* counters: Distinguish between normal and abnormal release cause
|
||||
* KPI: Add initial set of DTAP message type rate counters
|
||||
* kpi_ranap: Avoid null pointer de-ref during HNB shutdown
|
||||
|
||||
[ Oliver Smith ]
|
||||
* gitignore: add *.la, hnbgw_vty_reference.xml
|
||||
* debian: fix having no manuals in osmo-hnbgw-doc
|
||||
* .deb/.rpm: various fixes related to non-root
|
||||
* contrib: remove rpm spec file
|
||||
* debian/postinst: add checks, be verbose
|
||||
* contrib/jenkins: set --enable-werror
|
||||
|
||||
[ Max ]
|
||||
* .deb/.rpm: add osmocom user during package install
|
||||
|
||||
-- Oliver Smith <osmith@sysmocom.de> Thu, 25 Jul 2024 10:05:58 +0200
|
||||
|
||||
osmo-hnbgw (1.5.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Janosch Hofmeyr ]
|
||||
|
||||
21
debian/control
vendored
21
debian/control
vendored
@@ -13,16 +13,17 @@ Build-Depends: debhelper (>= 10),
|
||||
libtalloc-dev,
|
||||
libasn1c-dev (>= 0.9.30),
|
||||
libsctp-dev,
|
||||
libosmocore-dev (>= 1.9.0),
|
||||
libosmo-sigtran-dev (>= 1.8.0),
|
||||
libosmo-abis-dev (>= 1.5.0),
|
||||
libosmo-netif-dev (>= 1.4.0),
|
||||
libosmo-mgcp-client-dev (>= 1.12.0),
|
||||
libosmo-hnbap-dev (>= 1.5.0),
|
||||
libosmo-ranap-dev (>= 1.5.0),
|
||||
libosmo-rua-dev (>= 1.5.0),
|
||||
libosmo-pfcp-dev (>= 0.3.0),
|
||||
osmo-gsm-manuals-dev (>= 1.5.0)
|
||||
libosmocore-dev (>= 1.10.0),
|
||||
libosmo-sigtran-dev (>= 1.9.0),
|
||||
libosmo-abis-dev (>= 1.6.0),
|
||||
libosmo-netif-dev (>= 1.5.0),
|
||||
libosmo-mgcp-client-dev (>= 1.13.0),
|
||||
libosmo-hnbap-dev (>= 1.6.0),
|
||||
libosmo-ranap-dev (>= 1.6.0),
|
||||
libosmo-rua-dev (>= 1.6.0),
|
||||
libosmo-pfcp-dev (>= 0.4.0),
|
||||
libnftables-dev,
|
||||
osmo-gsm-manuals-dev (>= 1.6.0)
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw
|
||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw
|
||||
|
||||
38
debian/postinst
vendored
Executable file
38
debian/postinst
vendored
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
# Create the osmocom group and user (if it doesn't exist yet)
|
||||
if ! getent group osmocom >/dev/null; then
|
||||
groupadd --system osmocom
|
||||
fi
|
||||
if ! getent passwd osmocom >/dev/null; then
|
||||
useradd \
|
||||
--system \
|
||||
--gid osmocom \
|
||||
--home-dir /var/lib/osmocom \
|
||||
--shell /sbin/nologin \
|
||||
--comment "Open Source Mobile Communications" \
|
||||
osmocom
|
||||
fi
|
||||
|
||||
# Fix permissions of previous (root-owned) install (OS#4107)
|
||||
if dpkg --compare-versions "$2" le "1.6.0"; then
|
||||
if [ -e /etc/osmocom/osmo-hnbgw.cfg ]; then
|
||||
chown -v osmocom:osmocom /etc/osmocom/osmo-hnbgw.cfg
|
||||
chmod -v 0660 /etc/osmocom/osmo-hnbgw.cfg
|
||||
fi
|
||||
|
||||
if [ -d /etc/osmocom ]; then
|
||||
chown -v root:osmocom /etc/osmocom
|
||||
chmod -v 2775 /etc/osmocom
|
||||
fi
|
||||
|
||||
mkdir -p /var/lib/osmocom
|
||||
chown -R -v osmocom:osmocom /var/lib/osmocom
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# dh_installdeb(1) will replace this with shell code automatically
|
||||
# generated by other debhelper scripts.
|
||||
#DEBHELPER#
|
||||
15
debian/rules
vendored
15
debian/rules
vendored
@@ -44,11 +44,18 @@
|
||||
%:
|
||||
dh $@ --with autoreconf
|
||||
|
||||
# debmake generated override targets
|
||||
CONFIGURE_FLAGS += --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||
CONFIGURE_FLAGS += --enable-pfcp
|
||||
# libnftables is too old in Debian 10 (OS#6425)
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- $(CONFIGURE_FLAGS)
|
||||
CONFIGURE_FLAGS=" \
|
||||
--enable-manuals \
|
||||
--enable-pfcp \
|
||||
--with-systemdsystemunitdir=/lib/systemd/system \
|
||||
"; \
|
||||
if pkg-config --exists libnftables --atleast-version=1.0.2; then \
|
||||
CONFIGURE_FLAGS="$$CONFIGURE_FLAGS --enable-nftables"; \
|
||||
fi; \
|
||||
dh_auto_configure -- $$CONFIGURE_FLAGS
|
||||
|
||||
#
|
||||
# Do not install libtool archive, python .pyc .pyo
|
||||
#override_dh_install:
|
||||
|
||||
@@ -3,6 +3,7 @@ noinst_HEADERS = \
|
||||
context_map.h hnbgw.h hnbgw_cn.h \
|
||||
hnbgw_hnbap.h hnbgw_rua.h hnbgw_ranap.h \
|
||||
kpi.h \
|
||||
nft_kpi.h \
|
||||
ranap_rab_ass.h mgw_fsm.h tdefs.h \
|
||||
hnbgw_pfcp.h \
|
||||
ps_rab_ass_fsm.h \
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#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/sccp_sap.h>
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
@@ -18,6 +19,8 @@
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_pool.h>
|
||||
|
||||
#include <osmocom/hnbgw/nft_kpi.h>
|
||||
|
||||
#define STORE_UPTIME_INTERVAL 10 /* seconds */
|
||||
#define HNB_STORE_RAB_DURATIONS_INTERVAL 1 /* seconds */
|
||||
|
||||
@@ -29,6 +32,7 @@ enum {
|
||||
DMGW,
|
||||
DHNB,
|
||||
DCN,
|
||||
DNFT,
|
||||
};
|
||||
|
||||
extern const struct log_info hnbgw_log_info;
|
||||
@@ -37,6 +41,12 @@ 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
|
||||
|
||||
@@ -100,6 +110,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,
|
||||
@@ -109,6 +121,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,
|
||||
|
||||
@@ -132,6 +146,25 @@ enum hnb_rate_ctr {
|
||||
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 {
|
||||
@@ -139,22 +172,22 @@ enum hnb_stat {
|
||||
};
|
||||
|
||||
struct umts_cell_id {
|
||||
uint16_t mcc; /*!< Mobile Country Code (0-999) */
|
||||
uint16_t mnc; /*!< Mobile Network Code (0-999) */
|
||||
struct osmo_plmn_id plmn; /*!< Mobile Country Code and Mobile Network Code (000-00 to 999-999) */
|
||||
uint16_t lac; /*!< Locaton Area Code (1-65534) */
|
||||
uint16_t rac; /*!< Routing Area Code (0-255) */
|
||||
uint16_t sac; /*!< Service Area Code */
|
||||
uint32_t cid; /*!< Cell ID */
|
||||
};
|
||||
const char *umts_cell_id_name(const struct umts_cell_id *ucid);
|
||||
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 (a->mcc != b->mcc)
|
||||
return false;
|
||||
if (a->mnc != b->mnc)
|
||||
if (osmo_plmn_cmp(&a->plmn, &b->plmn))
|
||||
return false;
|
||||
if (a->lac != b->lac)
|
||||
return false;
|
||||
@@ -340,6 +373,8 @@ struct hnb_context {
|
||||
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;
|
||||
|
||||
@@ -353,6 +388,32 @@ struct hnb_persistent {
|
||||
|
||||
struct rate_ctr_group *ctrs;
|
||||
struct osmo_stat_item_group *statg;
|
||||
|
||||
/* State that the main thread needs in order to know what was requested from the nft worker threads and what
|
||||
* still needs to be requested. */
|
||||
struct {
|
||||
/* Whether a persistent named counter was added in nftables for this cell id. */
|
||||
bool persistent_counter_added;
|
||||
|
||||
/* The last hNodeB GTP-U address we asked the nft maintenance thread to set up.
|
||||
* osmo_sockaddr_str_is_nonzero(addr_remote) == false when no rules were added yet, and when
|
||||
* we asked the nft maintenance thread to remove the rules for this hNodeB because it has
|
||||
* disconnected. */
|
||||
struct osmo_sockaddr_str addr_remote;
|
||||
|
||||
/* the nft handles needed to clean up the UL and DL rules when the hNodeB disconnects,
|
||||
* and the last seen counter value gotten from nft. */
|
||||
struct {
|
||||
struct nft_kpi_handle h;
|
||||
struct nft_kpi_val v;
|
||||
} ul;
|
||||
struct {
|
||||
struct nft_kpi_handle h;
|
||||
struct nft_kpi_val v;
|
||||
} dl;
|
||||
} nft_kpi;
|
||||
|
||||
struct osmo_timer_list disconnected_timeout;
|
||||
};
|
||||
|
||||
struct ue_context {
|
||||
@@ -391,16 +452,24 @@ struct hnbgw {
|
||||
char *core;
|
||||
} netinst;
|
||||
} pfcp;
|
||||
struct {
|
||||
bool enable;
|
||||
/* The table name as used in nftables for the ruleset owned by this process. It is "osmo-hnbgw"
|
||||
* by default. */
|
||||
char *table_name;
|
||||
} nft_kpi;
|
||||
} config;
|
||||
/*! SCTP listen socket for incoming connections */
|
||||
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;
|
||||
/* optimized lookup for hnb_persistent, by cell id string */
|
||||
DECLARE_HASHTABLE(hnb_persistent_by_id, 5);
|
||||
|
||||
struct osmo_timer_list store_uptime_timer;
|
||||
/* list of struct ue_context */
|
||||
struct llist_head ue_list;
|
||||
/* next availble UE Context ID */
|
||||
uint32_t next_ue_ctx_id;
|
||||
struct ctrl_handle *ctrl;
|
||||
@@ -424,6 +493,12 @@ struct hnbgw {
|
||||
} pfcp;
|
||||
|
||||
struct osmo_timer_list hnb_store_rab_durations_timer;
|
||||
|
||||
struct {
|
||||
bool active;
|
||||
struct osmo_timer_list get_counters_timer;
|
||||
struct timespec next_timer;
|
||||
} nft_kpi;
|
||||
};
|
||||
|
||||
extern struct hnbgw *g_hnbgw;
|
||||
@@ -439,18 +514,13 @@ 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);
|
||||
|
||||
struct ue_context *ue_context_by_id(uint32_t id);
|
||||
struct ue_context *ue_context_by_imsi(const char *imsi);
|
||||
struct ue_context *ue_context_by_tmsi(uint32_t tmsi);
|
||||
struct ue_context *ue_context_alloc(struct hnb_context *hnb, const char *imsi,
|
||||
uint32_t tmsi);
|
||||
void ue_context_free(struct ue_context *ue);
|
||||
|
||||
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);
|
||||
@@ -471,3 +541,5 @@ struct msgb *hnbgw_ranap_msg_alloc(const char *name);
|
||||
int hnbgw_peek_l3_ul(struct hnbgw_context_map *map, struct msgb *ranap_msg);
|
||||
|
||||
unsigned long long hnb_get_updowntime(const struct hnb_context *ctx);
|
||||
|
||||
uint32_t get_next_ue_ctx_id(void);
|
||||
|
||||
@@ -6,3 +6,6 @@
|
||||
|
||||
void kpi_ranap_process_ul(struct hnbgw_context_map *map, ranap_message *ranap);
|
||||
void kpi_ranap_process_dl(struct hnbgw_context_map *map, ranap_message *ranap);
|
||||
|
||||
void kpi_dtap_process_ul(struct hnbgw_context_map *map, const uint8_t *buf, unsigned int len, uint8_t sapi);
|
||||
void kpi_dtap_process_dl(struct hnbgw_context_map *map, const uint8_t *buf, unsigned int len, uint8_t sapi);
|
||||
|
||||
25
include/osmocom/hnbgw/nft_kpi.h
Normal file
25
include/osmocom/hnbgw/nft_kpi.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct hnb_persistent;
|
||||
|
||||
/* A "handle" that nftables returns for chains and rules -- a plain number. Deleting an unnamed rule can only be done by
|
||||
* such a handle. */
|
||||
struct nft_kpi_handle {
|
||||
bool handle_present;
|
||||
int64_t handle;
|
||||
};
|
||||
|
||||
/* One GTP-U packet and byte counter cache, i.e. for one UL/DL direction of one hNodeB. */
|
||||
struct nft_kpi_val {
|
||||
uint64_t packets;
|
||||
uint64_t total_bytes;
|
||||
uint64_t ue_bytes;
|
||||
};
|
||||
|
||||
void nft_kpi_init(const char *table_name);
|
||||
void nft_kpi_hnb_persistent_add(struct hnb_persistent *hnbp);
|
||||
void nft_kpi_hnb_persistent_remove(struct hnb_persistent *hnbp);
|
||||
int nft_kpi_hnb_start(struct hnb_persistent *hnbp, const struct osmo_sockaddr_str *gtpu_remote);
|
||||
void nft_kpi_hnb_stop(struct hnb_persistent *hnbp);
|
||||
@@ -20,6 +20,7 @@ AM_CFLAGS = \
|
||||
$(LIBOSMORANAP_CFLAGS) \
|
||||
$(LIBOSMOHNBAP_CFLAGS) \
|
||||
$(LIBOSMOMGCPCLIENT_CFLAGS) \
|
||||
$(LIBNFTABLES_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
@@ -44,8 +45,10 @@ libhnbgw_la_SOURCES = \
|
||||
cnlink.c \
|
||||
ranap_rab_ass.c \
|
||||
mgw_fsm.c \
|
||||
kpi_dtap.c \
|
||||
kpi_ranap.c \
|
||||
tdefs.c \
|
||||
nft_kpi.c \
|
||||
$(NULL)
|
||||
|
||||
libhnbgw_la_LIBADD = \
|
||||
@@ -62,6 +65,7 @@ libhnbgw_la_LIBADD = \
|
||||
$(LIBOSMOHNBAP_LIBS) \
|
||||
$(LIBSCTP_LIBS) \
|
||||
$(LIBOSMOMGCPCLIENT_LIBS) \
|
||||
$(LIBNFTABLES_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
if ENABLE_PFCP
|
||||
|
||||
@@ -158,6 +158,11 @@ ranap_message *hnbgw_decode_ranap_co(struct msgb *ranap_msg)
|
||||
static int handle_rx_rua(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
|
||||
/* If the FSM instance has already terminated, don't dispatch anything. */
|
||||
if (fi->proc.terminating)
|
||||
return 0;
|
||||
|
||||
if (!msg_has_l2_data(ranap_msg))
|
||||
return 0;
|
||||
|
||||
@@ -214,16 +219,14 @@ static int forward_ranap_to_rua(struct hnbgw_context_map *map, struct msgb *rana
|
||||
|
||||
static void map_rua_init_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct msgb *ranap_msg = data;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case MAP_RUA_EV_RX_CONNECT:
|
||||
/* not needed for RAB assignment scanning, but for KPI scanning */
|
||||
handle_rx_rua(fi, ranap_msg);
|
||||
map_rua_fsm_state_chg(MAP_RUA_ST_CONNECTED);
|
||||
/* The Connect will never be a RAB Assignment response, so no need for handle_rx_rua() (which decodes
|
||||
* the RANAP message to detect a RAB Assignment response). Just forward to SCCP as is. */
|
||||
map_sccp_dispatch(map, MAP_SCCP_EV_TX_DATA_REQUEST, ranap_msg);
|
||||
return;
|
||||
|
||||
case MAP_RUA_EV_RX_DISCONNECT:
|
||||
|
||||
@@ -198,6 +198,10 @@ static int handle_rx_sccp(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
int rc;
|
||||
|
||||
/* If the FSM instance has already terminated, don't dispatch anything. */
|
||||
if (fi->proc.terminating)
|
||||
return 0;
|
||||
|
||||
/* When there was no message received along with the received event, then there is nothing to forward to RUA. */
|
||||
if (!msg_has_l2_data(ranap_msg))
|
||||
return 0;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
#include <osmocom/core/jhash.h>
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
|
||||
@@ -43,6 +44,7 @@
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
|
||||
struct hnbgw *g_hnbgw = NULL;
|
||||
|
||||
@@ -92,92 +94,11 @@ static void hnbgw_store_hnb_rab_durations(void *data)
|
||||
* UE Context
|
||||
***********************************************************************/
|
||||
|
||||
struct ue_context *ue_context_by_id(uint32_t id)
|
||||
uint32_t get_next_ue_ctx_id(void)
|
||||
{
|
||||
struct ue_context *ue;
|
||||
|
||||
llist_for_each_entry(ue, &g_hnbgw->ue_list, list) {
|
||||
if (ue->context_id == id)
|
||||
return ue;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
return g_hnbgw->next_ue_ctx_id++;
|
||||
}
|
||||
|
||||
struct ue_context *ue_context_by_imsi(const char *imsi)
|
||||
{
|
||||
struct ue_context *ue;
|
||||
|
||||
llist_for_each_entry(ue, &g_hnbgw->ue_list, list) {
|
||||
if (!strcmp(ue->imsi, imsi))
|
||||
return ue;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ue_context *ue_context_by_tmsi(uint32_t tmsi)
|
||||
{
|
||||
struct ue_context *ue;
|
||||
|
||||
llist_for_each_entry(ue, &g_hnbgw->ue_list, list) {
|
||||
if (ue->tmsi == tmsi)
|
||||
return ue;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ue_context_free_by_hnb(const struct hnb_context *hnb)
|
||||
{
|
||||
struct ue_context *ue, *tmp;
|
||||
|
||||
llist_for_each_entry_safe(ue, tmp, &g_hnbgw->ue_list, list) {
|
||||
if (ue->hnb == hnb)
|
||||
ue_context_free(ue);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t get_next_ue_ctx_id(void)
|
||||
{
|
||||
uint32_t id;
|
||||
|
||||
do {
|
||||
id = g_hnbgw->next_ue_ctx_id++;
|
||||
} while (ue_context_by_id(id));
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
struct ue_context *ue_context_alloc(struct hnb_context *hnb, const char *imsi,
|
||||
uint32_t tmsi)
|
||||
{
|
||||
struct ue_context *ue;
|
||||
|
||||
ue = talloc_zero(g_hnbgw, struct ue_context);
|
||||
if (!ue)
|
||||
return NULL;
|
||||
|
||||
ue->hnb = hnb;
|
||||
if (imsi)
|
||||
OSMO_STRLCPY_ARRAY(ue->imsi, imsi);
|
||||
else
|
||||
ue->imsi[0] = '\0';
|
||||
ue->tmsi = tmsi;
|
||||
ue->context_id = get_next_ue_ctx_id();
|
||||
llist_add_tail(&ue->list, &g_hnbgw->ue_list);
|
||||
|
||||
LOGP(DHNBAP, LOGL_INFO, "created UE context: id 0x%x, imsi %s, tmsi 0x%x\n",
|
||||
ue->context_id, imsi? imsi : "-", tmsi);
|
||||
|
||||
return ue;
|
||||
}
|
||||
|
||||
void ue_context_free(struct ue_context *ue)
|
||||
{
|
||||
llist_del(&ue->list);
|
||||
talloc_free(ue);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* HNB Context
|
||||
***********************************************************************/
|
||||
@@ -231,31 +152,66 @@ static struct hnb_context *hnb_context_alloc(struct osmo_stream_srv_link *link,
|
||||
return ctx;
|
||||
}
|
||||
|
||||
const char *umts_cell_id_name(const struct umts_cell_id *ucid)
|
||||
int umts_cell_id_to_str_buf(char *buf, size_t buflen, const struct umts_cell_id *ucid)
|
||||
{
|
||||
const char *fmtstr = "%03u-%02u-L%u-R%u-S%u-C%u";
|
||||
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;
|
||||
}
|
||||
|
||||
if (g_hnbgw->config.plmn.mnc_3_digits)
|
||||
fmtstr = "%03u-%03u-L%u-R%u-S%u-C%u";
|
||||
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)
|
||||
}
|
||||
|
||||
return talloc_asprintf(OTC_SELECT, fmtstr, ucid->mcc, ucid->mnc, ucid->lac, ucid->rac,
|
||||
ucid->sac, ucid->cid);
|
||||
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 = sscanf(instr, "%hu-%hu-L%hu-R%hu-S%hu-C%u", &ucid->mcc, &ucid->mnc, &ucid->lac, &ucid->rac, &ucid->sac, &ucid->cid);
|
||||
int rc;
|
||||
char buf[4];
|
||||
const char *pos = instr;
|
||||
const char *end;
|
||||
|
||||
/* We want to use struct umts_cell_id as hashtable key. If it ever happens to contain any padding bytes, make
|
||||
* sure everything is deterministically zero. */
|
||||
memset(ucid, 0, sizeof(*ucid));
|
||||
|
||||
/* read MCC */
|
||||
end = strchr(pos, '-');
|
||||
if (!end || end <= pos || (end - pos) >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
osmo_strlcpy(buf, pos, end - pos + 1);
|
||||
if (osmo_mcc_from_str(buf, &ucid->plmn.mcc))
|
||||
return -EINVAL;
|
||||
pos = end + 1;
|
||||
|
||||
/* read MNC -- here the number of leading zeros matters. */
|
||||
end = strchr(pos, '-');
|
||||
if (!end || end == pos || (end - pos) >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
osmo_strlcpy(buf, pos, end - pos + 1);
|
||||
if (osmo_mnc_from_str(buf, &ucid->plmn.mnc, &ucid->plmn.mnc_3_digits))
|
||||
return -EINVAL;
|
||||
pos = end + 1;
|
||||
|
||||
/* parse the rest, where leading zeros do not matter */
|
||||
rc = sscanf(pos, "L%hu-R%hu-S%hu-C%u", &ucid->lac, &ucid->rac, &ucid->sac, &ucid->cid);
|
||||
if (rc < 0)
|
||||
return -errno;
|
||||
|
||||
if (rc != 6)
|
||||
return -EINVAL;
|
||||
|
||||
if (ucid->mcc > 999)
|
||||
return -EINVAL;
|
||||
|
||||
if (ucid->mnc > 999)
|
||||
if (rc != 4)
|
||||
return -EINVAL;
|
||||
|
||||
if (ucid->lac == 0 || ucid->lac == 0xffff)
|
||||
@@ -291,7 +247,7 @@ const char *hnb_context_name(struct hnb_context *ctx)
|
||||
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_name(&ctx->id));
|
||||
result = talloc_asprintf(OTC_SELECT, "%s %s", result, umts_cell_id_to_str(&ctx->id));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -304,7 +260,6 @@ void hnb_context_release_ue_state(struct hnb_context *ctx)
|
||||
context_map_hnb_released(map);
|
||||
/* hnbgw_context_map will remove itself from lists when it is ready. */
|
||||
}
|
||||
ue_context_free_by_hnb(ctx);
|
||||
}
|
||||
|
||||
void hnb_context_release(struct hnb_context *ctx)
|
||||
@@ -343,7 +298,7 @@ void hnb_context_release(struct hnb_context *ctx)
|
||||
|
||||
/* remove back reference from hnb_persistent to context */
|
||||
if (ctx->persistent)
|
||||
ctx->persistent->ctx = NULL;
|
||||
hnb_persistent_deregistered(ctx->persistent);
|
||||
|
||||
talloc_free(ctx);
|
||||
}
|
||||
@@ -398,11 +353,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" },
|
||||
@@ -415,9 +373,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" },
|
||||
@@ -457,6 +419,44 @@ const struct rate_ctr_desc hnb_ctr_description[] = {
|
||||
|
||||
[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 = {
|
||||
@@ -479,6 +479,24 @@ const struct osmo_stat_item_group_desc hnb_statg_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);
|
||||
@@ -486,7 +504,7 @@ struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id)
|
||||
return NULL;
|
||||
|
||||
hnbp->id = *id;
|
||||
hnbp->id_str = talloc_strdup(hnbp, umts_cell_id_name(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;
|
||||
@@ -497,6 +515,15 @@ struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id)
|
||||
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;
|
||||
|
||||
@@ -510,19 +537,93 @@ out_free:
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -847,6 +948,11 @@ static const struct log_info_cat hnbgw_log_cat[] = {
|
||||
.color = OSMO_LOGCOLOR_DARKYELLOW,
|
||||
.description = "Core Network side (via SCCP)",
|
||||
},
|
||||
[DNFT] = {
|
||||
.name = "DNFT", .loglevel = LOGL_NOTICE, .enabled = 1,
|
||||
.color = OSMO_LOGCOLOR_BLUE,
|
||||
.description = "nftables interaction for retrieving stats",
|
||||
},
|
||||
};
|
||||
|
||||
const struct log_info hnbgw_log_info = {
|
||||
@@ -877,8 +983,10 @@ void g_hnbgw_alloc(void *ctx)
|
||||
|
||||
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);
|
||||
hash_init(g_hnbgw->hnb_persistent_by_id);
|
||||
|
||||
INIT_LLIST_HEAD(&g_hnbgw->sccp.users);
|
||||
|
||||
g_hnbgw->mgw_pool = mgcp_client_pool_alloc(g_hnbgw);
|
||||
|
||||
@@ -446,8 +446,10 @@ static struct hnbgw_context_map *map_from_conn_id(struct hnbgw_sccp_user *hsu, u
|
||||
const struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
hash_for_each_possible(hsu->hnbgw_context_map_by_conn_id, map, hnbgw_sccp_user_entry, conn_id)
|
||||
return map;
|
||||
hash_for_each_possible(hsu->hnbgw_context_map_by_conn_id, map, hnbgw_sccp_user_entry, conn_id) {
|
||||
if (map->scu_conn_id == conn_id)
|
||||
return map;
|
||||
}
|
||||
LOGP(DRANAP, LOGL_ERROR, "Rx for unknown SCCP connection ID: %u: %s\n",
|
||||
conn_id, osmo_scu_prim_hdr_name_c(OTC_SELECT, oph));
|
||||
return NULL;
|
||||
|
||||
@@ -171,7 +171,7 @@ static int hnbgw_tx_hnb_register_acc(struct hnb_context *ctx)
|
||||
}
|
||||
|
||||
|
||||
static int hnbgw_tx_ue_register_acc(struct ue_context *ue)
|
||||
static int hnbgw_tx_ue_register_acc(struct hnb_context *hnb, const char *imsi, uint32_t context_id)
|
||||
{
|
||||
HNBAP_UERegisterAccept_t accept_out;
|
||||
HNBAP_UERegisterAcceptIEs_t accept;
|
||||
@@ -182,18 +182,20 @@ static int hnbgw_tx_ue_register_acc(struct ue_context *ue)
|
||||
int rc;
|
||||
|
||||
encoded_imsi_len = ranap_imsi_encode(encoded_imsi,
|
||||
sizeof(encoded_imsi), ue->imsi);
|
||||
sizeof(encoded_imsi), imsi);
|
||||
|
||||
memset(&accept, 0, sizeof(accept));
|
||||
accept.uE_Identity.present = HNBAP_UE_Identity_PR_iMSI;
|
||||
OCTET_STRING_fromBuf(&accept.uE_Identity.choice.iMSI,
|
||||
(const char *)encoded_imsi, encoded_imsi_len);
|
||||
asn1_u24_to_bitstring(&accept.context_ID, &ctx_id, ue->context_id);
|
||||
asn1_u24_to_bitstring(&accept.context_ID, &ctx_id, context_id);
|
||||
|
||||
memset(&accept_out, 0, sizeof(accept_out));
|
||||
rc = hnbap_encode_ueregisteraccepties(&accept_out, &accept);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING, &accept.uE_Identity.choice.iMSI);
|
||||
if (rc < 0) {
|
||||
LOGHNB(hnb, DHNBAP, LOGL_ERROR,
|
||||
"Failed to encode HNBAP UE Register Accept message for UE IMSI-%s\n", imsi);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -204,13 +206,18 @@ static int hnbgw_tx_ue_register_acc(struct ue_context *ue)
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBAP_UERegisterAccept, &accept_out);
|
||||
|
||||
return hnbgw_hnbap_tx(ue->hnb, msg);
|
||||
rc = hnbgw_hnbap_tx(hnb, msg);
|
||||
if (rc)
|
||||
LOGHNB(hnb, DHNBAP, LOGL_ERROR,
|
||||
"Failed to enqueue HNBAP UE Register Accept message for UE IMSI-%s\n", imsi);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnbgw_tx_ue_register_rej(struct hnb_context *hnb, HNBAP_UE_Identity_t *ue_id, const struct HNBAP_Cause *cause)
|
||||
{
|
||||
HNBAP_UERegisterReject_t reject_out;
|
||||
HNBAP_UERegisterRejectIEs_t reject;
|
||||
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
|
||||
struct msgb *msg;
|
||||
int rc;
|
||||
|
||||
@@ -271,6 +278,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 +327,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() */
|
||||
@@ -330,8 +348,6 @@ static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Ident
|
||||
struct msgb *msg;
|
||||
uint32_t ctx_id;
|
||||
uint32_t tmsi = 0;
|
||||
struct ue_context *ue;
|
||||
struct ue_context *ue_allocated = NULL;
|
||||
int rc;
|
||||
|
||||
memset(&accept, 0, sizeof(accept));
|
||||
@@ -377,11 +393,7 @@ static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Ident
|
||||
tmsi = ntohl(tmsi);
|
||||
LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "HNBAP register with TMSI %x\n", tmsi);
|
||||
|
||||
ue = ue_context_by_tmsi(tmsi);
|
||||
if (!ue)
|
||||
ue = ue_allocated = ue_context_alloc(hnb, NULL, tmsi);
|
||||
|
||||
asn1_u24_to_bitstring(&accept.context_ID, &ctx_id, ue->context_id);
|
||||
asn1_u24_to_bitstring(&accept.context_ID, &ctx_id, get_next_ue_ctx_id());
|
||||
|
||||
memset(&accept_out, 0, sizeof(accept_out));
|
||||
rc = hnbap_encode_ueregisteraccepties(&accept_out, &accept);
|
||||
@@ -414,11 +426,8 @@ static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Ident
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
LOGHNB(hnb, DHNBAP, LOGL_ERROR, "Failed to encode HNBAP UE Register Accept for TMSI 0x%08x\n", tmsi);
|
||||
/* Encoding failed. Nothing in 'accept_out'. */
|
||||
/* If we allocated the UE context but the UE REGISTER fails, get rid of it again: there will likely
|
||||
* never be a UE DE-REGISTER for this UE from the HNB, and the ue_context would linger forever. */
|
||||
if (ue_allocated)
|
||||
ue_context_free(ue_allocated);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -429,13 +438,15 @@ static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Ident
|
||||
&accept_out);
|
||||
rc = hnbgw_hnbap_tx(hnb, msg);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBAP_UERegisterAccept, &accept_out);
|
||||
if (rc)
|
||||
LOGHNB(hnb, DHNBAP, LOGL_ERROR, "Failed to transmit HNBAP UE Register Accept for TMSI 0x%08x\n", tmsi);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnbgw_rx_hnb_deregister(struct hnb_context *ctx, ANY_t *in)
|
||||
{
|
||||
HNBAP_HNBDe_RegisterIEs_t ies;
|
||||
HNBAP_Cause_t cause;
|
||||
HNBAP_Cause_t cause = {};
|
||||
int rc;
|
||||
|
||||
rc = hnbap_decode_hnbde_registeries(&ies, in);
|
||||
@@ -460,12 +471,13 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
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;
|
||||
struct timespec tp;
|
||||
HNBAP_Cause_t cause;
|
||||
HNBAP_Cause_t cause = {};
|
||||
struct osmo_sockaddr cur_osa = {};
|
||||
socklen_t len = sizeof(cur_osa);
|
||||
|
||||
rc = hnbap_decode_hnbregisterrequesties(&ies, in);
|
||||
if (rc < 0) {
|
||||
@@ -478,14 +490,25 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
|
||||
/* copy all identity parameters from the message to ctx */
|
||||
OSMO_STRLCPY_ARRAY(ctx->identity_info, identity_str);
|
||||
|
||||
/* 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(&ctx->id, 0, sizeof(ctx->id));
|
||||
ctx->id.lac = asn1str_to_u16(&ies.lac);
|
||||
ctx->id.sac = asn1str_to_u16(&ies.sac);
|
||||
ctx->id.rac = asn1str_to_u8(&ies.rac);
|
||||
ctx->id.cid = asn1bitstr_to_u28(&ies.cellIdentity);
|
||||
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);
|
||||
osmo_plmn_from_bcd(ies.plmNidentity.buf, &ctx->id.plmn);
|
||||
cell_id_str = umts_cell_id_to_str(&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)
|
||||
@@ -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;
|
||||
@@ -553,6 +568,8 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
|
||||
ctx->hnb_registered = true;
|
||||
|
||||
hnb_persistent_registered(ctx->persistent);
|
||||
|
||||
/* Send HNBRegisterAccept */
|
||||
rc = hnbgw_tx_hnb_register_acc(ctx);
|
||||
hnbap_free_hnbregisterrequesties(&ies);
|
||||
@@ -562,9 +579,7 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
static int hnbgw_rx_ue_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
{
|
||||
HNBAP_UERegisterRequestIEs_t ies;
|
||||
HNBAP_Cause_t cause;
|
||||
struct ue_context *ue;
|
||||
struct ue_context *ue_allocated = NULL;
|
||||
HNBAP_Cause_t cause = {};
|
||||
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
|
||||
int rc;
|
||||
|
||||
@@ -621,18 +636,10 @@ static int hnbgw_rx_ue_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "UE-REGISTER-REQ ID_type=%d imsi=%s cause=%ld\n",
|
||||
ies.uE_Identity.present, imsi, ies.registration_Cause);
|
||||
|
||||
ue = ue_context_by_imsi(imsi);
|
||||
if (!ue)
|
||||
ue = ue_allocated = ue_context_alloc(ctx, imsi, 0);
|
||||
|
||||
/* Send UERegisterAccept */
|
||||
rc = hnbgw_tx_ue_register_acc(ue);
|
||||
if (rc < 0) {
|
||||
/* If we allocated the UE context but the UE REGISTER fails, get rid of it again: there will likely
|
||||
* never be a UE DE-REGISTER for this UE from the HNB, and the ue_context would linger forever. */
|
||||
if (ue_allocated)
|
||||
ue_context_free(ue_allocated);
|
||||
}
|
||||
rc = hnbgw_tx_ue_register_acc(ctx, imsi, get_next_ue_ctx_id());
|
||||
if (rc < 0)
|
||||
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Failed to transmit HNBAP UE Register Accept for IMSI %s\n", imsi);
|
||||
free_and_return_rc:
|
||||
hnbap_free_ueregisterrequesties(&ies);
|
||||
return rc;
|
||||
@@ -641,8 +648,7 @@ free_and_return_rc:
|
||||
static int hnbgw_rx_ue_deregister(struct hnb_context *ctx, ANY_t *in)
|
||||
{
|
||||
HNBAP_UEDe_RegisterIEs_t ies;
|
||||
HNBAP_Cause_t cause;
|
||||
struct ue_context *ue;
|
||||
HNBAP_Cause_t cause = {};
|
||||
int rc;
|
||||
uint32_t ctxid;
|
||||
|
||||
@@ -665,9 +671,6 @@ static int hnbgw_rx_ue_deregister(struct hnb_context *ctx, ANY_t *in)
|
||||
HNBAP_Criticality_ignore, HNBAP_TriggeringMessage_initiating_message);
|
||||
} else {
|
||||
LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "UE-DE-REGISTER context=%u cause=%s\n", ctxid, hnbap_cause_str(&ies.cause));
|
||||
ue = ue_context_by_id(ctxid);
|
||||
if (ue)
|
||||
ue_context_free(ue);
|
||||
}
|
||||
|
||||
hnbap_free_uede_registeries(&ies);
|
||||
@@ -740,8 +743,7 @@ static int hnbgw_rx_unsuccessful_outcome_msg(struct hnb_context *hnb, HNBAP_Unsu
|
||||
{
|
||||
/* We don't care much about HNBAP */
|
||||
LOGHNB(hnb, DHNBAP, LOGL_ERROR, "Received Unsuccessful Outcome, procedureCode %ld, criticality %ld,"
|
||||
" cell mcc %u mnc %u lac %u rac %u sac %u cid %u\n", msg->procedureCode, msg->criticality,
|
||||
hnb->id.mcc, hnb->id.mnc, hnb->id.lac, hnb->id.rac, hnb->id.sac, hnb->id.cid);
|
||||
" cell %s\n", msg->procedureCode, msg->criticality, umts_cell_id_to_str(&hnb->id));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
#include <osmocom/hnbgw/nft_kpi.h>
|
||||
#include <osmocom/sigtran/protocol/sua.h>
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
#include <osmocom/netif/stream.h>
|
||||
@@ -209,8 +210,9 @@ static void vty_dump_hnb_info(struct vty *vty, struct hnb_context *hnb)
|
||||
vty_out(vty, "HNB ");
|
||||
vty_out_ofd_addr(vty, hnb->conn? osmo_stream_srv_get_ofd(hnb->conn) : NULL);
|
||||
vty_out(vty, " \"%s\"%s", hnb->identity_info, VTY_NEWLINE);
|
||||
vty_out(vty, " MCC %u MNC %u LAC %u RAC %u SAC %u CID %u SCTP-stream:HNBAP=%u,RUA=%u%s",
|
||||
hnb->id.mcc, hnb->id.mnc, hnb->id.lac, hnb->id.rac, hnb->id.sac, hnb->id.cid,
|
||||
vty_out(vty, " MCC %s MNC %s LAC %u RAC %u SAC %u CID %u SCTP-stream:HNBAP=%u,RUA=%u%s",
|
||||
osmo_mcc_name(hnb->id.plmn.mcc), osmo_mnc_name(hnb->id.plmn.mnc, hnb->id.plmn.mnc_3_digits),
|
||||
hnb->id.lac, hnb->id.rac, hnb->id.sac, hnb->id.cid,
|
||||
hnb->hnbap_stream, hnb->rua_stream, VTY_NEWLINE);
|
||||
|
||||
llist_for_each_entry(map, &hnb->map_list, hnb_list) {
|
||||
@@ -227,12 +229,6 @@ static void vty_dump_hnb_info(struct vty *vty, struct hnb_context *hnb)
|
||||
}
|
||||
}
|
||||
|
||||
static void vty_dump_ue_info(struct vty *vty, struct ue_context *ue)
|
||||
{
|
||||
vty_out(vty, "UE IMSI \"%s\" context ID %u HNB %s%s", ue->imsi, ue->context_id,
|
||||
hnb_context_name(ue->hnb), VTY_NEWLINE);
|
||||
}
|
||||
|
||||
#define SHOW_HNB_STR SHOW_STR "Display information about HNB\n"
|
||||
|
||||
DEFUN(show_hnb, show_hnb_cmd, "show hnb all",
|
||||
@@ -277,18 +273,6 @@ DEFUN(show_one_hnb, show_one_hnb_cmd, "show hnb NAME ",
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_ue, show_ue_cmd, "show ue all",
|
||||
SHOW_STR "Display HNBAP information about UE\n" "All UE\n")
|
||||
{
|
||||
struct ue_context *ue;
|
||||
|
||||
llist_for_each_entry(ue, &g_hnbgw->ue_list, list) {
|
||||
vty_dump_ue_info(vty, ue);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_talloc, show_talloc_cmd, "show talloc", SHOW_STR "Display talloc info")
|
||||
{
|
||||
talloc_report_full(g_hnbgw, stderr);
|
||||
@@ -878,6 +862,36 @@ DEFUN(cfg_hnbgw_no_hnb, cfg_hnbgw_no_hnb_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define NFT_KPI_STR "Retrieve traffic counters from nftables\n"
|
||||
|
||||
DEFUN(cfg_hnbgw_nft_kpi, cfg_hnbgw_nft_kpi_cmd,
|
||||
"nft-kpi [TABLE_NAME]",
|
||||
NFT_KPI_STR
|
||||
"Set a custom nft table name to use, instead of 'osmo-hnbgw'\n")
|
||||
{
|
||||
const char *set_table_name = NULL;
|
||||
if (argc > 0)
|
||||
set_table_name = argv[0];
|
||||
|
||||
if (vty->type == VTY_TERM)
|
||||
vty_out(vty, "%% WARNING: nft configuration changes need a restart of osmo-hnbgw%s", VTY_NEWLINE);
|
||||
|
||||
g_hnbgw->config.nft_kpi.enable = true;
|
||||
osmo_talloc_replace_string(g_hnbgw, &g_hnbgw->config.nft_kpi.table_name, set_table_name);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_hnbgw_no_nft_kpi, cfg_hnbgw_no_nft_kpi_cmd,
|
||||
"no nft-kpi",
|
||||
NO_STR NFT_KPI_STR)
|
||||
{
|
||||
if (vty->type == VTY_TERM)
|
||||
vty_out(vty, "%% WARNING: nft configuration changes need a restart of osmo-hnbgw%s", VTY_NEWLINE);
|
||||
g_hnbgw->config.nft_kpi.enable = false;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#if ENABLE_PFCP
|
||||
|
||||
static struct cmd_node pfcp_node = {
|
||||
@@ -1001,6 +1015,12 @@ static int config_write_hnbgw(struct vty *vty)
|
||||
_config_write_cnpool(vty, &g_hnbgw->sccp.cnpool_iucs);
|
||||
_config_write_cnpool(vty, &g_hnbgw->sccp.cnpool_iups);
|
||||
|
||||
if (g_hnbgw->config.nft_kpi.enable)
|
||||
vty_out(vty, " nft-kpi%s%s%s",
|
||||
g_hnbgw->config.nft_kpi.table_name ? " " : "",
|
||||
g_hnbgw->config.nft_kpi.table_name ? : "",
|
||||
VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1125,10 +1145,12 @@ void hnbgw_vty_init(void)
|
||||
install_element(HNBGW_NODE, &cfg_hnbgw_no_hnb_cmd);
|
||||
install_node(&hnb_node, NULL);
|
||||
|
||||
install_element(HNBGW_NODE, &cfg_hnbgw_nft_kpi_cmd);
|
||||
install_element(HNBGW_NODE, &cfg_hnbgw_no_nft_kpi_cmd);
|
||||
|
||||
install_element_ve(&show_cnlink_cmd);
|
||||
install_element_ve(&show_hnb_cmd);
|
||||
install_element_ve(&show_one_hnb_cmd);
|
||||
install_element_ve(&show_ue_cmd);
|
||||
install_element_ve(&show_talloc_cmd);
|
||||
|
||||
install_element(HNBGW_NODE, &cfg_hnbgw_mgcp_cmd);
|
||||
|
||||
111
src/osmo-hnbgw/kpi_dtap.c
Normal file
111
src/osmo-hnbgw/kpi_dtap.c
Normal file
@@ -0,0 +1,111 @@
|
||||
/* KPI (statistics, counters) at DTAP level */
|
||||
/* (C) 2024 by Harald Welte <laforge@osmocom.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* 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 <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/kpi.h>
|
||||
|
||||
/***********************************************************************
|
||||
* DOWNLINK messages
|
||||
***********************************************************************/
|
||||
|
||||
void kpi_dtap_process_dl(struct hnbgw_context_map *map, const uint8_t *buf, unsigned int len,
|
||||
uint8_t sapi)
|
||||
{
|
||||
struct hnb_persistent *hnbp = map->hnb_ctx->persistent;
|
||||
const struct gsm48_hdr *gh = (const struct gsm48_hdr *)buf;
|
||||
if (len < sizeof(*gh))
|
||||
return;
|
||||
|
||||
/* if you make use of any data beyond the fixed-size gsm48_hdr, you must make sure the underlying
|
||||
* buffer length is actually long enough! */
|
||||
|
||||
if (map->is_ps) {
|
||||
/* Packet Switched Domain (from SGSN) */
|
||||
switch (gsm48_hdr_msg_type(gh)) {
|
||||
case GSM48_MT_GMM_ATTACH_ACK:
|
||||
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_PS_ATT_ACK);
|
||||
break;
|
||||
case GSM48_MT_GMM_ATTACH_REJ:
|
||||
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_PS_ATT_REJ);
|
||||
break;
|
||||
case GSM48_MT_GMM_RA_UPD_ACK:
|
||||
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_PS_RAU_ACK);
|
||||
break;
|
||||
case GSM48_MT_GMM_RA_UPD_REJ:
|
||||
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_PS_RAU_REJ);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Circuit Switched Domain (from MSC) */
|
||||
switch (gsm48_hdr_msg_type(gh)) {
|
||||
case GSM48_MT_MM_LOC_UPD_ACCEPT:
|
||||
/* FIXME: many LU are acknwoeldged implicitly with TMSI allocation */
|
||||
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_CS_LU_ACC);
|
||||
break;
|
||||
case GSM48_MT_MM_LOC_UPD_REJECT:
|
||||
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_CS_LU_REJ);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* UPLINK messages
|
||||
***********************************************************************/
|
||||
|
||||
void kpi_dtap_process_ul(struct hnbgw_context_map *map, const uint8_t *buf, unsigned int len,
|
||||
uint8_t sapi)
|
||||
{
|
||||
struct hnb_persistent *hnbp = map->hnb_ctx->persistent;
|
||||
const struct gsm48_hdr *gh = (const struct gsm48_hdr *)buf;
|
||||
if (len < sizeof(*gh))
|
||||
return;
|
||||
|
||||
/* if you make use of any data beyond the fixed-size gsm48_hdr, you must make sure the underlying
|
||||
* buffer length is actually long enough! */
|
||||
|
||||
if (map->is_ps) {
|
||||
/* Packet Switched Domain (to SGSN) */
|
||||
switch (gsm48_hdr_msg_type(gh)) {
|
||||
case GSM48_MT_GMM_ATTACH_REQ:
|
||||
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_PS_ATT_REQ);
|
||||
break;
|
||||
case GSM48_MT_GMM_RA_UPD_REQ:
|
||||
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_PS_RAU_REQ);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Circuit Switched Domain (to MSC) */
|
||||
switch (gsm48_hdr_msg_type(gh)) {
|
||||
case GSM48_MT_MM_LOC_UPD_REQUEST:
|
||||
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_CS_LU_REQ);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
@@ -147,8 +165,27 @@ static void kpi_ranap_process_dl_rab_ass_req(struct hnbgw_context_map *map, rana
|
||||
}
|
||||
}
|
||||
|
||||
static void kpi_ranap_process_dl_direct_transfer(struct hnbgw_context_map *map, ranap_message *ranap)
|
||||
{
|
||||
const RANAP_DirectTransferIEs_t *dt_ies = &ranap->msg.directTransferIEs;
|
||||
uint8_t sapi = 0;
|
||||
|
||||
if (dt_ies->presenceMask & DIRECTTRANSFERIES_RANAP_SAPI_PRESENT) {
|
||||
if (dt_ies->sapi == RANAP_SAPI_sapi_3)
|
||||
sapi = 3;
|
||||
}
|
||||
kpi_dtap_process_dl(map, dt_ies->nas_pdu.buf, dt_ies->nas_pdu.size, sapi);
|
||||
}
|
||||
|
||||
void kpi_ranap_process_dl(struct hnbgw_context_map *map, ranap_message *ranap)
|
||||
{
|
||||
if (map->hnb_ctx == NULL) {
|
||||
/* This can happen if the HNB has disconnected and we are processing downlink messages
|
||||
* from the CN which were already in flight before the CN side has realized the HNB
|
||||
* is gone. */
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ranap->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment: /* RAB ASSIGNMENT REQ (8.2) */
|
||||
kpi_ranap_process_dl_rab_ass_req(map, ranap);
|
||||
@@ -156,6 +193,9 @@ void kpi_ranap_process_dl(struct hnbgw_context_map *map, ranap_message *ranap)
|
||||
case RANAP_ProcedureCode_id_Iu_Release:
|
||||
kpi_ranap_process_dl_iu_rel_cmd(map, ranap); /* IU RELEASE CMD (8.5) */
|
||||
break;
|
||||
case RANAP_ProcedureCode_id_DirectTransfer:
|
||||
kpi_ranap_process_dl_direct_transfer(map, ranap);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -352,8 +392,29 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
|
||||
}
|
||||
}
|
||||
|
||||
static void kpi_ranap_process_ul_initial_ue(struct hnbgw_context_map *map, ranap_message *ranap)
|
||||
{
|
||||
const RANAP_InitialUE_MessageIEs_t *iue_ies = &ranap->msg.initialUE_MessageIEs;
|
||||
kpi_dtap_process_ul(map, iue_ies->nas_pdu.buf, iue_ies->nas_pdu.size, 0);
|
||||
}
|
||||
|
||||
static void kpi_ranap_process_ul_direct_transfer(struct hnbgw_context_map *map, ranap_message *ranap)
|
||||
{
|
||||
const RANAP_DirectTransferIEs_t *dt_ies = &ranap->msg.directTransferIEs;
|
||||
uint8_t sapi = 0;
|
||||
|
||||
if (dt_ies->presenceMask & DIRECTTRANSFERIES_RANAP_SAPI_PRESENT) {
|
||||
if (dt_ies->sapi == RANAP_SAPI_sapi_3)
|
||||
sapi = 3;
|
||||
}
|
||||
kpi_dtap_process_ul(map, dt_ies->nas_pdu.buf, dt_ies->nas_pdu.size, sapi);
|
||||
}
|
||||
|
||||
void kpi_ranap_process_ul(struct hnbgw_context_map *map, ranap_message *ranap)
|
||||
{
|
||||
/* we should never be processing uplink messages from a non-existant HNB */
|
||||
OSMO_ASSERT(map->hnb_ctx);
|
||||
|
||||
switch (ranap->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment: /* RAB ASSIGNMENT REQ (8.2) */
|
||||
kpi_ranap_process_ul_rab_ass_resp(map, ranap);
|
||||
@@ -364,6 +425,12 @@ void kpi_ranap_process_ul(struct hnbgw_context_map *map, ranap_message *ranap)
|
||||
* processing of the downlink Iu Release Command. It's not like the RNC/HNB has any way to
|
||||
* refuse the release anyway. */
|
||||
break;
|
||||
case RANAP_ProcedureCode_id_InitialUE_Message:
|
||||
kpi_ranap_process_ul_initial_ue(map, ranap);
|
||||
break;
|
||||
case RANAP_ProcedureCode_id_DirectTransfer:
|
||||
kpi_ranap_process_ul_direct_transfer(map, ranap);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -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_ONLY,
|
||||
};
|
||||
mgw_info.codecs[0] = CODEC_IUFP;
|
||||
mgw_info.codecs_len = 1;
|
||||
|
||||
1044
src/osmo-hnbgw/nft_kpi.c
Normal file
1044
src/osmo-hnbgw/nft_kpi.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -222,6 +222,7 @@ int main(int argc, char **argv)
|
||||
rc = osmo_init_logging2(g_hnbgw, &hnbgw_log_info);
|
||||
if (rc < 0)
|
||||
exit(1);
|
||||
log_enable_multithread();
|
||||
|
||||
osmo_stats_init(g_hnbgw);
|
||||
rc = rate_ctr_init(g_hnbgw);
|
||||
@@ -329,6 +330,20 @@ int main(int argc, char **argv)
|
||||
hnbgw_pfcp_init();
|
||||
#endif
|
||||
|
||||
/* If nftables is enabled, initialize the nft table now or fail startup. This is important to immediately let
|
||||
* the user know if cap_net_admin privileges are missing, and not only when the first hNodeB connects. */
|
||||
if (g_hnbgw->config.nft_kpi.enable) {
|
||||
#if ENABLE_NFTABLES
|
||||
nft_kpi_init(g_hnbgw->config.nft_kpi.table_name);
|
||||
/* There is no direct error handling here, because nftables initialization happens asynchronously.
|
||||
* See nft_kpi.c nft_thread_t2m_cb(), case NFT_THREAD_INIT_TABLE to see what happens when initializing
|
||||
* nftables failed. */
|
||||
#else
|
||||
fprintf(stderr, "ERROR: Cannot enable nft KPI, this binary was built without nftables support\n");
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
hnbgw_cnpool_start(&g_hnbgw->sccp.cnpool_iucs);
|
||||
hnbgw_cnpool_start(&g_hnbgw->sccp.cnpool_iups);
|
||||
|
||||
@@ -345,6 +360,8 @@ int main(int argc, char **argv)
|
||||
signal(SIGUSR2, &signal_handler);
|
||||
osmo_init_ignore_signals();
|
||||
|
||||
osmo_fsm_set_dealloc_ctx(OTC_SELECT);
|
||||
|
||||
while (1) {
|
||||
rc = osmo_select_main_ctx(0);
|
||||
if (rc < 0)
|
||||
|
||||
@@ -294,7 +294,7 @@ static void ps_rab_ass_fsm_wait_local_f_teids(struct osmo_fsm_inst *fi, uint32_t
|
||||
/* See whether all information is in so that we can forward the modified RAB Assignment Request to RUA. */
|
||||
static void ps_rab_ass_req_check_local_f_teids(struct ps_rab_ass *rab_ass)
|
||||
{
|
||||
struct ps_rab *rab;
|
||||
struct ps_rab *rab = NULL;
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies = &rab_ass->ranap_rab_ass_req_message->msg.raB_AssignmentRequestIEs;
|
||||
int i;
|
||||
struct msgb *msg;
|
||||
@@ -346,6 +346,12 @@ continue_cleanloop:
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyItemFirst, &first);
|
||||
}
|
||||
|
||||
if (!rab) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Lookup PS RAB Assignment Request failed\n");
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send the modified RAB Assignment Request to the hNodeB, wait for the RAB Assignment Response */
|
||||
msg = ranap_rab_ass_req_encode(ies);
|
||||
if (!msg) {
|
||||
|
||||
@@ -35,6 +35,13 @@ struct osmo_tdef hnbgw_T_defs[] = {
|
||||
{.T = 3113, .default_val = 15, .desc = "Time to keep Paging record, for CN pools with more than one link" },
|
||||
{.T = 4, .default_val = 5, .desc = "Timeout to receive RANAP RESET ACKNOWLEDGE from an MSC/SGSN" },
|
||||
{.T = -31, .default_val = 15, .desc = "Timeout for establishing and releasing context maps (RUA <-> SCCP)" },
|
||||
{.T = -34, .default_val = 1000, .unit = OSMO_TDEF_MS, .desc = "Period to query network traffic stats from netfilter" },
|
||||
{
|
||||
.T = -35,
|
||||
.default_val = 60*60*24*7,
|
||||
.desc = "Clean up all hNodeB persistent state after this time of the hNodeB being disconnected."
|
||||
" Set to zero to never clear hNodeB persistent state. (default is 60*60*24*27 = a week)",
|
||||
},
|
||||
{.T = -1002, .default_val = 10, .desc = "Timeout for the HNB to respond to PS RAB Assignment Request" },
|
||||
{ }
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
SUBDIRS = \
|
||||
ranap_rab_ass \
|
||||
umts_cell_id \
|
||||
$(NULL)
|
||||
|
||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||
@@ -42,7 +43,6 @@ DISTCLEANFILES = \
|
||||
if ENABLE_EXT_TESTS
|
||||
python-tests:
|
||||
$(MAKE) vty-test
|
||||
$(MAKE) config-tests
|
||||
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
else
|
||||
@@ -68,6 +68,11 @@ vty-test: $(top_builddir)/src/osmo-hnbgw/osmo-hnbgw
|
||||
$(U) $(srcdir)/$(VTY_TEST)
|
||||
|
||||
# Test each config/*.cfg file with the corresponding config/*.vty transcript test.
|
||||
#
|
||||
# To be invoked manually only: This is not part of 'make check' because the
|
||||
# output may change when libosmo-sccp changes its VTY appearance, which can
|
||||
# cause annoying test fallout.
|
||||
#
|
||||
# Each config test runs an osmo-hnbgw process to talk to, so they must not run concurrently.
|
||||
# To prevent 'make -j N' from running these tests in parallel, call a script with a linear for-loop.
|
||||
.PHONY: config-tests
|
||||
|
||||
@@ -20,6 +20,7 @@ OsmoHNBGW(config-hnbgw)# list
|
||||
iups
|
||||
hnb UMTS_CELL_ID
|
||||
no hnb IDENTITY_INFO
|
||||
nft-kpi [TABLE_NAME]
|
||||
...
|
||||
|
||||
OsmoHNBGW(config-hnbgw)# plmn?
|
||||
@@ -82,3 +83,39 @@ hnbgw
|
||||
...
|
||||
rnc-id 42
|
||||
...
|
||||
|
||||
OsmoHNBGW(config-hnbgw)# nft-kpi?
|
||||
nft-kpi Retrieve traffic counters from nftables
|
||||
OsmoHNBGW(config-hnbgw)# nft-kpi ?
|
||||
[TABLE_NAME] Set a custom nft table name to use, instead of 'osmo-hnbgw'
|
||||
|
||||
OsmoHNBGW(config-hnbgw)# show running-config
|
||||
... !nft-kpi
|
||||
|
||||
OsmoHNBGW(config-hnbgw)# nft-kpi
|
||||
% WARNING: nft configuration changes need a restart of osmo-hnbgw
|
||||
OsmoHNBGW(config-hnbgw)# show running-config
|
||||
...
|
||||
hnbgw
|
||||
...
|
||||
nft-kpi
|
||||
...
|
||||
|
||||
OsmoHNBGW(config-hnbgw)# no nft-kpi
|
||||
% WARNING: nft configuration changes need a restart of osmo-hnbgw
|
||||
OsmoHNBGW(config-hnbgw)# show running-config
|
||||
... !nft-kpi
|
||||
|
||||
OsmoHNBGW(config-hnbgw)# nft-kpi maple
|
||||
% WARNING: nft configuration changes need a restart of osmo-hnbgw
|
||||
OsmoHNBGW(config-hnbgw)# show running-config
|
||||
...
|
||||
hnbgw
|
||||
...
|
||||
nft-kpi maple
|
||||
...
|
||||
|
||||
OsmoHNBGW(config-hnbgw)# no nft-kpi
|
||||
% WARNING: nft configuration changes need a restart of osmo-hnbgw
|
||||
OsmoHNBGW(config-hnbgw)# show running-config
|
||||
... !nft-kpi
|
||||
|
||||
@@ -5,4 +5,10 @@ AT_SETUP([ranap_rab_ass])
|
||||
AT_KEYWORDS([ranap_rab_ass])
|
||||
cat $abs_srcdir/ranap_rab_ass/ranap_rab_ass_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/ranap_rab_ass/ranap_rab_ass_test], [0], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([umts_cell_id])
|
||||
AT_KEYWORDS([umts_cell_id])
|
||||
cat $abs_srcdir/umts_cell_id/umts_cell_id_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/umts_cell_id/umts_cell_id_test], [0], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
38
tests/umts_cell_id/Makefile.am
Normal file
38
tests/umts_cell_id/Makefile.am
Normal file
@@ -0,0 +1,38 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
-ggdb3 \
|
||||
$(LIBASN1C_CFLAGS) \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOVTY_CFLAGS) \
|
||||
$(LIBOSMORANAP_CFLAGS) \
|
||||
$(LIBOSMOSIGTRAN_CFLAGS) \
|
||||
$(LIBOSMOMGCPCLIENT_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = -no-install
|
||||
|
||||
EXTRA_DIST = \
|
||||
umts_cell_id_test.ok \
|
||||
$(NULL)
|
||||
|
||||
check_PROGRAMS = \
|
||||
umts_cell_id_test \
|
||||
$(NULL)
|
||||
|
||||
umts_cell_id_test_SOURCES = \
|
||||
umts_cell_id_test.c \
|
||||
$(NULL)
|
||||
|
||||
umts_cell_id_test_LDADD = \
|
||||
$(top_builddir)/src/osmo-hnbgw/libhnbgw.la \
|
||||
$(NULL)
|
||||
|
||||
.PHONY: update_exp
|
||||
update_exp:
|
||||
$(builddir)/umts_cell_id_test >$(srcdir)/umts_cell_id_test.ok
|
||||
160
tests/umts_cell_id/umts_cell_id_test.c
Normal file
160
tests/umts_cell_id/umts_cell_id_test.c
Normal file
@@ -0,0 +1,160 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
|
||||
struct test {
|
||||
const char *id_str;
|
||||
int expect_rc;
|
||||
struct umts_cell_id id;
|
||||
};
|
||||
|
||||
struct test tests[] = {
|
||||
{
|
||||
.id_str = "001-01-L1-R1-S1-C1",
|
||||
.id = {
|
||||
.plmn = {
|
||||
.mcc = 1,
|
||||
.mnc = 1,
|
||||
},
|
||||
.lac = 1,
|
||||
.rac = 1,
|
||||
.sac = 1,
|
||||
.cid = 1,
|
||||
},
|
||||
},
|
||||
|
||||
/* ensure that a 3-digit MNC with leading zeroes is kept separate from two-digit MNC */
|
||||
{
|
||||
.id_str = "001-001-L1-R1-S1-C1",
|
||||
.id = {
|
||||
.plmn = {
|
||||
.mcc = 1,
|
||||
.mnc = 1,
|
||||
.mnc_3_digits = true,
|
||||
},
|
||||
.lac = 1,
|
||||
.rac = 1,
|
||||
.sac = 1,
|
||||
.cid = 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
.id_str = "001-099-L1-R1-S1-C1",
|
||||
.id = {
|
||||
.plmn = {
|
||||
.mcc = 1,
|
||||
.mnc = 99,
|
||||
.mnc_3_digits = true,
|
||||
},
|
||||
.lac = 1,
|
||||
.rac = 1,
|
||||
.sac = 1,
|
||||
.cid = 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
.id_str = "001-99-L1-R1-S1-C1",
|
||||
.id = {
|
||||
.plmn = {
|
||||
.mcc = 1,
|
||||
.mnc = 99,
|
||||
.mnc_3_digits = false,
|
||||
},
|
||||
.lac = 1,
|
||||
.rac = 1,
|
||||
.sac = 1,
|
||||
.cid = 1,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
.id_str = "999-999-L65534-R65535-S65535-C268435455",
|
||||
.id = {
|
||||
.plmn = {
|
||||
.mcc = 999,
|
||||
.mnc = 999,
|
||||
.mnc_3_digits = true,
|
||||
},
|
||||
.lac = 65534,
|
||||
.rac = 65535,
|
||||
.sac = 65535,
|
||||
.cid = (1 << 28) - 1,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
.id_str = "1000-001-L1-R1-S1-C1",
|
||||
.expect_rc = -EINVAL,
|
||||
},
|
||||
{
|
||||
.id_str = "001-001-L65535-R1-S1-C1",
|
||||
.expect_rc = -EINVAL,
|
||||
},
|
||||
/* TODO? There is no bounds checking on RAC and SAC.
|
||||
{
|
||||
.id_str = "001-001-L1-R65536-S1-C1",
|
||||
.expect_rc = -EINVAL,
|
||||
},
|
||||
{
|
||||
.id_str = "001-001-L1-R1-S65536-C1",
|
||||
.expect_rc = -EINVAL,
|
||||
},
|
||||
*/
|
||||
{
|
||||
.id_str = "001-001-L1-R1-S1-C268435456",
|
||||
.expect_rc = -EINVAL,
|
||||
},
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct test *t;
|
||||
for (t = tests; (t - tests) < ARRAY_SIZE(tests); t++) {
|
||||
int rc;
|
||||
struct umts_cell_id parsed;
|
||||
char to_str[128] = {};
|
||||
|
||||
printf("\"%s\"\n", t->id_str);
|
||||
|
||||
memset(&parsed, 0x2b, sizeof(parsed));
|
||||
rc = umts_cell_id_from_str(&parsed, t->id_str);
|
||||
if (rc != t->expect_rc) {
|
||||
printf(" ERROR: umts_cell_id_from_str(): expected rc == %d, got %d\n",
|
||||
t->expect_rc, rc);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
if (rc == t->expect_rc)
|
||||
printf(" expected rc != 0: ok\n");
|
||||
continue;
|
||||
}
|
||||
printf(" -> umts_cell_id_from_str(): ok\n");
|
||||
|
||||
rc = umts_cell_id_to_str_buf(to_str, sizeof(to_str), &parsed);
|
||||
if (rc <= 0) {
|
||||
printf(" ERROR: umts_cell_id_to_str_buf(): expected rc == 0, got %d\n", rc);
|
||||
continue;
|
||||
} else {
|
||||
printf(" -> umts_cell_id_to_str_buf(): ok\n");
|
||||
|
||||
if (strcmp(t->id_str, to_str))
|
||||
printf(" ERROR: conversion to umts_cell_id and back to string doesn't return the original string\n");
|
||||
printf(" -> \"%s\"\n", to_str);
|
||||
}
|
||||
|
||||
if (umts_cell_id_equal(&t->id, &parsed)) {
|
||||
printf(" umts_cell_id_equal(expected, parsed): ok\n");
|
||||
} else {
|
||||
char to_str_expect[128] = {};
|
||||
umts_cell_id_to_str_buf(to_str_expect, sizeof(to_str_expect), &t->id);
|
||||
printf(" ERROR: umts_cell_id_equal(expected, parsed) == false\n");
|
||||
printf(" expected %s\n", to_str_expect);
|
||||
printf(" got %s\n", to_str);
|
||||
printf(" expected %s\n", osmo_hexdump((void *)&t->id, sizeof(t->id)));
|
||||
printf(" got %s\n", osmo_hexdump((void *)&parsed, sizeof(t->id)));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
31
tests/umts_cell_id/umts_cell_id_test.ok
Normal file
31
tests/umts_cell_id/umts_cell_id_test.ok
Normal file
@@ -0,0 +1,31 @@
|
||||
"001-01-L1-R1-S1-C1"
|
||||
-> umts_cell_id_from_str(): ok
|
||||
-> umts_cell_id_to_str_buf(): ok
|
||||
-> "001-01-L1-R1-S1-C1"
|
||||
umts_cell_id_equal(expected, parsed): ok
|
||||
"001-001-L1-R1-S1-C1"
|
||||
-> umts_cell_id_from_str(): ok
|
||||
-> umts_cell_id_to_str_buf(): ok
|
||||
-> "001-001-L1-R1-S1-C1"
|
||||
umts_cell_id_equal(expected, parsed): ok
|
||||
"001-099-L1-R1-S1-C1"
|
||||
-> umts_cell_id_from_str(): ok
|
||||
-> umts_cell_id_to_str_buf(): ok
|
||||
-> "001-099-L1-R1-S1-C1"
|
||||
umts_cell_id_equal(expected, parsed): ok
|
||||
"001-99-L1-R1-S1-C1"
|
||||
-> umts_cell_id_from_str(): ok
|
||||
-> umts_cell_id_to_str_buf(): ok
|
||||
-> "001-99-L1-R1-S1-C1"
|
||||
umts_cell_id_equal(expected, parsed): ok
|
||||
"999-999-L65534-R65535-S65535-C268435455"
|
||||
-> umts_cell_id_from_str(): ok
|
||||
-> umts_cell_id_to_str_buf(): ok
|
||||
-> "999-999-L65534-R65535-S65535-C268435455"
|
||||
umts_cell_id_equal(expected, parsed): ok
|
||||
"1000-001-L1-R1-S1-C1"
|
||||
expected rc != 0: ok
|
||||
"001-001-L65535-R1-S1-C1"
|
||||
expected rc != 0: ok
|
||||
"001-001-L1-R1-S1-C268435456"
|
||||
expected rc != 0: ok
|
||||
Reference in New Issue
Block a user