mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw.git
synced 2025-11-02 13:03:26 +00:00
Compare commits
117 Commits
laforge/hn
...
4da67bb587
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4da67bb587 | ||
|
|
2c88bdb492 | ||
|
|
2601d9b3f0 | ||
|
|
315874927e | ||
|
|
cb4962f7f0 | ||
|
|
46a2c0afc2 | ||
|
|
6679c49133 | ||
|
|
4db21846e7 | ||
|
|
883048efd4 | ||
|
|
33cbc694f5 | ||
|
|
563f574178 | ||
|
|
d144d7b497 | ||
|
|
35a834078f | ||
|
|
160b372bf9 | ||
|
|
225a215b09 | ||
|
|
5eb95cf42a | ||
|
|
f91e32fc01 | ||
|
|
37b9a98a10 | ||
|
|
1f8fb9ae8c | ||
|
|
6a254f08c7 | ||
|
|
4e88e2b4ee | ||
|
|
894a3a455e | ||
|
|
135a8b079f | ||
|
|
df7dc3b6f4 | ||
|
|
8a573665c4 | ||
|
|
72c96d4792 | ||
|
|
b561656431 | ||
|
|
8aef494af5 | ||
|
|
718d71d3db | ||
|
|
24c850f8f0 | ||
|
|
cb03fc80c4 | ||
|
|
a3cceea45a | ||
|
|
1386a0da73 | ||
|
|
5ebcace219 | ||
|
|
bb3f454c8b | ||
|
|
ca8fc60231 | ||
|
|
3ef79eb800 | ||
|
|
5676dfbb75 | ||
|
|
05dd0272d0 | ||
|
|
0b4374bb9f | ||
|
|
f5b5c75d2b | ||
|
|
ca09bc0c44 | ||
|
|
70e2ee8a0a | ||
|
|
edc7baa3b8 | ||
|
|
050704c562 | ||
|
|
d25fa3723b | ||
|
|
bad546b431 | ||
|
|
af0a6aeb39 | ||
|
|
78fd0b97a9 | ||
|
|
580411823d | ||
|
|
5d91358114 | ||
|
|
d8b9a53a44 | ||
|
|
804ebe1593 | ||
|
|
8897a7764a | ||
|
|
7f3a50c619 | ||
|
|
79c5600bb6 | ||
|
|
aa2cefb173 | ||
|
|
d37f3dc20c | ||
|
|
f063d6bab8 | ||
|
|
619b81e5dc | ||
|
|
a824ebd8c2 | ||
|
|
71ad913d99 | ||
|
|
dbbfefad2c | ||
|
|
167f7aaac8 | ||
|
|
4ed837cba8 | ||
|
|
7784eb4e3b | ||
|
|
1a15261931 | ||
|
|
fb450888b6 | ||
|
|
a3c0a00573 | ||
|
|
e6d87ff967 | ||
|
|
1ac1d1277b | ||
|
|
1d4811e38c | ||
|
|
89504050ea | ||
|
|
7958955624 | ||
|
|
ca6f6fded9 | ||
|
|
3140381337 | ||
|
|
6f0197878a | ||
|
|
3fb99d143a | ||
|
|
30068ad391 | ||
|
|
1eb2a759ad | ||
|
|
280f9e6ca9 | ||
|
|
2fc8abeecf | ||
|
|
71f3169140 | ||
|
|
61e278a452 | ||
|
|
4468d8728e | ||
|
|
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 | ||
|
|
f64cdf85d1 |
@@ -19,7 +19,6 @@ SUBDIRS = \
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
contrib/osmo-hnbgw.spec.in \
|
||||
debian \
|
||||
git-version-gen \
|
||||
osmoappdesc.py \
|
||||
|
||||
10
TODO-RELEASE
10
TODO-RELEASE
@@ -1,10 +1,10 @@
|
||||
# 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
|
||||
libosmo-sigtran >2.1.0 osmo_sccp_addr_{create,update}()
|
||||
|
||||
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.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.11.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 2.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 1.7.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.7.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 1.7.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.14.0)
|
||||
|
||||
# Enable PFCP support for GTP tunnel mapping via UPF
|
||||
AC_ARG_ENABLE([pfcp], [AS_HELP_STRING([--enable-pfcp], [Build with PFCP support, for GTP tunnel mapping via UPF])],
|
||||
[osmo_ac_pfcp="$enableval"],[osmo_ac_pfcp="no"])
|
||||
if test "x$osmo_ac_pfcp" = "xyes" ; then
|
||||
PKG_CHECK_MODULES(LIBOSMOPFCP, libosmo-pfcp >= 0.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOPFCP, libosmo-pfcp >= 0.5.0)
|
||||
AC_DEFINE(ENABLE_PFCP, 1, [Define to build with PFCP support])
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_PFCP, test "x$osmo_ac_pfcp" = "xyes")
|
||||
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 !"
|
||||
@@ -32,9 +34,9 @@ export LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
osmo-build-dep.sh libosmocore "" --disable-doxygen
|
||||
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
||||
osmo-build-dep.sh libosmo-abis
|
||||
osmo-build-dep.sh libosmo-netif
|
||||
osmo-build-dep.sh libosmo-sccp
|
||||
osmo-build-dep.sh libosmo-sigtran "" --disable-doxygen
|
||||
osmo-build-dep.sh libasn1c
|
||||
osmo-build-dep.sh osmo-iuh
|
||||
osmo-build-dep.sh osmo-mgw
|
||||
@@ -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
|
||||
|
||||
|
||||
134
debian/changelog
vendored
134
debian/changelog
vendored
@@ -1,3 +1,137 @@
|
||||
osmo-hnbgw (1.7.0) unstable; urgency=medium
|
||||
|
||||
[ Neels Janosch Hofmeyr ]
|
||||
* drop config.vty tests from make check
|
||||
* coverity CID#358071
|
||||
* coverity CID#358072
|
||||
* coverity CID#358070
|
||||
* coverity CID#358069
|
||||
* on RUA Connect failure, respond with RUA Disconnect
|
||||
* on RUA DT for unknown context, respond with RUA Disconnect
|
||||
|
||||
[ Harald Welte ]
|
||||
* hnb_persistent: Use incrementing counter for rate_ctr + stat_item index
|
||||
* Revert "hnb_persistent: Use incrementing counter for rate_ctr + stat_item index"
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib/jenkins: libosmo-sccp -> libosmo-sigtran
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Log cn_disconnect.ind cause
|
||||
* Fix SCCP RLSD not sent upon rx RUA Disconnect due to error condition
|
||||
* ps_rab_ass_fsm: Fix uninitialized ptr access
|
||||
* Add PFCP stats item group
|
||||
* hnbgw: Avoid using struct osmo_pfcp_cp_peer fields directly
|
||||
* hnbgw: Fix wrong map object retrieved from hashtable
|
||||
* Use new libosmo-sigtran APIs to access osmo_ss7_instance
|
||||
* jenkins.sh: libosmo-netif no longer depends on libosmo-abis
|
||||
* jenkins.sh: No need to build libosmo-sigtran with doxygen
|
||||
* map_sccp: Fix crash during ev MAP_SCCP_EV_RAN_DISC in st MAP_SCCP_ST_WAIT_CC
|
||||
* peek_l3_ul_nas: Split parsing logic based on PS/CS ctx
|
||||
* peek_l3_ul_nas: Improve RAU req parsing checks
|
||||
* peek_l3_ul_nas: Improve GMM Attach Req parsing checks
|
||||
* peek_l3_ul_nas: Parse PTMSI MI from GMM RAU req
|
||||
|
||||
-- Oliver Smith <osmith@sysmocom.de> Wed, 12 Feb 2025 14:37:35 +0100
|
||||
|
||||
osmo-hnbgw (1.6.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* 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.11.0),
|
||||
libosmo-sigtran-dev (>= 2.1.0),
|
||||
libosmo-abis-dev (>= 2.0.0),
|
||||
libosmo-netif-dev (>= 1.6.0),
|
||||
libosmo-mgcp-client-dev (>= 1.14.0),
|
||||
libosmo-hnbap-dev (>= 1.7.0),
|
||||
libosmo-ranap-dev (>= 1.7.0),
|
||||
libosmo-rua-dev (>= 1.7.0),
|
||||
libosmo-pfcp-dev (>= 0.5.0),
|
||||
libnftables-dev,
|
||||
osmo-gsm-manuals-dev (>= 1.6.0)
|
||||
Standards-Version: 3.9.8
|
||||
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:
|
||||
|
||||
@@ -13,7 +13,6 @@ hnbgw
|
||||
plmn 001 01
|
||||
iuh
|
||||
local-ip 0.0.0.0
|
||||
hnbap-allow-tmsi 1
|
||||
msc 0
|
||||
remote-addr my-msc
|
||||
sgsn 0
|
||||
|
||||
@@ -11,7 +11,6 @@ hnbgw
|
||||
plmn 001 01
|
||||
iuh
|
||||
local-ip 0.0.0.0
|
||||
hnbap-allow-tmsi 1
|
||||
mgw 0
|
||||
remote-ip 127.0.0.1
|
||||
local-port 2729
|
||||
|
||||
@@ -11,7 +11,6 @@ hnbgw
|
||||
plmn 001 01
|
||||
iuh
|
||||
local-ip 0.0.0.0
|
||||
hnbap-allow-tmsi 1
|
||||
mgw 0
|
||||
remote-ip 127.0.0.1
|
||||
local-port 2729
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
noinst_HEADERS = \
|
||||
vty.h \
|
||||
context_map.h hnbgw.h hnbgw_cn.h \
|
||||
hnbgw_hnbap.h hnbgw_rua.h hnbgw_ranap.h \
|
||||
kpi.h \
|
||||
ranap_rab_ass.h mgw_fsm.h tdefs.h \
|
||||
cnlink.h \
|
||||
context_map.h \
|
||||
hnb.h \
|
||||
hnb_persistent.h \
|
||||
hnbgw.h \
|
||||
hnbgw_cn.h \
|
||||
hnbgw_hnbap.h \
|
||||
hnbgw_pfcp.h \
|
||||
hnbgw_ranap.h \
|
||||
hnbgw_rua.h \
|
||||
hnbgw_sccp.h \
|
||||
kpi.h \
|
||||
mgw_fsm.h \
|
||||
nft_kpi.h \
|
||||
ps_rab_ass_fsm.h \
|
||||
ps_rab_fsm.h \
|
||||
ranap_rab_ass.h \
|
||||
tdefs.h \
|
||||
umts_cell_id.h \
|
||||
vty.h \
|
||||
$(NULL)
|
||||
|
||||
138
include/osmocom/hnbgw/cnlink.h
Normal file
138
include/osmocom/hnbgw/cnlink.h
Normal file
@@ -0,0 +1,138 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw_sccp.h>
|
||||
|
||||
struct hnbgw_cnpool;
|
||||
|
||||
enum hnbgw_cnlink_ctr {
|
||||
/* TODO: basic counters completely missing
|
||||
* ...
|
||||
*/
|
||||
CNLINK_CTR_RANAP_RX_UDT_RESET,
|
||||
CNLINK_CTR_RANAP_RX_UDT_RESET_ACK,
|
||||
CNLINK_CTR_RANAP_RX_UDT_PAGING,
|
||||
CNLINK_CTR_RANAP_RX_UDT_UNKNOWN,
|
||||
CNLINK_CTR_RANAP_RX_UDT_UNSUPPORTED,
|
||||
CNLINK_CTR_RANAP_RX_UDT_OVERLOAD_IND,
|
||||
CNLINK_CTR_RANAP_RX_UDT_ERROR_IND,
|
||||
|
||||
CNLINK_CTR_RANAP_TX_UDT_RESET,
|
||||
CNLINK_CTR_RANAP_TX_UDT_RESET_ACK,
|
||||
|
||||
/* SCCP Counters: */
|
||||
CNLINK_CTR_SCCP_N_UNITDATA_REQ,
|
||||
CNLINK_CTR_SCCP_N_UNITDATA_IND,
|
||||
CNLINK_CTR_SCCP_N_NOTICE_IND,
|
||||
CNLINK_CTR_SCCP_N_CONNECT_REQ,
|
||||
CNLINK_CTR_SCCP_N_CONNECT_CNF,
|
||||
CNLINK_CTR_SCCP_N_DATA_REQ,
|
||||
CNLINK_CTR_SCCP_N_DATA_IND,
|
||||
CNLINK_CTR_SCCP_N_DISCONNECT_REQ,
|
||||
CNLINK_CTR_SCCP_N_DISCONNECT_IND,
|
||||
CNLINK_CTR_SCCP_N_PCSTATE_IND,
|
||||
CNLINK_CTR_SCCP_RLSD_CN_ORIGIN,
|
||||
|
||||
/* Counters related to link selection from a CN pool. */
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_NEW,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_REATTACH,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_KNOWN,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_PAGED,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_ATTACH_LOST,
|
||||
CNLINK_CTR_CNPOOL_EMERG_FORWARDED,
|
||||
};
|
||||
#define CNLINK_CTR_INC(cnlink, x) rate_ctr_inc2((cnlink)->ctrs, x)
|
||||
|
||||
enum cnlink_stat {
|
||||
CNLINK_STAT_CONNECTED,
|
||||
};
|
||||
#define CNLINK_STAT(cnlink, x) osmo_stat_item_group_get_item((cnlink)->statg, x)
|
||||
#define CNLINK_STAT_SET(cnlink, x, val) osmo_stat_item_set(CNLINK_STAT(cnlink, x), val)
|
||||
|
||||
/* User provided configuration for struct hnbgw_cnlink. */
|
||||
struct hnbgw_cnlink_cfg {
|
||||
/* cs7 address book entry to indicate both the remote point-code of the peer, as well as which cs7 instance to
|
||||
* use. */
|
||||
char *remote_addr_name;
|
||||
|
||||
struct osmo_nri_ranges *nri_ranges;
|
||||
};
|
||||
|
||||
/* A CN peer, like 'msc 0' or 'sgsn 23' */
|
||||
struct hnbgw_cnlink {
|
||||
struct llist_head entry;
|
||||
|
||||
/* backpointer to CS or PS CN pool. */
|
||||
struct hnbgw_cnpool *pool;
|
||||
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
int nr;
|
||||
|
||||
struct hnbgw_cnlink_cfg vty;
|
||||
struct hnbgw_cnlink_cfg use;
|
||||
|
||||
/* To print in logging/VTY */
|
||||
char *name;
|
||||
|
||||
/* Copy of the address book entry use.remote_addr_name. */
|
||||
struct osmo_sccp_addr remote_addr;
|
||||
|
||||
/* The SCCP instance for the cs7 instance indicated by remote_addr_name. (Multiple hnbgw_cnlinks may use the
|
||||
* same hnbgw_sccp_user -- there is exactly one hnbgw_sccp_user per configured cs7 instance.) */
|
||||
struct hnbgw_sccp_user *hnbgw_sccp_user;
|
||||
|
||||
/* linked list of hnbgw_context_map */
|
||||
struct llist_head map_list;
|
||||
|
||||
bool allow_attach;
|
||||
bool allow_emerg;
|
||||
struct llist_head paging;
|
||||
|
||||
struct rate_ctr_group *ctrs;
|
||||
struct osmo_stat_item_group *statg;
|
||||
};
|
||||
|
||||
struct hnbgw_cnlink *hnbgw_cnlink_alloc(struct hnbgw_cnpool *cnpool, int nr);
|
||||
void hnbgw_cnlink_term_and_free(struct hnbgw_cnlink *cnlink);
|
||||
void hnbgw_cnlink_drop_sccp(struct hnbgw_cnlink *cnlink);
|
||||
int hnbgw_cnlink_set_name(struct hnbgw_cnlink *cnlink, const char *name);
|
||||
int hnbgw_cnlink_tx_ranap_reset(struct hnbgw_cnlink *cnlink);
|
||||
int hnbgw_cnlink_tx_ranap_reset_ack(struct hnbgw_cnlink *cnlink);
|
||||
|
||||
int hnbgw_cnlink_start_or_restart(struct hnbgw_cnlink *cnlink);
|
||||
|
||||
char *hnbgw_cnlink_sccp_addr_to_str(struct hnbgw_cnlink *cnlink, const struct osmo_sccp_addr *addr);
|
||||
|
||||
static inline struct osmo_sccp_instance *hnbgw_cnlink_sccp(const struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
if (!cnlink)
|
||||
return NULL;
|
||||
if (!cnlink->hnbgw_sccp_user)
|
||||
return NULL;
|
||||
return hnbgw_sccp_user_get_sccp_instance(cnlink->hnbgw_sccp_user);
|
||||
}
|
||||
|
||||
/* cnlink_fsm.c related: */
|
||||
extern struct osmo_fsm cnlink_fsm;
|
||||
bool cnlink_is_conn_ready(const struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_rx_reset_cmd(struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_rx_reset_ack(struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_resend_reset(struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_set_disconnected(struct hnbgw_cnlink *cnlink);
|
||||
|
||||
/* cnlink_paging.c related: */
|
||||
const char *cnlink_paging_add_ranap(struct hnbgw_cnlink *cnlink, const RANAP_PagingIEs_t *paging_ies);
|
||||
struct hnbgw_cnlink *cnlink_find_by_paging_mi(struct hnbgw_cnpool *cnpool, const struct osmo_mobile_identity *mi);
|
||||
|
||||
#define LOG_CNLINK(CNLINK, SUBSYS, LEVEL, FMT, ARGS...) \
|
||||
LOGP(SUBSYS, LEVEL, "(%s) " FMT, (CNLINK) ? (CNLINK)->name : "null", ##ARGS)
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
|
||||
@@ -28,13 +30,17 @@
|
||||
* - The RANAP message shall be at msgb_l2().
|
||||
*/
|
||||
enum map_rua_fsm_event {
|
||||
/* Receiving a RUA Connect from HNB. */
|
||||
/* Receiving a RUA Connect from HNB.
|
||||
* Parameter: struct msgb *ranap_msg */
|
||||
MAP_RUA_EV_RX_CONNECT,
|
||||
/* Receiving some data from HNB via RUA, to forward via SCCP to CN. */
|
||||
/* Receiving some data from HNB via RUA, to forward via SCCP to CN.
|
||||
* Parameter: struct msgb *ranap_msg */
|
||||
MAP_RUA_EV_RX_DIRECT_TRANSFER,
|
||||
/* Receiving a RUA Disconnect from HNB. */
|
||||
/* Receiving a RUA Disconnect from HNB.
|
||||
* Parameter: struct msgb *ranap_msg (can be NULL) */
|
||||
MAP_RUA_EV_RX_DISCONNECT,
|
||||
/* SCCP has received some data from CN to forward via RUA to HNB. */
|
||||
/* SCCP has received some data from CN to forward via RUA to HNB.
|
||||
* Parameter: struct msgb *ranap_msg */
|
||||
MAP_RUA_EV_TX_DIRECT_TRANSFER,
|
||||
/* The CN side is disconnected (e.g. received an SCCP Released), that means we are going gracefully disconnect
|
||||
* RUA, too. */
|
||||
@@ -49,25 +55,41 @@ enum map_rua_fsm_event {
|
||||
* MAP_SCCP_EV_TX_DATA_REQUEST, MAP_SCCP_EV_RAN_DISC.
|
||||
*/
|
||||
enum map_sccp_fsm_event {
|
||||
/* Receiving an SCCP CC from CN. */
|
||||
/* Receiving an SCCP CC from CN.
|
||||
* Parameter: struct msgb *ranap_msg, may be NULL or empty. */
|
||||
MAP_SCCP_EV_RX_CONNECTION_CONFIRM,
|
||||
/* Receiving some data from CN via SCCP, to forward via RUA to HNB. */
|
||||
/* Receiving some data from CN via SCCP, to forward via RUA to HNB.
|
||||
* Parameter: struct msgb *ranap_msg, may be NULL or empty. */
|
||||
MAP_SCCP_EV_RX_DATA_INDICATION,
|
||||
/* RUA has received some data from HNB to forward via SCCP to CN. */
|
||||
/* RUA has received some data from HNB to forward via SCCP to CN.
|
||||
* Parameter: struct msgb *ranap_msg. */
|
||||
MAP_SCCP_EV_TX_DATA_REQUEST,
|
||||
/* The RAN side received a Disconnect, that means we are going to expect SCCP to disconnect too.
|
||||
* CN should have received an Iu-ReleaseComplete with or before this, give CN a chance to send an SCCP RLSD;
|
||||
* after a timeout we will send a non-standard RLSD to the CN instead. */
|
||||
/* 3GPP TS 25.468 9.1.5: The RAN side received a RUA Disconnect.
|
||||
* - Under normal conditions (cause=Normal) the RUA Disconnect contains a RANAP Iu-ReleaseComplete.
|
||||
* On SCCP, the Iu-ReleaseComplete should still be forwarded as N-Data SCCP Data Form 1),
|
||||
* and we will expect the CN to send an SCCP RLSD soon. Hence, give CN a chance to send an SCCP RLSD;
|
||||
* after a timeout we will send a non-standard RLSD to the CN instead.
|
||||
* - Under error conditions, cause!=Normal and there's no RANAP message.
|
||||
* In that case, we need to tear down the associated SCCP link towards CN with an RLSD,
|
||||
* which in turn will tear down the upper layer Iu conn.
|
||||
*
|
||||
* Parameter: bool rua_disconnect_err_condition, whether the disconnect
|
||||
* happened under error or normal conditions, as per the above.
|
||||
*/
|
||||
MAP_SCCP_EV_RAN_DISC,
|
||||
/* The RAN released ungracefully. We will directly disconnect the SCCP connection, too. */
|
||||
/* The RAN released ungracefully. We will directly disconnect the SCCP connection, too.
|
||||
* Parameter: no parameter, NULL. */
|
||||
MAP_SCCP_EV_RAN_LINK_LOST,
|
||||
/* Receiving an SCCP RLSD from CN, or libosmo-sigtran tells us about SCCP connection timeout. All done. */
|
||||
/* Receiving an SCCP RLSD from CN, or libosmo-sigtran tells us about SCCP connection timeout. All done.
|
||||
* Parameter: struct msgb *ranap_msg, may be NULL or empty. */
|
||||
MAP_SCCP_EV_RX_RELEASED,
|
||||
/* The human admin asks to drop the current SCCP connection, by telnet VTY 'apply sccp' in presence of SCCP
|
||||
* config changes. */
|
||||
* config changes.
|
||||
* Parameter: no parameter, NULL. */
|
||||
MAP_SCCP_EV_USER_ABORT,
|
||||
/* The CN link can no longer work, for example a RANAP RESET was received from the cnlink that hosts this
|
||||
* context map. */
|
||||
* context map.
|
||||
* Parameter: no parameter, NULL. */
|
||||
MAP_SCCP_EV_CN_LINK_LOST,
|
||||
};
|
||||
|
||||
@@ -108,6 +130,10 @@ enum rab_state {
|
||||
RAB_STATE_ACTIVE,
|
||||
RAB_STATE_REL_REQ,
|
||||
};
|
||||
extern const struct value_string hnbgw_rab_state_names[];
|
||||
static inline const char *hnbgw_rab_state_name(enum rab_state val)
|
||||
{ return get_value_string(hnbgw_rab_state_names, val); }
|
||||
|
||||
|
||||
struct hnbgw_context_map {
|
||||
/* entry in the per-CN list of mappings */
|
||||
@@ -127,12 +153,28 @@ struct hnbgw_context_map {
|
||||
/* FSM handling the RUA state for rua_ctx_id. */
|
||||
struct osmo_fsm_inst *rua_fi;
|
||||
|
||||
/* State context related to field rua_fi above: */
|
||||
struct {
|
||||
/* Whether RUA Disconnect received from HNB happened as a normal condition or an error/abnormal condition.
|
||||
* This is known based on cause and/or RANAP message included in the RUA
|
||||
* Disconnect message, and tells us whether we should immediately
|
||||
* terminate the related SCCP session or wait for CN to finish it.
|
||||
* Defaults to false, only set to true explicitly when needed. */
|
||||
bool rua_disconnect_err_condition;
|
||||
} rua_fi_ctx;
|
||||
|
||||
/* Pointer to CN, to transceive SCCP. */
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
/* SCCP User SAP connection ID used in SCCP messages to/from the cn_link. */
|
||||
uint32_t scu_conn_id;
|
||||
/* FSM handling the SCCP state for scu_conn_id. */
|
||||
struct osmo_fsm_inst *sccp_fi;
|
||||
/* State context related to field sccp_fi above: */
|
||||
struct {
|
||||
/* List of cached packets received from RUA and to be forwarded
|
||||
once SCCP CReq is CC'ed and move to CONNECTED state. */
|
||||
struct llist_head wait_cc_tx_msg_list;
|
||||
} sccp_fi_ctx;
|
||||
|
||||
/* False for CS, true for PS */
|
||||
bool is_ps;
|
||||
@@ -158,15 +200,15 @@ struct hnbgw_context_map {
|
||||
* and Request and Response don't necessarily match the RAB IDs contained. In practice I only ever see a single
|
||||
* RAB matching in Request and Response, but we cannot rely on that to always be true.
|
||||
*
|
||||
* The state of each RAB's PFCP negotiation is kept separately in the list ps_rabs, and as soon as all RABs
|
||||
* The state of each RAB's PFCP negotiation is kept separately in the field ps_rab_list, and as soon as all RABs
|
||||
* appearing in a PS RAB Assignment message have completed their PFCP setup, we can replace the GTP info for the
|
||||
* RAB IDs and forward the RAB Assignment Request to HNB / the RAB Assignment Response to CN.
|
||||
*/
|
||||
struct llist_head ps_rab_ass;
|
||||
struct llist_head ps_rab_ass_list;
|
||||
|
||||
/* All PS RABs and their GTP tunnel mappings. list of struct ps_rab. Each ps_rab FSM handles the PFCP
|
||||
* communication for one particular RAB ID. */
|
||||
struct llist_head ps_rabs;
|
||||
struct llist_head ps_rab_list;
|
||||
|
||||
/* RAB state tracking. As RAB-ID is an 8-bit integer, we need 256 elements in the array */
|
||||
uint8_t rab_state[256];
|
||||
|
||||
76
include/osmocom/hnbgw/hnb.h
Normal file
76
include/osmocom/hnbgw/hnb.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/hashtable.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ranap/RANAP_CN-DomainIndicator.h>
|
||||
|
||||
#define DEBUG
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_pool.h>
|
||||
|
||||
#include <osmocom/hnbgw/umts_cell_id.h>
|
||||
#include <osmocom/hnbgw/nft_kpi.h>
|
||||
#include <osmocom/hnbgw/cnlink.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
|
||||
#define HNB_STORE_RAB_DURATIONS_INTERVAL 1 /* seconds */
|
||||
|
||||
#define LOGHNB(HNB_CTX, ss, lvl, fmt, args ...) \
|
||||
LOGP(ss, lvl, "(%s) " fmt, hnb_context_name(HNB_CTX), ## args)
|
||||
|
||||
enum hnb_ctrl_node {
|
||||
CTRL_NODE_HNB = _LAST_CTRL_NODE,
|
||||
_LAST_CTRL_NODE_HNB
|
||||
};
|
||||
|
||||
/* The lifecycle of the hnb_context object is the same as its conn */
|
||||
struct hnb_context {
|
||||
/*! Entry in HNB-global list of HNB */
|
||||
struct llist_head list;
|
||||
/*! SCTP socket + write queue for Iuh to this specific HNB */
|
||||
struct osmo_stream_srv *conn;
|
||||
/*! copied from HNB-Identity-Info IE */
|
||||
char identity_info[256];
|
||||
/*! copied from Cell Identity IE */
|
||||
struct umts_cell_id id;
|
||||
|
||||
/*! SCTP stream ID for HNBAP */
|
||||
uint16_t hnbap_stream;
|
||||
/*! SCTP stream ID for RUA */
|
||||
uint16_t rua_stream;
|
||||
|
||||
/*! True if a HNB-REGISTER-REQ from this HNB has been accepted. */
|
||||
bool hnb_registered;
|
||||
|
||||
/* linked list of hnbgw_context_map */
|
||||
struct llist_head map_list;
|
||||
|
||||
/*! pointer to the associated hnb persistent state. Always present after HNB-Register */
|
||||
struct hnb_persistent *persistent;
|
||||
};
|
||||
|
||||
|
||||
int hnbgw_rua_accept_cb(struct osmo_stream_srv_link *srv, int fd);
|
||||
int hnb_ctrl_cmds_install(void);
|
||||
int hnb_ctrl_node_lookup(void *data, vector vline, int *node_type, void **node_data, int *i);
|
||||
|
||||
struct hnb_context *hnb_context_by_identity_info(const char *identity_info);
|
||||
const char *hnb_context_name(struct hnb_context *ctx);
|
||||
|
||||
void hnb_context_release(struct hnb_context *ctx);
|
||||
void hnb_context_release_ue_state(struct hnb_context *ctx);
|
||||
|
||||
unsigned long long hnb_get_updowntime(const struct hnb_context *ctx);
|
||||
void hnb_store_rab_durations(struct hnb_context *hnb);
|
||||
193
include/osmocom/hnbgw/hnb_persistent.h
Normal file
193
include/osmocom/hnbgw/hnb_persistent.h
Normal file
@@ -0,0 +1,193 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/hashtable.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ranap/RANAP_CN-DomainIndicator.h>
|
||||
|
||||
#define DEBUG
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_pool.h>
|
||||
|
||||
#include <osmocom/hnbgw/umts_cell_id.h>
|
||||
#include <osmocom/hnbgw/nft_kpi.h>
|
||||
#include <osmocom/hnbgw/cnlink.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
|
||||
#define LOG_HNBP(HNBP, lvl, fmt, args...) \
|
||||
LOGP(DHNB, lvl, "(%s) " fmt, \
|
||||
(HNBP) ? \
|
||||
(((HNBP)->id_str && *(HNBP)->id_str) ? (HNBP)->id_str : "no-cell-id") \
|
||||
: "null", ## args)
|
||||
|
||||
|
||||
enum hnb_rate_ctr {
|
||||
HNB_CTR_IUH_ESTABLISHED,
|
||||
HNB_CTR_RANAP_PS_ERR_IND_UL,
|
||||
HNB_CTR_RANAP_CS_ERR_IND_UL,
|
||||
HNB_CTR_RANAP_PS_RESET_REQ_UL,
|
||||
HNB_CTR_RANAP_CS_RESET_REQ_UL,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_REQ,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_REQ,
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_REQ_UNEXP,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_REQ_UNEXP,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_CNF,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_CNF,
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_CNF_UNEXP,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_CNF_UNEXP,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_FAIL,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_FAIL,
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_FAIL_UNEXP,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_FAIL_UNEXP,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_MOD_REQ,
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_REQ,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_MOD_CNF,
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_CNF,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_MOD_FAIL,
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_FAIL,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_REQ,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ,
|
||||
HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ_ABNORMAL,
|
||||
HNB_CTR_RANAP_PS_RAB_REL_REQ_UNEXP,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ_UNEXP,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_CNF,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_CNF,
|
||||
HNB_CTR_RANAP_PS_RAB_REL_CNF_UNEXP,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_CNF_UNEXP,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_FAIL,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_FAIL,
|
||||
HNB_CTR_RANAP_PS_RAB_REL_FAIL_UNEXP,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_FAIL_UNEXP,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT,
|
||||
HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT_ABNORMAL,
|
||||
|
||||
HNB_CTR_RUA_ERR_IND,
|
||||
|
||||
HNB_CTR_RUA_PS_CONNECT_UL,
|
||||
HNB_CTR_RUA_CS_CONNECT_UL,
|
||||
|
||||
HNB_CTR_RUA_PS_DISCONNECT_UL,
|
||||
HNB_CTR_RUA_CS_DISCONNECT_UL,
|
||||
HNB_CTR_RUA_PS_DISCONNECT_DL,
|
||||
HNB_CTR_RUA_CS_DISCONNECT_DL,
|
||||
|
||||
HNB_CTR_RUA_PS_DT_UL,
|
||||
HNB_CTR_RUA_CS_DT_UL,
|
||||
HNB_CTR_RUA_PS_DT_DL,
|
||||
HNB_CTR_RUA_CS_DT_DL,
|
||||
|
||||
HNB_CTR_RUA_UDT_UL,
|
||||
HNB_CTR_RUA_UDT_DL,
|
||||
|
||||
HNB_CTR_PS_PAGING_ATTEMPTED,
|
||||
HNB_CTR_CS_PAGING_ATTEMPTED,
|
||||
|
||||
HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL,
|
||||
|
||||
HNB_CTR_DTAP_CS_LU_REQ,
|
||||
HNB_CTR_DTAP_CS_LU_ACC,
|
||||
HNB_CTR_DTAP_CS_LU_REJ,
|
||||
|
||||
HNB_CTR_DTAP_PS_ATT_REQ,
|
||||
HNB_CTR_DTAP_PS_ATT_ACK,
|
||||
HNB_CTR_DTAP_PS_ATT_REJ,
|
||||
|
||||
HNB_CTR_DTAP_PS_RAU_REQ,
|
||||
HNB_CTR_DTAP_PS_RAU_ACK,
|
||||
HNB_CTR_DTAP_PS_RAU_REJ,
|
||||
|
||||
HNB_CTR_GTPU_PACKETS_UL,
|
||||
HNB_CTR_GTPU_TOTAL_BYTES_UL,
|
||||
HNB_CTR_GTPU_UE_BYTES_UL,
|
||||
HNB_CTR_GTPU_PACKETS_DL,
|
||||
HNB_CTR_GTPU_TOTAL_BYTES_DL,
|
||||
HNB_CTR_GTPU_UE_BYTES_DL,
|
||||
};
|
||||
|
||||
enum hnb_stat {
|
||||
HNB_STAT_UPTIME_SECONDS,
|
||||
};
|
||||
|
||||
#define HNBP_CTR(hnbp, x) rate_ctr_group_get_ctr((hnbp)->ctrs, x)
|
||||
#define HNBP_CTR_INC(hnbp, x) rate_ctr_inc(HNBP_CTR(hnbp, x))
|
||||
#define HNBP_CTR_ADD(hnbp, x, y) rate_ctr_add2((hnbp)->ctrs, x, y)
|
||||
|
||||
#define HNBP_STAT(hbp, x) osmo_stat_item_group_get_item((hnbp)->statg, x)
|
||||
#define HNBP_STAT_SET(hnbp, x, val) osmo_stat_item_set(HNBP_STAT(hnbp, x), val)
|
||||
|
||||
/* persistent data for one HNB. This continues to exist even as conn / hnb_context is deleted on disconnect */
|
||||
struct hnb_persistent {
|
||||
/*! Entry in HNBGW-global list of hnb_persistent */
|
||||
struct llist_head list;
|
||||
/*! Entry in hash table g_hnbgw->hnb_persistent_by_id. */
|
||||
struct hlist_node node_by_id;
|
||||
/*! back-pointer to hnb_context. Can be NULL if no context at this point */
|
||||
struct hnb_context *ctx;
|
||||
|
||||
/*! unique cell identity; copied from HNB REGISTER REQ */
|
||||
struct umts_cell_id id;
|
||||
/*! stringified version of the cell identiy above (for printing/naming) */
|
||||
const char *id_str;
|
||||
|
||||
/*! copied from HNB-Identity-Info IE */
|
||||
time_t updowntime;
|
||||
|
||||
struct rate_ctr_group *ctrs;
|
||||
struct osmo_stat_item_group *statg;
|
||||
|
||||
/* State that the main thread needs in order to know what was requested from the nft worker threads and what
|
||||
* still needs to be requested. */
|
||||
struct {
|
||||
/* Whether a persistent named counter was added in nftables for this cell id. */
|
||||
bool persistent_counter_added;
|
||||
|
||||
/* The last hNodeB GTP-U address we asked the nft maintenance thread to set up.
|
||||
* osmo_sockaddr_str_is_nonzero(addr_remote) == false when no rules were added yet, and when
|
||||
* we asked the nft maintenance thread to remove the rules for this hNodeB because it has
|
||||
* disconnected. */
|
||||
struct osmo_sockaddr_str addr_remote;
|
||||
|
||||
/* the nft handles needed to clean up the UL and DL rules when the hNodeB disconnects,
|
||||
* and the last seen counter value gotten from nft. */
|
||||
struct {
|
||||
struct nft_kpi_handle h;
|
||||
struct nft_kpi_val v;
|
||||
} ul;
|
||||
struct {
|
||||
struct nft_kpi_handle h;
|
||||
struct nft_kpi_val v;
|
||||
} dl;
|
||||
} nft_kpi;
|
||||
|
||||
struct osmo_timer_list disconnected_timeout;
|
||||
};
|
||||
|
||||
struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id);
|
||||
struct hnb_persistent *hnb_persistent_find_by_id(const struct umts_cell_id *id);
|
||||
void hnb_persistent_registered(struct hnb_persistent *hnbp);
|
||||
void hnb_persistent_deregistered(struct hnb_persistent *hnbp);
|
||||
void hnb_persistent_free(struct hnb_persistent *hnbp);
|
||||
|
||||
unsigned long long hnbp_get_updowntime(const struct hnb_persistent *hnbp);
|
||||
@@ -6,8 +6,8 @@
|
||||
#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>
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ranap/RANAP_CN-DomainIndicator.h>
|
||||
@@ -18,8 +18,11 @@
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_pool.h>
|
||||
|
||||
#include <osmocom/hnbgw/nft_kpi.h>
|
||||
#include <osmocom/hnbgw/cnlink.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
|
||||
#define STORE_UPTIME_INTERVAL 10 /* seconds */
|
||||
#define HNB_STORE_RAB_DURATIONS_INTERVAL 1 /* seconds */
|
||||
|
||||
enum {
|
||||
DMAIN,
|
||||
@@ -29,14 +32,12 @@ enum {
|
||||
DMGW,
|
||||
DHNB,
|
||||
DCN,
|
||||
DNFT,
|
||||
};
|
||||
|
||||
extern const struct log_info hnbgw_log_info;
|
||||
extern struct vty_app_info hnbgw_vty_info;
|
||||
|
||||
#define LOGHNB(HNB_CTX, ss, lvl, fmt, args ...) \
|
||||
LOGP(ss, lvl, "(%s) " fmt, hnb_context_name(HNB_CTX), ## args)
|
||||
|
||||
#define DOMAIN_CS RANAP_CN_DomainIndicator_cs_domain
|
||||
#define DOMAIN_PS RANAP_CN_DomainIndicator_ps_domain
|
||||
|
||||
@@ -46,11 +47,6 @@ static inline const char *ranap_domain_name(RANAP_CN_DomainIndicator_t domain)
|
||||
return get_value_string(ranap_domain_names, domain);
|
||||
}
|
||||
|
||||
enum hnb_ctrl_node {
|
||||
CTRL_NODE_HNB = _LAST_CTRL_NODE,
|
||||
_LAST_CTRL_NODE_HNB
|
||||
};
|
||||
|
||||
#define HNBGW_LOCAL_IP_DEFAULT "0.0.0.0"
|
||||
/* TODO: CS and PS now both connect to OsmoSTP, i.e. that's always going to be the same address. Drop the
|
||||
* duplicity. */
|
||||
@@ -60,6 +56,8 @@ enum hnb_ctrl_node {
|
||||
#define DEFAULT_PC_HNBGW ((23 << 3) + 5)
|
||||
#define DEFAULT_PC_MSC ((23 << 3) + 1)
|
||||
#define DEFAULT_PC_SGSN ((23 << 3) + 4)
|
||||
#define DEFAULT_ADDR_NAME_MSC "addr-dyn-msc-default"
|
||||
#define DEFAULT_ADDR_NAME_SGSN "addr-dyn-sgsn-default"
|
||||
|
||||
/* 25.467 Section 7.1 */
|
||||
#define IUH_DEFAULT_SCTP_PORT 29169
|
||||
@@ -73,215 +71,8 @@ enum hnb_ctrl_node {
|
||||
|
||||
#define IUH_MSGB_SIZE 2048
|
||||
|
||||
enum hnb_rate_ctr {
|
||||
HNB_CTR_IUH_ESTABLISHED,
|
||||
HNB_CTR_RANAP_PS_ERR_IND_UL,
|
||||
HNB_CTR_RANAP_CS_ERR_IND_UL,
|
||||
HNB_CTR_RANAP_PS_RESET_REQ_UL,
|
||||
HNB_CTR_RANAP_CS_RESET_REQ_UL,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_REQ,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_REQ,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_CNF,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_CNF,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_ACT_FAIL,
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_FAIL,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_MOD_REQ,
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_REQ,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_MOD_CNF,
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_CNF,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_MOD_FAIL,
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_FAIL,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_REQ,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_CNF,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_CNF,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_FAIL,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_FAIL,
|
||||
|
||||
HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT,
|
||||
HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT,
|
||||
|
||||
HNB_CTR_RUA_ERR_IND,
|
||||
|
||||
HNB_CTR_RUA_PS_CONNECT_UL,
|
||||
HNB_CTR_RUA_CS_CONNECT_UL,
|
||||
|
||||
HNB_CTR_RUA_PS_DISCONNECT_UL,
|
||||
HNB_CTR_RUA_CS_DISCONNECT_UL,
|
||||
HNB_CTR_RUA_PS_DISCONNECT_DL,
|
||||
HNB_CTR_RUA_CS_DISCONNECT_DL,
|
||||
|
||||
HNB_CTR_RUA_PS_DT_UL,
|
||||
HNB_CTR_RUA_CS_DT_UL,
|
||||
HNB_CTR_RUA_PS_DT_DL,
|
||||
HNB_CTR_RUA_CS_DT_DL,
|
||||
|
||||
HNB_CTR_RUA_UDT_UL,
|
||||
HNB_CTR_RUA_UDT_DL,
|
||||
|
||||
HNB_CTR_PS_PAGING_ATTEMPTED,
|
||||
HNB_CTR_CS_PAGING_ATTEMPTED,
|
||||
|
||||
HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL,
|
||||
};
|
||||
|
||||
enum hnb_stat {
|
||||
HNB_STAT_UPTIME_SECONDS,
|
||||
};
|
||||
|
||||
struct umts_cell_id {
|
||||
uint16_t mcc; /*!< Mobile Country Code (0-999) */
|
||||
uint16_t mnc; /*!< Mobile Network Code (0-999) */
|
||||
uint16_t lac; /*!< Locaton Area Code (1-65534) */
|
||||
uint16_t rac; /*!< Routing Area Code (0-255) */
|
||||
uint16_t sac; /*!< Service Area Code */
|
||||
uint32_t cid; /*!< Cell ID */
|
||||
};
|
||||
const char *umts_cell_id_name(const struct umts_cell_id *ucid);
|
||||
int umts_cell_id_from_str(struct umts_cell_id *ucid, const char *instr);
|
||||
|
||||
/*! are both given umts_cell_id euqal? */
|
||||
static inline bool umts_cell_id_equal(const struct umts_cell_id *a, const struct umts_cell_id *b)
|
||||
{
|
||||
if (a->mcc != b->mcc)
|
||||
return false;
|
||||
if (a->mnc != b->mnc)
|
||||
return false;
|
||||
if (a->lac != b->lac)
|
||||
return false;
|
||||
if (a->rac != b->rac)
|
||||
return false;
|
||||
if (a->sac != b->sac)
|
||||
return false;
|
||||
if (a->cid != b->cid)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct hnbgw_context_map;
|
||||
|
||||
/* osmo-hnbgw keeps a single hnbgw_sccp_user per osmo_sccp_instance, for the local point-code and SSN == RANAP.
|
||||
* This relates the (opaque) osmo_sccp_user to osmo-hnbgw's per-ss7 state. */
|
||||
struct hnbgw_sccp_user {
|
||||
/* entry in g_hnbgw->sccp.users */
|
||||
struct llist_head entry;
|
||||
|
||||
/* logging context */
|
||||
char *name;
|
||||
|
||||
/* Which 'cs7 instance' is this for? Below sccp_user is registered at the osmo_sccp_instance ss7->sccp. */
|
||||
struct osmo_ss7_instance *ss7;
|
||||
|
||||
/* Local address: cs7 instance's primary PC if present, else the default HNBGW PC; with SSN == RANAP. */
|
||||
struct osmo_sccp_addr local_addr;
|
||||
|
||||
/* osmo_sccp API state for above local address on above ss7 instance. */
|
||||
struct osmo_sccp_user *sccp_user;
|
||||
|
||||
/* Fast access to the hnbgw_context_map responsible for a given SCCP conn_id of the ss7->sccp instance.
|
||||
* hlist_node: hnbgw_context_map->hnbgw_sccp_user_entry. */
|
||||
DECLARE_HASHTABLE(hnbgw_context_map_by_conn_id, 6);
|
||||
};
|
||||
|
||||
#define LOG_HSI(HNBGW_SCCP_INST, SUBSYS, LEVEL, FMT, ARGS...) \
|
||||
LOGP(SUBSYS, LEVEL, "(%s) " FMT, (HNBGW_SCCP_INST) ? (HNBGW_SCCP_INST)->name : "null", ##ARGS)
|
||||
|
||||
/* User provided configuration for struct hnbgw_cnpool. */
|
||||
struct hnbgw_cnpool_cfg {
|
||||
uint8_t nri_bitlen;
|
||||
struct osmo_nri_ranges *null_nri_ranges;
|
||||
};
|
||||
|
||||
/* User provided configuration for struct hnbgw_cnlink. */
|
||||
struct hnbgw_cnlink_cfg {
|
||||
/* cs7 address book entry to indicate both the remote point-code of the peer, as well as which cs7 instance to
|
||||
* use. */
|
||||
char *remote_addr_name;
|
||||
|
||||
struct osmo_nri_ranges *nri_ranges;
|
||||
};
|
||||
|
||||
/* Collection of CN peers to distribute UE connections across. MSCs for DOMAIN_CS, SGSNs for DOMAIN_PS. */
|
||||
struct hnbgw_cnpool {
|
||||
RANAP_CN_DomainIndicator_t domain;
|
||||
|
||||
/* CN pool string used in VTY config and logging, "iucs" or "iups". */
|
||||
const char *pool_name;
|
||||
/* CN peer string used in VTY config and logging, "msc" or "sgsn". */
|
||||
const char *peer_name;
|
||||
/* What we use as the remote MSC/SGSN point-code if the user does not configure any address. */
|
||||
uint32_t default_remote_pc;
|
||||
|
||||
struct hnbgw_cnpool_cfg vty;
|
||||
struct hnbgw_cnpool_cfg use;
|
||||
|
||||
/* List of struct hnbgw_cnlink */
|
||||
struct llist_head cnlinks;
|
||||
|
||||
unsigned int round_robin_next_nr;
|
||||
/* Emergency calls potentially select a different set of MSCs, so to not mess up the normal round-robin
|
||||
* behavior, emergency calls need a separate round-robin counter. */
|
||||
unsigned int round_robin_next_emerg_nr;
|
||||
|
||||
/* rate counter group that child hnbgw_cnlinks should use (points to msc_ctrg_desc or sgsn_ctrg_desc) */
|
||||
const struct rate_ctr_group_desc *cnlink_ctrg_desc;
|
||||
|
||||
/* Running counters for this pool */
|
||||
struct rate_ctr_group *ctrs;
|
||||
};
|
||||
|
||||
#define CNPOOL_CTR_INC(cnpool, x) rate_ctr_inc2((cnpool)->ctrs, x)
|
||||
|
||||
/* A CN peer, like 'msc 0' or 'sgsn 23' */
|
||||
struct hnbgw_cnlink {
|
||||
struct llist_head entry;
|
||||
|
||||
/* backpointer to CS or PS CN pool. */
|
||||
struct hnbgw_cnpool *pool;
|
||||
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
int nr;
|
||||
|
||||
struct hnbgw_cnlink_cfg vty;
|
||||
struct hnbgw_cnlink_cfg use;
|
||||
|
||||
/* To print in logging/VTY */
|
||||
char *name;
|
||||
|
||||
/* Copy of the address book entry use.remote_addr_name. */
|
||||
struct osmo_sccp_addr remote_addr;
|
||||
|
||||
/* The SCCP instance for the cs7 instance indicated by remote_addr_name. (Multiple hnbgw_cnlinks may use the
|
||||
* same hnbgw_sccp_user -- there is exactly one hnbgw_sccp_user per configured cs7 instance.) */
|
||||
struct hnbgw_sccp_user *hnbgw_sccp_user;
|
||||
|
||||
/* linked list of hnbgw_context_map */
|
||||
struct llist_head map_list;
|
||||
|
||||
bool allow_attach;
|
||||
bool allow_emerg;
|
||||
struct llist_head paging;
|
||||
|
||||
struct rate_ctr_group *ctrs;
|
||||
};
|
||||
|
||||
#define LOG_CNLINK(CNLINK, SUBSYS, LEVEL, FMT, ARGS...) \
|
||||
LOGP(SUBSYS, LEVEL, "(%s) " FMT, (CNLINK) ? (CNLINK)->name : "null", ##ARGS)
|
||||
|
||||
#define CNLINK_CTR_INC(cnlink, x) rate_ctr_inc2((cnlink)->ctrs, x)
|
||||
|
||||
struct hnbgw_cnlink *cnlink_get_nr(struct hnbgw_cnpool *cnpool, int nr, bool create_if_missing);
|
||||
|
||||
static inline bool cnlink_is_cs(const struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
return cnlink && cnlink->pool->domain == DOMAIN_CS;
|
||||
@@ -292,80 +83,6 @@ static inline bool cnlink_is_ps(const struct hnbgw_cnlink *cnlink)
|
||||
return cnlink && cnlink->pool->domain == DOMAIN_PS;
|
||||
}
|
||||
|
||||
static inline struct osmo_sccp_instance *cnlink_sccp(const struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
if (!cnlink)
|
||||
return NULL;
|
||||
if (!cnlink->hnbgw_sccp_user)
|
||||
return NULL;
|
||||
if (!cnlink->hnbgw_sccp_user->ss7)
|
||||
return NULL;
|
||||
return cnlink->hnbgw_sccp_user->ss7->sccp;
|
||||
}
|
||||
|
||||
/* The lifecycle of the hnb_context object is the same as its conn */
|
||||
struct hnb_context {
|
||||
/*! Entry in HNB-global list of HNB */
|
||||
struct llist_head list;
|
||||
/*! SCTP socket + write queue for Iuh to this specific HNB */
|
||||
struct osmo_stream_srv *conn;
|
||||
/*! copied from HNB-Identity-Info IE */
|
||||
char identity_info[256];
|
||||
/*! copied from Cell Identity IE */
|
||||
struct umts_cell_id id;
|
||||
|
||||
/*! SCTP stream ID for HNBAP */
|
||||
uint16_t hnbap_stream;
|
||||
/*! SCTP stream ID for RUA */
|
||||
uint16_t rua_stream;
|
||||
|
||||
/*! True if a HNB-REGISTER-REQ from this HNB has been accepted. */
|
||||
bool hnb_registered;
|
||||
|
||||
/* linked list of hnbgw_context_map */
|
||||
struct llist_head map_list;
|
||||
|
||||
/*! pointer to the associated hnb persistent state. Always present after HNB-Register */
|
||||
struct hnb_persistent *persistent;
|
||||
};
|
||||
|
||||
#define HNBP_CTR(hnbp, x) rate_ctr_group_get_ctr((hnbp)->ctrs, x)
|
||||
#define HNBP_CTR_INC(hnbp, x) rate_ctr_inc(HNBP_CTR(hnbp, x))
|
||||
#define HNBP_CTR_ADD(hnbp, x, y) rate_ctr_add2((hnbp)->ctrs, x, y)
|
||||
|
||||
#define HNBP_STAT(hbp, x) osmo_stat_item_group_get_item((hnbp)->statg, x)
|
||||
#define HNBP_STAT_SET(hnbp, x, val) osmo_stat_item_set(HNBP_STAT(hnbp, x), val)
|
||||
|
||||
/* persistent data for one HNB. This continues to exist even as conn / hnb_context is deleted on disconnect */
|
||||
struct hnb_persistent {
|
||||
/*! Entry in HNBGW-global list of hnb_persistent */
|
||||
struct llist_head list;
|
||||
/*! back-pointer to hnb_context. Can be NULL if no context at this point */
|
||||
struct hnb_context *ctx;
|
||||
|
||||
/*! unique cell identity; copied from HNB REGISTER REQ */
|
||||
struct umts_cell_id id;
|
||||
/*! stringified version of the cell identiy above (for printing/naming) */
|
||||
const char *id_str;
|
||||
|
||||
/*! copied from HNB-Identity-Info IE */
|
||||
time_t updowntime;
|
||||
|
||||
struct rate_ctr_group *ctrs;
|
||||
struct osmo_stat_item_group *statg;
|
||||
};
|
||||
|
||||
struct ue_context {
|
||||
/*! Entry in the HNB-global list of UE */
|
||||
struct llist_head list;
|
||||
/*! Unique Context ID for this UE */
|
||||
uint32_t context_id;
|
||||
char imsi[16+1];
|
||||
uint32_t tmsi;
|
||||
/*! UE is serviced via this HNB */
|
||||
struct hnb_context *hnb;
|
||||
};
|
||||
|
||||
struct hnbgw {
|
||||
struct {
|
||||
const char *iuh_local_ip;
|
||||
@@ -391,16 +108,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;
|
||||
@@ -410,9 +135,9 @@ struct hnbgw {
|
||||
struct llist_head users;
|
||||
|
||||
/* Pool of core network peers: MSCs for IuCS */
|
||||
struct hnbgw_cnpool cnpool_iucs;
|
||||
struct hnbgw_cnpool *cnpool_iucs;
|
||||
/* Pool of core network peers: SGSNs for IuPS */
|
||||
struct hnbgw_cnpool cnpool_iups;
|
||||
struct hnbgw_cnpool *cnpool_iups;
|
||||
} sccp;
|
||||
/* MGW pool, also includes the single MGCP client as fallback if no
|
||||
* pool is configured. */
|
||||
@@ -421,9 +146,17 @@ struct hnbgw {
|
||||
struct {
|
||||
struct osmo_pfcp_endpoint *ep;
|
||||
struct osmo_pfcp_cp_peer *cp_peer;
|
||||
/* Running counters for the PFCP conn */
|
||||
struct osmo_stat_item_group *statg;
|
||||
} pfcp;
|
||||
|
||||
struct osmo_timer_list hnb_store_rab_durations_timer;
|
||||
|
||||
struct {
|
||||
bool active;
|
||||
struct osmo_timer_list get_counters_timer;
|
||||
struct timespec next_timer;
|
||||
} nft_kpi;
|
||||
};
|
||||
|
||||
extern struct hnbgw *g_hnbgw;
|
||||
@@ -432,27 +165,8 @@ extern void *talloc_asn1_ctx;
|
||||
void g_hnbgw_alloc(void *ctx);
|
||||
|
||||
int hnbgw_rua_accept_cb(struct osmo_stream_srv_link *srv, int fd);
|
||||
int hnb_ctrl_cmds_install(void);
|
||||
int hnb_ctrl_node_lookup(void *data, vector vline, int *node_type, void **node_data, int *i);
|
||||
int hnbgw_mgw_setup(void);
|
||||
|
||||
struct hnb_context *hnb_context_by_identity_info(const char *identity_info);
|
||||
const char *hnb_context_name(struct hnb_context *ctx);
|
||||
|
||||
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_free(struct hnb_persistent *hnbp);
|
||||
|
||||
void hnbgw_vty_init(void);
|
||||
int hnbgw_vty_go_parent(struct vty *vty);
|
||||
|
||||
@@ -470,4 +184,4 @@ struct msgb *hnbgw_ranap_msg_alloc(const char *name);
|
||||
|
||||
int hnbgw_peek_l3_ul(struct hnbgw_context_map *map, struct msgb *ranap_msg);
|
||||
|
||||
unsigned long long hnb_get_updowntime(const struct hnb_context *ctx);
|
||||
uint32_t get_next_ue_ctx_id(void);
|
||||
|
||||
@@ -1,25 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
|
||||
struct hnbgw_cnlink *cnlink_alloc(struct hnbgw_cnpool *cnpool, int nr);
|
||||
struct hnbgw_cnlink *hnbgw_cnlink_find_by_addr(const struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_sccp_addr *remote_addr);
|
||||
struct hnbgw_cnlink *hnbgw_cnlink_select(struct hnbgw_context_map *map);
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
void hnbgw_cnpool_start(struct hnbgw_cnpool *cnpool);
|
||||
void hnbgw_cnpool_apply_cfg(struct hnbgw_cnpool *cnpool);
|
||||
void hnbgw_cnpool_cnlinks_start_or_restart(struct hnbgw_cnpool *cnpool);
|
||||
int hnbgw_cnlink_start_or_restart(struct hnbgw_cnlink *cnlink);
|
||||
#include <osmocom/ranap/RANAP_CN-DomainIndicator.h>
|
||||
|
||||
char *cnlink_sccp_addr_to_str(struct hnbgw_cnlink *cnlink, const struct osmo_sccp_addr *addr);
|
||||
|
||||
bool cnlink_is_conn_ready(const struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_rx_reset_cmd(struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_rx_reset_ack(struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_resend_reset(struct hnbgw_cnlink *cnlink);
|
||||
void cnlink_set_disconnected(struct hnbgw_cnlink *cnlink);
|
||||
struct hnbgw_context_map;
|
||||
|
||||
enum hnbgw_cnpool_ctr {
|
||||
/* TODO: basic counters completely missing
|
||||
@@ -31,33 +19,46 @@ enum hnbgw_cnpool_ctr {
|
||||
CNPOOL_CTR_EMERG_FORWARDED,
|
||||
CNPOOL_CTR_EMERG_LOST,
|
||||
};
|
||||
#define CNPOOL_CTR_INC(cnpool, x) rate_ctr_inc2((cnpool)->ctrs, x)
|
||||
|
||||
extern const struct rate_ctr_group_desc iucs_ctrg_desc;
|
||||
extern const struct rate_ctr_group_desc iups_ctrg_desc;
|
||||
|
||||
enum hnbgw_cnlink_ctr {
|
||||
/* TODO: basic counters completely missing
|
||||
* ...
|
||||
*/
|
||||
CNLINK_CTR_RANAP_RX_UDT_RESET,
|
||||
CNLINK_CTR_RANAP_RX_UDT_RESET_ACK,
|
||||
CNLINK_CTR_RANAP_RX_UDT_PAGING,
|
||||
CNLINK_CTR_RANAP_RX_UDT_UNKNOWN,
|
||||
CNLINK_CTR_RANAP_RX_UDT_UNSUPPORTED,
|
||||
CNLINK_CTR_RANAP_RX_UDT_OVERLOAD_IND,
|
||||
CNLINK_CTR_RANAP_RX_UDT_ERROR_IND,
|
||||
|
||||
CNLINK_CTR_RANAP_TX_UDT_RESET,
|
||||
CNLINK_CTR_RANAP_TX_UDT_RESET_ACK,
|
||||
|
||||
/* Counters related to link selection from a CN pool. */
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_NEW,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_REATTACH,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_KNOWN,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_PAGED,
|
||||
CNLINK_CTR_CNPOOL_SUBSCR_ATTACH_LOST,
|
||||
CNLINK_CTR_CNPOOL_EMERG_FORWARDED,
|
||||
/* User provided configuration for struct hnbgw_cnpool. */
|
||||
struct hnbgw_cnpool_cfg {
|
||||
uint8_t nri_bitlen;
|
||||
struct osmo_nri_ranges *null_nri_ranges;
|
||||
};
|
||||
|
||||
extern const struct rate_ctr_group_desc msc_ctrg_desc;
|
||||
extern const struct rate_ctr_group_desc sgsn_ctrg_desc;
|
||||
/* Collection of CN peers to distribute UE connections across. MSCs for DOMAIN_CS, SGSNs for DOMAIN_PS. */
|
||||
struct hnbgw_cnpool {
|
||||
RANAP_CN_DomainIndicator_t domain;
|
||||
|
||||
/* CN pool string used in VTY config and logging, "iucs" or "iups". */
|
||||
const char *pool_name;
|
||||
/* CN peer string used in VTY config and logging, "msc" or "sgsn". */
|
||||
const char *peer_name;
|
||||
/* What we use as the remote MSC/SGSN point-code if the user does not configure any address. */
|
||||
uint32_t default_remote_pc;
|
||||
const char *default_addr_name;
|
||||
|
||||
struct hnbgw_cnpool_cfg vty;
|
||||
struct hnbgw_cnpool_cfg use;
|
||||
|
||||
/* List of struct hnbgw_cnlink */
|
||||
struct llist_head cnlinks;
|
||||
|
||||
unsigned int round_robin_next_nr;
|
||||
/* Emergency calls potentially select a different set of MSCs, so to not mess up the normal round-robin
|
||||
* behavior, emergency calls need a separate round-robin counter. */
|
||||
unsigned int round_robin_next_emerg_nr;
|
||||
|
||||
/* Running counters for this pool */
|
||||
struct rate_ctr_group *ctrs;
|
||||
};
|
||||
|
||||
struct hnbgw_cnpool *hnbgw_cnpool_alloc(RANAP_CN_DomainIndicator_t domain);
|
||||
|
||||
struct hnbgw_cnlink *hnbgw_cnlink_select(struct hnbgw_context_map *map);
|
||||
|
||||
void hnbgw_cnpool_start(struct hnbgw_cnpool *cnpool);
|
||||
void hnbgw_cnpool_cnlinks_start_or_restart(struct hnbgw_cnpool *cnpool);
|
||||
struct hnbgw_cnlink *cnlink_get_nr(struct hnbgw_cnpool *cnpool, int nr, bool create_if_missing);
|
||||
void hnbgw_cnpool_apply_cfg(struct hnbgw_cnpool *cnpool);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/* HNBAP, 3GPP TS 25.469 */
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
|
||||
struct hnb_context;
|
||||
|
||||
int hnbgw_hnbap_rx(struct hnb_context *hnb, struct msgb *msg);
|
||||
int hnbgw_hnbap_init(void);
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
enum hnbgw_upf_stats {
|
||||
HNBGW_UPF_STAT_ASSOCIATED,
|
||||
};
|
||||
#define HNBGW_UPF_STAT_SET(stat, val) osmo_stat_item_set(osmo_stat_item_group_get_item(g_hnbgw->pfcp.statg, (stat)), (val))
|
||||
|
||||
int hnbgw_pfcp_init(void);
|
||||
void hnbgw_pfcp_release(void);
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
/* RANAP, 3GPP TS 25.413 */
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
|
||||
struct osmo_scu_unitdata_param;
|
||||
|
||||
ranap_message *hnbgw_decode_ranap_cn_co(struct msgb *ranap_msg);
|
||||
|
||||
int hnbgw_ranap_rx_udt_ul(struct msgb *msg, uint8_t *data, size_t len);
|
||||
int hnbgw_ranap_rx_data_ul(struct hnbgw_context_map *map, struct msgb *ranap_msg);
|
||||
|
||||
int hnbgw_ranap_rx_udt_dl(struct hnbgw_cnlink *cnlink, const struct osmo_scu_unitdata_param *unitdata,
|
||||
const uint8_t *data, unsigned int len);
|
||||
int hnbgw_ranap_rx_data_dl(struct hnbgw_context_map *map, struct msgb *ranap_msg);
|
||||
int hnbgw_ranap_init(void);
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/* RUA, 3GPP TS 25.468 */
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/rua/RUA_Cause.h>
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
|
||||
struct hnb_context;
|
||||
|
||||
int hnbgw_rua_rx(struct hnb_context *hnb, struct msgb *msg);
|
||||
|
||||
@@ -12,4 +14,3 @@ int rua_tx_dt(struct hnb_context *hnb, int is_ps, uint32_t context_id,
|
||||
int rua_tx_disc(struct hnb_context *hnb, int is_ps, uint32_t context_id,
|
||||
const RUA_Cause_t *cause, const uint8_t *data, unsigned int len);
|
||||
|
||||
ranap_message *hnbgw_decode_ranap_co(struct msgb *ranap_msg);
|
||||
|
||||
66
include/osmocom/hnbgw/hnbgw_sccp.h
Normal file
66
include/osmocom/hnbgw/hnbgw_sccp.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* SCCP, ITU Q.711 - Q.714 */
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/hashtable.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/prim.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/use_count.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
struct hnbgw_cnlink;
|
||||
|
||||
/* osmo-hnbgw keeps a single hnbgw_sccp_user per osmo_sccp_instance, for the local point-code and SSN == RANAP.
|
||||
* This relates the (opaque) osmo_sccp_user to osmo-hnbgw's per-ss7 state. */
|
||||
struct hnbgw_sccp_user {
|
||||
/* entry in g_hnbgw->sccp.users */
|
||||
struct llist_head entry;
|
||||
|
||||
/* logging context */
|
||||
char *name;
|
||||
|
||||
/* Which 'cs7 instance' is this for? Below sccp_user is registered at the osmo_sccp_instance ss7->sccp. */
|
||||
struct osmo_ss7_instance *ss7;
|
||||
|
||||
/* Local address: cs7 instance's primary PC if present, else the default HNBGW PC; with SSN == RANAP. */
|
||||
struct osmo_sccp_addr local_addr;
|
||||
|
||||
/* osmo_sccp API state for above local address on above ss7 instance. */
|
||||
struct osmo_sccp_user *sccp_user;
|
||||
|
||||
/* Ref count of users of this struct, ie.referencing it in cnlink->hnbgw_sccp_user */
|
||||
struct osmo_use_count use_count;
|
||||
|
||||
/* Fast access to the hnbgw_context_map responsible for a given SCCP conn_id of the ss7->sccp instance.
|
||||
* hlist_node: hnbgw_context_map->hnbgw_sccp_user_entry. */
|
||||
DECLARE_HASHTABLE(hnbgw_context_map_by_conn_id, 6);
|
||||
};
|
||||
|
||||
#define LOG_HSU(HSU, SUBSYS, LEVEL, FMT, ARGS...) \
|
||||
LOGP(SUBSYS, LEVEL, "(%s) " FMT, (HSU) ? (HSU)->name : "null", ##ARGS)
|
||||
|
||||
#define HSU_USE_CNLINK "cnlink"
|
||||
#define hnbgw_sccp_user_get(hsu, use) \
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&(hsu)->use_count, use, 1) == 0)
|
||||
#define hnbgw_sccp_user_put(hsu, use) \
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&(hsu)->use_count, use, -1) == 0)
|
||||
|
||||
struct hnbgw_sccp_user *hnbgw_sccp_user_alloc(int ss7_inst_id);
|
||||
|
||||
int hnbgw_sccp_user_tx_unitdata_req(struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *called_addr,
|
||||
struct msgb *ranap_msg);
|
||||
int hnbgw_sccp_user_tx_connect_req(struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *called_addr,
|
||||
uint32_t scu_conn_id, struct msgb *ranap_msg);
|
||||
int hnbgw_sccp_user_tx_data_req(struct hnbgw_sccp_user *hsu, uint32_t scu_conn_id,
|
||||
struct msgb *ranap_msg);
|
||||
int hnbgw_sccp_user_tx_disconnect_req(struct hnbgw_sccp_user *hsu, uint32_t scu_conn_id);
|
||||
|
||||
static inline struct osmo_sccp_instance *hnbgw_sccp_user_get_sccp_instance(const struct hnbgw_sccp_user *hsu)
|
||||
{
|
||||
if (!hsu->ss7)
|
||||
return NULL;
|
||||
return osmo_ss7_get_sccp(hsu->ss7);
|
||||
}
|
||||
@@ -6,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);
|
||||
@@ -6,6 +6,7 @@ enum ps_rab_ass_fsm_event {
|
||||
PS_RAB_ASS_EV_LOCAL_F_TEIDS_RX,
|
||||
PS_RAB_ASS_EV_RAB_ASS_RESP,
|
||||
PS_RAB_ASS_EV_RAB_ESTABLISHED,
|
||||
PS_RAB_ASS_EV_RAB_RELEASED,
|
||||
PS_RAB_ASS_EV_RAB_FAIL,
|
||||
};
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ struct ps_rab *ps_rab_start(struct hnbgw_context_map *map, uint8_t rab_id,
|
||||
|
||||
struct ps_rab *ps_rab_get(struct hnbgw_context_map *map, uint8_t rab_id);
|
||||
bool ps_rab_is_established(const struct ps_rab *rab);
|
||||
void ps_rab_release(struct ps_rab *rab);
|
||||
void ps_rab_release(struct ps_rab *rab, struct osmo_fsm_inst *notify_fi);
|
||||
|
||||
struct ps_rab_rx_args {
|
||||
struct addr_teid f_teid;
|
||||
|
||||
34
include/osmocom/hnbgw/umts_cell_id.h
Normal file
34
include/osmocom/hnbgw/umts_cell_id.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
|
||||
struct umts_cell_id {
|
||||
struct osmo_plmn_id plmn; /*!< Mobile Country Code and Mobile Network Code (000-00 to 999-999) */
|
||||
uint16_t lac; /*!< Locaton Area Code (1-65534) */
|
||||
uint8_t rac; /*!< Routing Area Code (0-255) */
|
||||
uint16_t sac; /*!< Service Area Code */
|
||||
uint32_t cid; /*!< Cell ID */
|
||||
};
|
||||
int umts_cell_id_to_str_buf(char *buf, size_t buflen, const struct umts_cell_id *ucid);
|
||||
char *umts_cell_id_to_str_c(void *ctx, const struct umts_cell_id *ucid);
|
||||
const char *umts_cell_id_to_str(const struct umts_cell_id *ucid);
|
||||
int umts_cell_id_from_str(struct umts_cell_id *ucid, const char *instr);
|
||||
uint32_t umts_cell_id_hash(const struct umts_cell_id *ucid);
|
||||
|
||||
/*! are both given umts_cell_id euqal? */
|
||||
static inline bool umts_cell_id_equal(const struct umts_cell_id *a, const struct umts_cell_id *b)
|
||||
{
|
||||
if (osmo_plmn_cmp(&a->plmn, &b->plmn))
|
||||
return false;
|
||||
if (a->lac != b->lac)
|
||||
return false;
|
||||
if (a->rac != b->rac)
|
||||
return false;
|
||||
if (a->sac != b->sac)
|
||||
return false;
|
||||
if (a->cid != b->cid)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@@ -20,6 +20,7 @@ AM_CFLAGS = \
|
||||
$(LIBOSMORANAP_CFLAGS) \
|
||||
$(LIBOSMOHNBAP_CFLAGS) \
|
||||
$(LIBOSMOMGCPCLIENT_CFLAGS) \
|
||||
$(LIBNFTABLES_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
@@ -31,21 +32,29 @@ noinst_LTLIBRARIES = \
|
||||
$(NULL)
|
||||
|
||||
libhnbgw_la_SOURCES = \
|
||||
hnb.c \
|
||||
hnb_persistent.c \
|
||||
hnbgw.c \
|
||||
hnbgw_hnbap.c \
|
||||
hnbgw_l3.c \
|
||||
hnbgw_rua.c \
|
||||
hnbgw_ranap.c \
|
||||
hnbgw_sccp.c \
|
||||
hnbgw_vty.c \
|
||||
context_map.c \
|
||||
context_map_rua.c \
|
||||
context_map_sccp.c \
|
||||
hnbgw_cn.c \
|
||||
cnlink.c \
|
||||
cnlink_fsm.c \
|
||||
cnlink_paging.c \
|
||||
ranap_rab_ass.c \
|
||||
mgw_fsm.c \
|
||||
kpi_dtap.c \
|
||||
kpi_ranap.c \
|
||||
tdefs.c \
|
||||
umts_cell_id.c \
|
||||
nft_kpi.c \
|
||||
$(NULL)
|
||||
|
||||
libhnbgw_la_LIBADD = \
|
||||
@@ -62,6 +71,7 @@ libhnbgw_la_LIBADD = \
|
||||
$(LIBOSMOHNBAP_LIBS) \
|
||||
$(LIBSCTP_LIBS) \
|
||||
$(LIBOSMOMGCPCLIENT_LIBS) \
|
||||
$(LIBNFTABLES_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
if ENABLE_PFCP
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
|
||||
#include <osmocom/gsm/gsm23236.h>
|
||||
|
||||
@@ -34,94 +36,255 @@
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
|
||||
static struct osmo_fsm cnlink_fsm;
|
||||
static const struct rate_ctr_desc cnlink_ctr_description[] = {
|
||||
[CNLINK_CTR_RANAP_RX_UDT_RESET] = {
|
||||
"ranap:rx:udt:reset",
|
||||
"RANAP Unitdata RESET messages received"
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_RESET_ACK] = {
|
||||
"ranap:rx:udt:reset_ack",
|
||||
"RANAP Unitdata RESET ACK messages received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_PAGING] = {
|
||||
"ranap:rx:udt:paging",
|
||||
"RANAP Unitdata PAGING messages received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_UNKNOWN] = {
|
||||
"ranap:rx:udt:unknown",
|
||||
"Unknown RANAP Unitdata messages received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_UNSUPPORTED] = {
|
||||
"ranap:rx:udt:unsupported",
|
||||
"Unsupported RANAP Unitdata messages received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_OVERLOAD_IND] = {
|
||||
"ranap:rx:udt:overload_ind",
|
||||
"RANAP Unitdata Overload Indications received",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_RX_UDT_ERROR_IND] = {
|
||||
"ranap:rx:udt:error_ind",
|
||||
"RANAP Unitdata Error Indications received",
|
||||
},
|
||||
|
||||
enum cnlink_fsm_state {
|
||||
CNLINK_ST_DISC,
|
||||
CNLINK_ST_CONN,
|
||||
[CNLINK_CTR_RANAP_TX_UDT_RESET] = {
|
||||
"ranap:tx:udt:reset",
|
||||
"RANAP Unitdata RESET messages transmitted",
|
||||
},
|
||||
[CNLINK_CTR_RANAP_TX_UDT_RESET_ACK] = {
|
||||
"ranap:tx:udt:reset_ack",
|
||||
"RANAP Unitdata RESET ACK messages transmitted",
|
||||
},
|
||||
|
||||
/* SCCP Counters: */
|
||||
[CNLINK_CTR_SCCP_N_UNITDATA_REQ] = {
|
||||
"sccp:n_unit_data:req",
|
||||
"Submit SCCP N-UNITDATA.req (UL)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_UNITDATA_IND] = {
|
||||
"sccp:n_unit_data:ind",
|
||||
"Received SCCP N-UNITDATA.ind (DL)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_NOTICE_IND] = {
|
||||
"sccp:n_notice:ind",
|
||||
"Received SCCP N-NOTICE.ind"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_CONNECT_REQ] = {
|
||||
"sccp:n_connect:req",
|
||||
"Submit SCCP N-CONNECT.req (UL SCCP CR)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_CONNECT_CNF] = {
|
||||
"sccp:n_connect:cnf",
|
||||
"Received SCCP N-CONNECT.cnf (DL SCCP CC)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_DATA_REQ] = {
|
||||
"sccp:n_data:req",
|
||||
"SUBMIT SCCP N-DATA.req (UL)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_DATA_IND] = {
|
||||
"sccp:n_data:ind",
|
||||
"Received SCCP N-DATA.ind (DL)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_DISCONNECT_REQ] = {
|
||||
"sccp:n_disconnect:req",
|
||||
"Submit SCCP N-DISCONNECT.req (UL SCCP RLC)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_DISCONNECT_IND] = {
|
||||
"sccp:n_disconnect:ind",
|
||||
"Received SCCP N-DISCONNECT.ind (DL SCCP RLSD)"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_N_PCSTATE_IND] = {
|
||||
"sccp:n_pcstate:ind",
|
||||
"Received SCCP N-PCSTATE.ind"
|
||||
},
|
||||
[CNLINK_CTR_SCCP_RLSD_CN_ORIGIN] = {
|
||||
"sccp:rlsd_cn_origin",
|
||||
"Received unexpected SCCP RSLD originated unilaterally by CN"
|
||||
},
|
||||
|
||||
/* Indicators for CN pool usage */
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_NEW] = {
|
||||
"cnpool:subscr:new",
|
||||
"Complete Layer 3 requests assigned to this CN link by round-robin (no NRI was assigned yet).",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_REATTACH] = {
|
||||
"cnpool:subscr:reattach",
|
||||
"Complete Layer 3 requests assigned to this CN link by round-robin because the subscriber indicates a"
|
||||
" NULL-NRI (previously assigned by another CN link).",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_KNOWN] = {
|
||||
"cnpool:subscr:known",
|
||||
"Complete Layer 3 requests directed to this CN link because the subscriber indicates an NRI of this CN link.",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_PAGED] = {
|
||||
"cnpool:subscr:paged",
|
||||
"Paging Response directed to this CN link because the subscriber was recently paged by this CN link.",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_SUBSCR_ATTACH_LOST] = {
|
||||
"cnpool:subscr:attach_lost",
|
||||
"A subscriber indicates an NRI value matching this CN link, but the CN link is not connected:"
|
||||
" a re-attach to another CN link (if available) was forced, with possible service failure.",
|
||||
},
|
||||
[CNLINK_CTR_CNPOOL_EMERG_FORWARDED] = {
|
||||
"cnpool:emerg:forwarded",
|
||||
"Emergency call requests forwarded to this CN link.",
|
||||
},
|
||||
};
|
||||
|
||||
enum cnlink_fsm_event {
|
||||
CNLINK_EV_RX_RESET,
|
||||
CNLINK_EV_RX_RESET_ACK,
|
||||
static const struct rate_ctr_group_desc msc_ctrg_desc = {
|
||||
"msc",
|
||||
"MSC",
|
||||
OSMO_STATS_CLASS_GLOBAL,
|
||||
ARRAY_SIZE(cnlink_ctr_description),
|
||||
cnlink_ctr_description,
|
||||
};
|
||||
|
||||
static const struct value_string cnlink_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(CNLINK_EV_RX_RESET),
|
||||
OSMO_VALUE_STRING(CNLINK_EV_RX_RESET_ACK),
|
||||
{}
|
||||
static const struct rate_ctr_group_desc sgsn_ctrg_desc = {
|
||||
"sgsn",
|
||||
"SGSN",
|
||||
OSMO_STATS_CLASS_GLOBAL,
|
||||
ARRAY_SIZE(cnlink_ctr_description),
|
||||
cnlink_ctr_description,
|
||||
};
|
||||
|
||||
static const struct osmo_tdef_state_timeout cnlink_timeouts[32] = {
|
||||
[CNLINK_ST_DISC] = { .T = 4 },
|
||||
static const struct osmo_stat_item_desc cnlink_stat_desc[] = {
|
||||
[CNLINK_STAT_CONNECTED] = { "connected", "Connected (1) or disconnected (0)", NULL, 60, 0 },
|
||||
};
|
||||
|
||||
#define cnlink_fsm_state_chg(FI, STATE) \
|
||||
osmo_tdef_fsm_inst_state_chg(FI, STATE, \
|
||||
cnlink_timeouts, \
|
||||
hnbgw_T_defs, \
|
||||
-1)
|
||||
const struct osmo_stat_item_group_desc msc_statg_desc = {
|
||||
.group_name_prefix = "msc",
|
||||
.group_description = "MSC",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_items = ARRAY_SIZE(cnlink_stat_desc),
|
||||
.item_desc = cnlink_stat_desc,
|
||||
};
|
||||
|
||||
struct hnbgw_cnlink *cnlink_alloc(struct hnbgw_cnpool *cnpool, int nr)
|
||||
const struct osmo_stat_item_group_desc sgsn_statg_desc = {
|
||||
.group_name_prefix = "sgsn",
|
||||
.group_description = "SGSN",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_items = ARRAY_SIZE(cnlink_stat_desc),
|
||||
.item_desc = cnlink_stat_desc,
|
||||
};
|
||||
|
||||
struct hnbgw_cnlink *hnbgw_cnlink_alloc(struct hnbgw_cnpool *cnpool, int nr)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
const struct rate_ctr_group_desc *ctrg_desc;
|
||||
const struct osmo_stat_item_group_desc *statg_desc;
|
||||
|
||||
char *name = talloc_asprintf(OTC_SELECT, "%s-%d", cnpool->peer_name, nr);
|
||||
OSMO_ASSERT(cnpool);
|
||||
|
||||
fi = osmo_fsm_inst_alloc(&cnlink_fsm, g_hnbgw, NULL, LOGL_DEBUG, name);
|
||||
OSMO_ASSERT(fi);
|
||||
cnlink = talloc_zero(g_hnbgw, struct hnbgw_cnlink);
|
||||
fi->priv = cnlink;
|
||||
switch (cnpool->domain) {
|
||||
case DOMAIN_CS:
|
||||
ctrg_desc = &msc_ctrg_desc;
|
||||
statg_desc = &msc_statg_desc;
|
||||
break;
|
||||
case DOMAIN_PS:
|
||||
ctrg_desc = &sgsn_ctrg_desc;
|
||||
statg_desc = &sgsn_statg_desc;
|
||||
break;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
|
||||
cnlink = talloc_zero(cnpool, struct hnbgw_cnlink);
|
||||
OSMO_ASSERT(cnlink);
|
||||
*cnlink = (struct hnbgw_cnlink){
|
||||
.name = name,
|
||||
.pool = cnpool,
|
||||
.fi = fi,
|
||||
.nr = nr,
|
||||
.vty = {
|
||||
/* VTY config defaults for the new cnlink */
|
||||
.nri_ranges = osmo_nri_ranges_alloc(cnlink),
|
||||
},
|
||||
.allow_attach = true,
|
||||
.ctrs = rate_ctr_group_alloc(g_hnbgw, cnpool->cnlink_ctrg_desc, nr),
|
||||
.ctrs = rate_ctr_group_alloc(cnlink, ctrg_desc, nr),
|
||||
.statg = osmo_stat_item_group_alloc(cnlink, statg_desc, nr),
|
||||
};
|
||||
talloc_steal(cnlink, name);
|
||||
cnlink->name = talloc_asprintf(cnlink, "%s-%d", cnpool->peer_name, nr);
|
||||
INIT_LLIST_HEAD(&cnlink->map_list);
|
||||
INIT_LLIST_HEAD(&cnlink->paging);
|
||||
|
||||
cnlink->fi = osmo_fsm_inst_alloc(&cnlink_fsm, cnlink, cnlink, LOGL_DEBUG, cnlink->name);
|
||||
OSMO_ASSERT(cnlink->fi);
|
||||
|
||||
llist_add_tail(&cnlink->entry, &cnpool->cnlinks);
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "allocated\n");
|
||||
|
||||
/* Immediately (1ms) kick off reset sending mechanism */
|
||||
osmo_fsm_inst_state_chg_ms(fi, CNLINK_ST_DISC, 1, 0);
|
||||
cnlink_resend_reset(cnlink);
|
||||
return cnlink;
|
||||
}
|
||||
|
||||
void cnlink_term_and_free(struct hnbgw_cnlink *cnlink)
|
||||
int hnbgw_cnlink_set_name(struct hnbgw_cnlink *cnlink, const char *name)
|
||||
{
|
||||
talloc_free(cnlink->name);
|
||||
cnlink->name = talloc_strdup(cnlink, name);
|
||||
osmo_fsm_inst_update_id_f_sanitize(cnlink->fi, '-', cnlink->name);
|
||||
/* Update rate_ctr/stats to report by name instead of index: */
|
||||
rate_ctr_group_set_name(cnlink->ctrs, cnlink->name);
|
||||
osmo_stat_item_group_set_name(cnlink->statg, cnlink->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hnbgw_cnlink_drop_sccp(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct hnbgw_context_map *map, *map2;
|
||||
struct hnbgw_sccp_user *hsu;
|
||||
|
||||
llist_for_each_entry_safe(map, map2, &cnlink->map_list, hnbgw_cnlink_entry) {
|
||||
map_sccp_dispatch(map, MAP_SCCP_EV_USER_ABORT, NULL);
|
||||
}
|
||||
|
||||
OSMO_ASSERT(cnlink->hnbgw_sccp_user);
|
||||
hsu = cnlink->hnbgw_sccp_user;
|
||||
cnlink->hnbgw_sccp_user = NULL;
|
||||
hnbgw_sccp_user_put(hsu, HSU_USE_CNLINK);
|
||||
}
|
||||
|
||||
void hnbgw_cnlink_term_and_free(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
if (!cnlink)
|
||||
return;
|
||||
|
||||
if (cnlink->hnbgw_sccp_user)
|
||||
hnbgw_cnlink_drop_sccp(cnlink);
|
||||
|
||||
osmo_fsm_inst_term(cnlink->fi, OSMO_FSM_TERM_REQUEST, NULL);
|
||||
cnlink->fi = NULL;
|
||||
osmo_stat_item_group_free(cnlink->statg);
|
||||
rate_ctr_group_free(cnlink->ctrs);
|
||||
llist_del(&cnlink->entry);
|
||||
talloc_free(cnlink);
|
||||
}
|
||||
|
||||
static void link_up(struct hnbgw_cnlink *cnlink)
|
||||
static int hnbgw_cnlink_tx_sccp_unitdata_req(struct hnbgw_cnlink *cnlink, struct msgb *msg)
|
||||
{
|
||||
LOGPFSML(cnlink->fi, LOGL_NOTICE, "link up\n");
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_SCCP_N_UNITDATA_REQ);
|
||||
return hnbgw_sccp_user_tx_unitdata_req(cnlink->hnbgw_sccp_user,
|
||||
&cnlink->remote_addr,
|
||||
msg);
|
||||
}
|
||||
|
||||
static void link_lost(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct hnbgw_context_map *map, *map2;
|
||||
|
||||
LOGPFSML(cnlink->fi, LOGL_NOTICE, "link lost\n");
|
||||
|
||||
llist_for_each_entry_safe(map, map2, &cnlink->map_list, hnbgw_cnlink_entry)
|
||||
context_map_cnlink_lost(map);
|
||||
}
|
||||
|
||||
static void tx_reset(struct hnbgw_cnlink *cnlink)
|
||||
int hnbgw_cnlink_tx_ranap_reset(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct msgb *msg;
|
||||
RANAP_Cause_t cause = {
|
||||
@@ -133,7 +296,7 @@ static void tx_reset(struct hnbgw_cnlink *cnlink)
|
||||
uint8_t plmn_buf[3];
|
||||
|
||||
if (!cnlink)
|
||||
return;
|
||||
return -1;
|
||||
|
||||
/* We need to have chosen an SCCP instance, and the remote SCCP address needs to be set.
|
||||
* Only check the remote_addr, allowing use.remote_addr_name to be NULL: if the user has not set an explicit
|
||||
@@ -141,12 +304,12 @@ static void tx_reset(struct hnbgw_cnlink *cnlink)
|
||||
if (!cnlink->hnbgw_sccp_user
|
||||
|| !osmo_sccp_check_addr(&cnlink->remote_addr, OSMO_SCCP_ADDR_T_PC | OSMO_SCCP_ADDR_T_SSN)) {
|
||||
LOG_CNLINK(cnlink, DRANAP, LOGL_DEBUG, "not yet configured, not sending RANAP RESET\n");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_CNLINK(cnlink, DRANAP, LOGL_DEBUG, "Tx RANAP RESET to %s %s\n",
|
||||
cnlink_is_cs(cnlink) ? "IuCS" : "IuPS",
|
||||
cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr));
|
||||
hnbgw_cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr));
|
||||
|
||||
if (g_hnbgw->config.plmn.mcc) {
|
||||
osmo_plmn_to_bcd(plmn_buf, &g_hnbgw->config.plmn);
|
||||
@@ -175,29 +338,26 @@ static void tx_reset(struct hnbgw_cnlink *cnlink)
|
||||
|
||||
msg = ranap_new_msg_reset2(cnlink->pool->domain, &cause, use_grnc_id);
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_TX_UDT_RESET);
|
||||
osmo_sccp_tx_unitdata_msg(cnlink->hnbgw_sccp_user->sccp_user,
|
||||
&cnlink->hnbgw_sccp_user->local_addr,
|
||||
&cnlink->remote_addr,
|
||||
msg);
|
||||
return hnbgw_cnlink_tx_sccp_unitdata_req(cnlink, msg);
|
||||
}
|
||||
|
||||
static void tx_reset_ack(struct hnbgw_cnlink *cnlink)
|
||||
int hnbgw_cnlink_tx_ranap_reset_ack(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmo_sccp_instance *sccp = cnlink_sccp(cnlink);
|
||||
struct osmo_sccp_instance *sccp = hnbgw_cnlink_sccp(cnlink);
|
||||
RANAP_GlobalRNC_ID_t grnc_id;
|
||||
RANAP_GlobalRNC_ID_t *use_grnc_id = NULL;
|
||||
uint8_t plmn_buf[3];
|
||||
|
||||
if (!sccp) {
|
||||
LOG_CNLINK(cnlink, DRANAP, LOGL_ERROR, "cannot send RANAP RESET ACK: no CN link\n");
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_CNLINK(cnlink, DRANAP, LOGL_NOTICE, "Tx RANAP RESET ACK %s %s --> %s\n",
|
||||
cnlink_is_cs(cnlink) ? "IuCS" : "IuPS",
|
||||
cnlink_sccp_addr_to_str(cnlink, &cnlink->hnbgw_sccp_user->local_addr),
|
||||
cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr));
|
||||
hnbgw_cnlink_sccp_addr_to_str(cnlink, &cnlink->hnbgw_sccp_user->local_addr),
|
||||
hnbgw_cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr));
|
||||
|
||||
if (g_hnbgw->config.plmn.mcc) {
|
||||
osmo_plmn_to_bcd(plmn_buf, &g_hnbgw->config.plmn);
|
||||
@@ -226,149 +386,169 @@ static void tx_reset_ack(struct hnbgw_cnlink *cnlink)
|
||||
|
||||
msg = ranap_new_msg_reset_ack(cnlink->pool->domain, use_grnc_id);
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_TX_UDT_RESET_ACK);
|
||||
osmo_sccp_tx_unitdata_msg(cnlink->hnbgw_sccp_user->sccp_user,
|
||||
&cnlink->hnbgw_sccp_user->local_addr,
|
||||
&cnlink->remote_addr,
|
||||
msg);
|
||||
return hnbgw_cnlink_tx_sccp_unitdata_req(cnlink, msg);
|
||||
}
|
||||
|
||||
static void cnlink_disc_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
/* Return address found in sccp address-book, and fill in missing fields in the
|
||||
* entry with default values. */
|
||||
static struct osmo_ss7_instance *sccp_addr_by_name_filled(struct osmo_sccp_addr *dest, const char *addr_name, uint32_t default_pc)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
if (prev_state == CNLINK_ST_CONN)
|
||||
link_lost(cnlink);
|
||||
struct osmo_ss7_instance *s7i;
|
||||
s7i = osmo_sccp_addr_by_name(dest, addr_name);
|
||||
if (!s7i)
|
||||
return NULL;
|
||||
|
||||
/* Address exists in address-book but may not be filled entirely: */
|
||||
if (!dest->presence)
|
||||
osmo_sccp_make_addr_pc_ssn(dest, default_pc, OSMO_SCCP_SSN_RANAP);
|
||||
else if (!(dest->presence & OSMO_SCCP_ADDR_T_SSN))
|
||||
osmo_sccp_addr_set_ssn(dest, OSMO_SCCP_SSN_RANAP);
|
||||
|
||||
return s7i;
|
||||
}
|
||||
|
||||
static void cnlink_disc_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
char *hnbgw_cnlink_sccp_addr_to_str(struct hnbgw_cnlink *cnlink, const struct osmo_sccp_addr *addr)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
switch (event) {
|
||||
struct osmo_sccp_instance *sccp = hnbgw_cnlink_sccp(cnlink);
|
||||
if (!sccp)
|
||||
return osmo_sccp_addr_dump(addr);
|
||||
return osmo_sccp_inst_addr_to_str_c(OTC_SELECT, sccp, addr);
|
||||
}
|
||||
|
||||
case CNLINK_EV_RX_RESET:
|
||||
tx_reset_ack(cnlink);
|
||||
cnlink_fsm_state_chg(fi, CNLINK_ST_CONN);
|
||||
break;
|
||||
static void hnbgw_cnlink_cfg_copy(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct osmo_nri_range *r;
|
||||
|
||||
case CNLINK_EV_RX_RESET_ACK:
|
||||
cnlink_fsm_state_chg(fi, CNLINK_ST_CONN);
|
||||
break;
|
||||
osmo_talloc_replace_string(cnlink, &cnlink->use.remote_addr_name, cnlink->vty.remote_addr_name);
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
osmo_nri_ranges_free(cnlink->use.nri_ranges);
|
||||
cnlink->use.nri_ranges = osmo_nri_ranges_alloc(cnlink);
|
||||
llist_for_each_entry(r, &cnlink->vty.nri_ranges->entries, entry)
|
||||
osmo_nri_ranges_add(cnlink->use.nri_ranges, r);
|
||||
}
|
||||
|
||||
static bool hnbgw_cnlink_sccp_cfg_changed(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
if (cnlink->vty.remote_addr_name && cnlink->use.remote_addr_name) {
|
||||
struct osmo_ss7_instance *s7i;
|
||||
struct osmo_sccp_addr remote_addr = {};
|
||||
|
||||
/* Instead of comparing whether the address book entry names are different, actually resolve the
|
||||
* resulting SCCP address, and only restart the cnlink if the resulting address changed. */
|
||||
s7i = sccp_addr_by_name_filled(&remote_addr, cnlink->vty.remote_addr_name,
|
||||
cnlink->pool->default_remote_pc);
|
||||
if (!s7i)
|
||||
return true;
|
||||
if (osmo_sccp_addr_cmp(&remote_addr, &cnlink->remote_addr,
|
||||
OSMO_SCCP_ADDR_T_PC | OSMO_SCCP_ADDR_T_SSN))
|
||||
changed = true;
|
||||
} else if (cnlink->vty.remote_addr_name != cnlink->use.remote_addr_name) {
|
||||
/* One of them is NULL, the other is not. */
|
||||
changed = true;
|
||||
}
|
||||
|
||||
/* if more cnlink configuration is added in the future, it needs to be compared here. */
|
||||
return changed;
|
||||
}
|
||||
|
||||
static void cnlink_conn_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
static void hnbgw_cnlink_log_self(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
if (prev_state != CNLINK_ST_CONN)
|
||||
link_up(cnlink);
|
||||
struct osmo_ss7_instance *ss7 = cnlink->hnbgw_sccp_user->ss7;
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "using: cs7-%u %s <-> %s %s %s\n",
|
||||
osmo_ss7_instance_get_id(ss7),
|
||||
/* printing the entire SCCP address is quite long, rather just print the point-code */
|
||||
osmo_ss7_pointcode_print(ss7, cnlink->hnbgw_sccp_user->local_addr.pc),
|
||||
osmo_ss7_pointcode_print2(ss7, cnlink->remote_addr.pc),
|
||||
cnlink->name, cnlink->use.remote_addr_name ? : "(default remote point-code)");
|
||||
}
|
||||
|
||||
static void cnlink_conn_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
/* If not present yet, set up all of osmo_ss7_instance, osmo_sccp_instance and hnbgw_sccp_user for the given cnlink.
|
||||
* The cs7 instance nr to use is determined by cnlink->remote_addr_name, or cs7 instance 0 if that is not present.
|
||||
* Set cnlink->hnbgw_sccp_user to the new SCCP instance. Return 0 on success, negative on error. */
|
||||
int hnbgw_cnlink_start_or_restart(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
struct osmo_ss7_instance *s7i = NULL;
|
||||
struct hnbgw_sccp_user *hsu;
|
||||
uint32_t ss7_id;
|
||||
int rc;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case CNLINK_EV_RX_RESET:
|
||||
/* We were connected, but the remote side has restarted. */
|
||||
link_lost(cnlink);
|
||||
tx_reset_ack(cnlink);
|
||||
link_up(cnlink);
|
||||
break;
|
||||
|
||||
case CNLINK_EV_RX_RESET_ACK:
|
||||
LOGPFSML(fi, LOGL_INFO, "Link is already up, ignoring RESET ACK\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
/* If a hnbgw_sccp_user has already been set up, use that. */
|
||||
if (cnlink->hnbgw_sccp_user) {
|
||||
if (!hnbgw_cnlink_sccp_cfg_changed(cnlink)) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "SCCP instance already set up, using %s\n",
|
||||
cnlink->hnbgw_sccp_user->name);
|
||||
return 0;
|
||||
}
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "config changed, restarting SCCP\n");
|
||||
hnbgw_cnlink_drop_sccp(cnlink);
|
||||
} else {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "no SCCP instance selected yet\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int cnlink_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
/* Copy the current configuration: cnlink->use = cnlink->vty */
|
||||
hnbgw_cnlink_cfg_copy(cnlink);
|
||||
|
||||
tx_reset(cnlink);
|
||||
if (!cnlink->use.remote_addr_name) {
|
||||
/* No remote address configured in VTY, set a default one and
|
||||
* make sure it becomes registered in the sccp address-book: */
|
||||
cnlink->use.remote_addr_name = talloc_strdup(cnlink, cnlink->pool->default_addr_name);
|
||||
s7i = osmo_sccp_addr_by_name(&cnlink->remote_addr, cnlink->use.remote_addr_name);
|
||||
if (!s7i) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "To auto-configure cnlink, creating cs7 instance 0 implicitly\n");
|
||||
s7i = osmo_ss7_instance_find_or_create(g_hnbgw, 0);
|
||||
OSMO_ASSERT(s7i);
|
||||
osmo_sccp_make_addr_pc_ssn(&cnlink->remote_addr,
|
||||
cnlink->pool->default_remote_pc,
|
||||
OSMO_SCCP_SSN_RANAP);
|
||||
rc = osmo_sccp_addr_create(s7i, cnlink->use.remote_addr_name, &cnlink->remote_addr);
|
||||
if (rc < 0) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Failed adding address '%s' to sccp address-book!\n",
|
||||
cnlink->use.remote_addr_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
/* Update VTY config to show & point to the address dynamically added to address-book: */
|
||||
cnlink->vty.remote_addr_name = talloc_strdup(cnlink, cnlink->use.remote_addr_name);
|
||||
} else {
|
||||
s7i = sccp_addr_by_name_filled(&cnlink->remote_addr, cnlink->use.remote_addr_name, cnlink->pool->default_remote_pc);
|
||||
if (!s7i) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "cannot initialize SCCP: there is no SCCP address named '%s'\n",
|
||||
cnlink->use.remote_addr_name);
|
||||
return -ENOENT;
|
||||
}
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "remote-addr is '%s', using cs7 instance %u\n",
|
||||
cnlink->use.remote_addr_name, osmo_ss7_instance_get_id(s7i));
|
||||
/* Address exists in address-book but may not be filled entirely: */
|
||||
rc = osmo_sccp_addr_update(s7i, cnlink->use.remote_addr_name, &cnlink->remote_addr);
|
||||
if (rc < 0) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Failed updating address '%s' in sccp address-book!\n",
|
||||
cnlink->use.remote_addr_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* (re-)enter disconnect state to resend RESET after timeout. */
|
||||
cnlink_fsm_state_chg(fi, CNLINK_ST_DISC);
|
||||
ss7_id = osmo_ss7_instance_get_id(s7i);
|
||||
|
||||
/* Return 0 to not terminate the fsm */
|
||||
/* Has another cnlink already set up an SCCP instance for this s7i? */
|
||||
llist_for_each_entry(hsu, &g_hnbgw->sccp.users, entry) {
|
||||
if (hsu->ss7 != s7i)
|
||||
continue;
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "using existing SCCP instance %s on cs7 instance %u\n",
|
||||
hsu->name, ss7_id);
|
||||
cnlink->hnbgw_sccp_user = hsu;
|
||||
hnbgw_sccp_user_get(cnlink->hnbgw_sccp_user, HSU_USE_CNLINK);
|
||||
hnbgw_cnlink_log_self(cnlink);
|
||||
return 0;
|
||||
}
|
||||
/* else cnlink->hnbgw_sccp_user stays NULL and is set up below. */
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG, "cs7 instance %u has no configured SCCP instance yet\n", ss7_id);
|
||||
|
||||
/* No SCCP instance yet for this ss7. Create it. If no address name is given that resolves to a
|
||||
* particular cs7 instance above, use 'cs7 instance 0'. */
|
||||
cnlink->hnbgw_sccp_user = hnbgw_sccp_user_alloc(ss7_id);
|
||||
hnbgw_sccp_user_get(cnlink->hnbgw_sccp_user, HSU_USE_CNLINK);
|
||||
hnbgw_cnlink_log_self(cnlink);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
static struct osmo_fsm_state cnlink_fsm_states[] = {
|
||||
[CNLINK_ST_DISC] = {
|
||||
.name = "DISCONNECTED",
|
||||
.in_event_mask = 0
|
||||
| S(CNLINK_EV_RX_RESET)
|
||||
| S(CNLINK_EV_RX_RESET_ACK)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(CNLINK_ST_DISC)
|
||||
| S(CNLINK_ST_CONN)
|
||||
,
|
||||
.onenter = cnlink_disc_onenter,
|
||||
.action = cnlink_disc_action,
|
||||
},
|
||||
[CNLINK_ST_CONN] = {
|
||||
.name = "CONNECTED",
|
||||
.in_event_mask = 0
|
||||
| S(CNLINK_EV_RX_RESET)
|
||||
| S(CNLINK_EV_RX_RESET_ACK)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(CNLINK_ST_DISC)
|
||||
| S(CNLINK_ST_CONN)
|
||||
,
|
||||
.onenter = cnlink_conn_onenter,
|
||||
.action = cnlink_conn_action,
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm cnlink_fsm = {
|
||||
.name = "cnlink",
|
||||
.states = cnlink_fsm_states,
|
||||
.num_states = ARRAY_SIZE(cnlink_fsm_states),
|
||||
.log_subsys = DRANAP,
|
||||
.timer_cb = cnlink_fsm_timer_cb,
|
||||
.event_names = cnlink_fsm_event_names,
|
||||
};
|
||||
|
||||
bool cnlink_is_conn_ready(const struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
return cnlink->fi->state == CNLINK_ST_CONN;
|
||||
}
|
||||
|
||||
void cnlink_resend_reset(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
/* Immediately (1ms) kick off reset sending mechanism */
|
||||
osmo_fsm_inst_state_chg_ms(cnlink->fi, CNLINK_ST_DISC, 1, 0);
|
||||
}
|
||||
|
||||
void cnlink_set_disconnected(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
/* Go to disconnected state, with the normal RESET timeout to re-send RESET. */
|
||||
cnlink_fsm_state_chg(cnlink->fi, CNLINK_ST_DISC);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void cnlink_fsm_init(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&cnlink_fsm) == 0);
|
||||
}
|
||||
|
||||
void cnlink_rx_reset_cmd(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
osmo_fsm_inst_dispatch(cnlink->fi, CNLINK_EV_RX_RESET, NULL);
|
||||
}
|
||||
|
||||
void cnlink_rx_reset_ack(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
osmo_fsm_inst_dispatch(cnlink->fi, CNLINK_EV_RX_RESET_ACK, NULL);
|
||||
}
|
||||
|
||||
|
||||
220
src/osmo-hnbgw/cnlink_fsm.c
Normal file
220
src/osmo-hnbgw/cnlink_fsm.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Hofmeyr
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
|
||||
#include <osmocom/gsm/gsm23236.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#include <asn1c/asn1helpers.h>
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/ranap/ranap_msg_factory.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
|
||||
enum cnlink_fsm_state {
|
||||
CNLINK_ST_DISC,
|
||||
CNLINK_ST_CONN,
|
||||
};
|
||||
|
||||
enum cnlink_fsm_event {
|
||||
CNLINK_EV_RX_RESET,
|
||||
CNLINK_EV_RX_RESET_ACK,
|
||||
};
|
||||
|
||||
static const struct value_string cnlink_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(CNLINK_EV_RX_RESET),
|
||||
OSMO_VALUE_STRING(CNLINK_EV_RX_RESET_ACK),
|
||||
{}
|
||||
};
|
||||
|
||||
static const struct osmo_tdef_state_timeout cnlink_timeouts[32] = {
|
||||
[CNLINK_ST_DISC] = { .T = 4 },
|
||||
};
|
||||
|
||||
#define cnlink_fsm_state_chg(FI, STATE) \
|
||||
osmo_tdef_fsm_inst_state_chg(FI, STATE, \
|
||||
cnlink_timeouts, \
|
||||
hnbgw_T_defs, \
|
||||
-1)
|
||||
|
||||
static void link_up(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
LOGPFSML(cnlink->fi, LOGL_NOTICE, "link up\n");
|
||||
CNLINK_STAT_SET(cnlink, CNLINK_STAT_CONNECTED, 1);
|
||||
}
|
||||
|
||||
static void link_lost(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
struct hnbgw_context_map *map, *map2;
|
||||
|
||||
LOGPFSML(cnlink->fi, LOGL_NOTICE, "link lost\n");
|
||||
CNLINK_STAT_SET(cnlink, CNLINK_STAT_CONNECTED, 0);
|
||||
|
||||
llist_for_each_entry_safe(map, map2, &cnlink->map_list, hnbgw_cnlink_entry)
|
||||
context_map_cnlink_lost(map);
|
||||
}
|
||||
|
||||
|
||||
static void cnlink_disc_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
if (prev_state == CNLINK_ST_CONN)
|
||||
link_lost(cnlink);
|
||||
}
|
||||
|
||||
static void cnlink_disc_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
switch (event) {
|
||||
|
||||
case CNLINK_EV_RX_RESET:
|
||||
hnbgw_cnlink_tx_ranap_reset_ack(cnlink);
|
||||
cnlink_fsm_state_chg(fi, CNLINK_ST_CONN);
|
||||
break;
|
||||
|
||||
case CNLINK_EV_RX_RESET_ACK:
|
||||
cnlink_fsm_state_chg(fi, CNLINK_ST_CONN);
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void cnlink_conn_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
if (prev_state != CNLINK_ST_CONN)
|
||||
link_up(cnlink);
|
||||
}
|
||||
|
||||
static void cnlink_conn_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case CNLINK_EV_RX_RESET:
|
||||
/* We were connected, but the remote side has restarted. */
|
||||
link_lost(cnlink);
|
||||
hnbgw_cnlink_tx_ranap_reset_ack(cnlink);
|
||||
link_up(cnlink);
|
||||
break;
|
||||
|
||||
case CNLINK_EV_RX_RESET_ACK:
|
||||
LOGPFSML(fi, LOGL_INFO, "Link is already up, ignoring RESET ACK\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int cnlink_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = (struct hnbgw_cnlink *)fi->priv;
|
||||
|
||||
hnbgw_cnlink_tx_ranap_reset(cnlink);
|
||||
|
||||
/* (re-)enter disconnect state to resend RESET after timeout. */
|
||||
cnlink_fsm_state_chg(fi, CNLINK_ST_DISC);
|
||||
|
||||
/* Return 0 to not terminate the fsm */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
static struct osmo_fsm_state cnlink_fsm_states[] = {
|
||||
[CNLINK_ST_DISC] = {
|
||||
.name = "DISCONNECTED",
|
||||
.in_event_mask = 0
|
||||
| S(CNLINK_EV_RX_RESET)
|
||||
| S(CNLINK_EV_RX_RESET_ACK)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(CNLINK_ST_DISC)
|
||||
| S(CNLINK_ST_CONN)
|
||||
,
|
||||
.onenter = cnlink_disc_onenter,
|
||||
.action = cnlink_disc_action,
|
||||
},
|
||||
[CNLINK_ST_CONN] = {
|
||||
.name = "CONNECTED",
|
||||
.in_event_mask = 0
|
||||
| S(CNLINK_EV_RX_RESET)
|
||||
| S(CNLINK_EV_RX_RESET_ACK)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(CNLINK_ST_DISC)
|
||||
| S(CNLINK_ST_CONN)
|
||||
,
|
||||
.onenter = cnlink_conn_onenter,
|
||||
.action = cnlink_conn_action,
|
||||
},
|
||||
};
|
||||
|
||||
struct osmo_fsm cnlink_fsm = {
|
||||
.name = "cnlink",
|
||||
.states = cnlink_fsm_states,
|
||||
.num_states = ARRAY_SIZE(cnlink_fsm_states),
|
||||
.log_subsys = DRANAP,
|
||||
.timer_cb = cnlink_fsm_timer_cb,
|
||||
.event_names = cnlink_fsm_event_names,
|
||||
};
|
||||
|
||||
bool cnlink_is_conn_ready(const struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
return cnlink->fi->state == CNLINK_ST_CONN;
|
||||
}
|
||||
|
||||
void cnlink_resend_reset(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
/* Immediately (1ms) kick off reset sending mechanism */
|
||||
osmo_fsm_inst_state_chg_ms(cnlink->fi, CNLINK_ST_DISC, 1, 0);
|
||||
}
|
||||
|
||||
void cnlink_set_disconnected(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
/* Go to disconnected state, with the normal RESET timeout to re-send RESET. */
|
||||
cnlink_fsm_state_chg(cnlink->fi, CNLINK_ST_DISC);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void cnlink_fsm_init(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&cnlink_fsm) == 0);
|
||||
}
|
||||
|
||||
void cnlink_rx_reset_cmd(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
osmo_fsm_inst_dispatch(cnlink->fi, CNLINK_EV_RX_RESET, NULL);
|
||||
}
|
||||
|
||||
void cnlink_rx_reset_ack(struct hnbgw_cnlink *cnlink)
|
||||
{
|
||||
osmo_fsm_inst_dispatch(cnlink->fi, CNLINK_EV_RX_RESET_ACK, NULL);
|
||||
}
|
||||
|
||||
230
src/osmo-hnbgw/cnlink_paging.c
Normal file
230
src/osmo-hnbgw/cnlink_paging.c
Normal file
@@ -0,0 +1,230 @@
|
||||
/* RANAP Paging of HNB-GW */
|
||||
/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2025 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <asn1c/asn1helpers.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/ranap/iu_helpers.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
|
||||
/***************
|
||||
* This module manages the list of "struct cnlink_paging" items in (struct
|
||||
* hnbgw_cnlink *)->paging.
|
||||
* Every time a new RANAP Paging Cmd arrives from some cnlink,
|
||||
* cnlink_paging_add_ranap() is called to potentially store the paging command
|
||||
* for a while.
|
||||
* When a paging response is received from HNB, cnlink_find_by_paging_mi() is
|
||||
* called to obtain the cnlink it should be routed back to.
|
||||
*/
|
||||
|
||||
struct cnlink_paging {
|
||||
struct llist_head entry;
|
||||
|
||||
struct osmo_mobile_identity mi;
|
||||
struct osmo_mobile_identity mi2;
|
||||
time_t timestamp;
|
||||
};
|
||||
|
||||
static int cnlink_paging_destructor(struct cnlink_paging *p)
|
||||
{
|
||||
llist_del(&p->entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Return current timestamp in *timestamp, and the oldest still valid timestamp according to T3113 timeout. */
|
||||
static const char *cnlink_paging_gettime(time_t *timestamp_p, time_t *timeout_p)
|
||||
{
|
||||
struct timespec now;
|
||||
time_t timestamp;
|
||||
|
||||
/* get timestamp */
|
||||
if (osmo_clock_gettime(CLOCK_MONOTONIC, &now) != 0)
|
||||
return "cannot get timestamp";
|
||||
timestamp = now.tv_sec;
|
||||
|
||||
if (timestamp_p)
|
||||
*timestamp_p = timestamp;
|
||||
if (timeout_p)
|
||||
*timeout_p = timestamp - osmo_tdef_get(hnbgw_T_defs, 3113, OSMO_TDEF_S, 15);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *cnlink_paging_add(struct hnbgw_cnlink *cnlink, const struct osmo_mobile_identity *mi,
|
||||
const struct osmo_mobile_identity *mi2)
|
||||
{
|
||||
struct cnlink_paging *p, *p2;
|
||||
time_t timestamp;
|
||||
time_t timeout;
|
||||
const char *errmsg;
|
||||
|
||||
errmsg = cnlink_paging_gettime(×tamp, &timeout);
|
||||
if (errmsg)
|
||||
return errmsg;
|
||||
|
||||
/* Prune all paging records that are older than the configured timeout. */
|
||||
llist_for_each_entry_safe(p, p2, &cnlink->paging, entry) {
|
||||
if (p->timestamp >= timeout)
|
||||
continue;
|
||||
talloc_free(p);
|
||||
}
|
||||
|
||||
/* Add new entry */
|
||||
p = talloc_zero(cnlink, struct cnlink_paging);
|
||||
*p = (struct cnlink_paging){
|
||||
.timestamp = timestamp,
|
||||
.mi = *mi,
|
||||
.mi2 = *mi2,
|
||||
};
|
||||
llist_add_tail(&p->entry, &cnlink->paging);
|
||||
talloc_set_destructor(p, cnlink_paging_destructor);
|
||||
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_INFO, "Rx Paging from CN for %s %s\n",
|
||||
osmo_mobile_identity_to_str_c(OTC_SELECT, mi),
|
||||
osmo_mobile_identity_to_str_c(OTC_SELECT, mi2));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *omi_from_ranap_ue_id(struct osmo_mobile_identity *mi, const RANAP_PermanentNAS_UE_ID_t *ranap_mi)
|
||||
{
|
||||
if (!ranap_mi)
|
||||
return "null UE ID";
|
||||
|
||||
if (ranap_mi->present != RANAP_PermanentNAS_UE_ID_PR_iMSI)
|
||||
return talloc_asprintf(OTC_SELECT, "unsupported UE ID type %u in RANAP Paging", ranap_mi->present);
|
||||
|
||||
if (ranap_mi->choice.iMSI.size > sizeof(mi->imsi))
|
||||
return talloc_asprintf(OTC_SELECT, "invalid IMSI size %d > %zu",
|
||||
ranap_mi->choice.iMSI.size, sizeof(mi->imsi));
|
||||
|
||||
*mi = (struct osmo_mobile_identity){
|
||||
.type = GSM_MI_TYPE_IMSI,
|
||||
};
|
||||
ranap_bcd_decode(mi->imsi, sizeof(mi->imsi), ranap_mi->choice.iMSI.buf, ranap_mi->choice.iMSI.size);
|
||||
LOGP(DCN, LOGL_DEBUG, "ranap MI %s = %s\n", osmo_hexdump(ranap_mi->choice.iMSI.buf, ranap_mi->choice.iMSI.size),
|
||||
mi->imsi);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *omi_from_ranap_temp_ue_id(struct osmo_mobile_identity *mi, const RANAP_TemporaryUE_ID_t *ranap_tmsi)
|
||||
{
|
||||
const OCTET_STRING_t *tmsi_str;
|
||||
|
||||
if (!ranap_tmsi)
|
||||
return "null UE ID";
|
||||
|
||||
switch (ranap_tmsi->present) {
|
||||
case RANAP_TemporaryUE_ID_PR_tMSI:
|
||||
tmsi_str = &ranap_tmsi->choice.tMSI;
|
||||
break;
|
||||
case RANAP_TemporaryUE_ID_PR_p_TMSI:
|
||||
tmsi_str = &ranap_tmsi->choice.p_TMSI;
|
||||
break;
|
||||
default:
|
||||
return talloc_asprintf(OTC_SELECT, "unsupported Temporary UE ID type %u in RANAP Paging", ranap_tmsi->present);
|
||||
}
|
||||
|
||||
*mi = (struct osmo_mobile_identity){
|
||||
.type = GSM_MI_TYPE_TMSI,
|
||||
.tmsi = asn1str_to_u32(tmsi_str),
|
||||
};
|
||||
LOGP(DCN, LOGL_DEBUG, "ranap temp UE ID = %s\n", osmo_mobile_identity_to_str_c(OTC_SELECT, mi));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *cnlink_paging_add_ranap(struct hnbgw_cnlink *cnlink, const RANAP_PagingIEs_t *paging_ies)
|
||||
{
|
||||
struct osmo_mobile_identity mi = {};
|
||||
struct osmo_mobile_identity mi2 = {};
|
||||
RANAP_CN_DomainIndicator_t domain;
|
||||
const char *errmsg;
|
||||
|
||||
domain = paging_ies->cN_DomainIndicator;
|
||||
errmsg = omi_from_ranap_ue_id(&mi, &paging_ies->permanentNAS_UE_ID);
|
||||
|
||||
if (!errmsg && (paging_ies->presenceMask & PAGINGIES_RANAP_TEMPORARYUE_ID_PRESENT))
|
||||
errmsg = omi_from_ranap_temp_ue_id(&mi2, &paging_ies->temporaryUE_ID);
|
||||
|
||||
LOG_CNLINK(cnlink, DCN, errmsg ? LOGL_NOTICE : LOGL_DEBUG,
|
||||
"Decoded Paging: %s %s %s%s%s\n",
|
||||
ranap_domain_name(domain),
|
||||
osmo_mobile_identity_to_str_c(OTC_SELECT, &mi),
|
||||
mi2.type ? osmo_mobile_identity_to_str_c(OTC_SELECT, &mi2) : "-",
|
||||
errmsg ? " -- MI error: " : "",
|
||||
errmsg ? : "");
|
||||
|
||||
if (errmsg)
|
||||
return errmsg;
|
||||
|
||||
return cnlink_paging_add(cnlink, &mi, &mi2);
|
||||
}
|
||||
|
||||
/* If this cnlink has a recent Paging for the given MI, return true and drop the Paging record.
|
||||
* Else return false. */
|
||||
static bool cnlink_match_paging_mi(struct hnbgw_cnlink *cnlink, const struct osmo_mobile_identity *mi, time_t timeout)
|
||||
{
|
||||
struct cnlink_paging *p, *p2;
|
||||
llist_for_each_entry_safe(p, p2, &cnlink->paging, entry) {
|
||||
if (p->timestamp < timeout) {
|
||||
talloc_free(p);
|
||||
continue;
|
||||
}
|
||||
if (osmo_mobile_identity_cmp(&p->mi, mi)
|
||||
&& osmo_mobile_identity_cmp(&p->mi2, mi))
|
||||
continue;
|
||||
talloc_free(p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct hnbgw_cnlink *cnlink_find_by_paging_mi(struct hnbgw_cnpool *cnpool, const struct osmo_mobile_identity *mi)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
time_t timeout = 0;
|
||||
const char *errmsg;
|
||||
|
||||
errmsg = cnlink_paging_gettime(NULL, &timeout);
|
||||
if (errmsg)
|
||||
LOGP(DCN, LOGL_ERROR, "%s\n", errmsg);
|
||||
|
||||
llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) {
|
||||
if (!cnlink_match_paging_mi(cnlink, mi, timeout))
|
||||
continue;
|
||||
return cnlink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
@@ -87,8 +88,8 @@ struct hnbgw_context_map *context_map_alloc(struct hnb_context *hnb, uint32_t ru
|
||||
map->hnb_ctx = hnb;
|
||||
map->rua_ctx_id = rua_ctx_id;
|
||||
map->is_ps = is_ps;
|
||||
INIT_LLIST_HEAD(&map->ps_rab_ass);
|
||||
INIT_LLIST_HEAD(&map->ps_rabs);
|
||||
INIT_LLIST_HEAD(&map->ps_rab_ass_list);
|
||||
INIT_LLIST_HEAD(&map->ps_rab_list);
|
||||
|
||||
map_rua_fsm_alloc(map);
|
||||
|
||||
@@ -111,7 +112,7 @@ int context_map_set_cnlink(struct hnbgw_context_map *map, struct hnbgw_cnlink *c
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
new_scu_conn_id = osmo_sccp_instance_next_conn_id(hsu->ss7->sccp);
|
||||
new_scu_conn_id = osmo_sccp_instance_next_conn_id(osmo_ss7_get_sccp(hsu->ss7));
|
||||
if (new_scu_conn_id < 0) {
|
||||
LOG_MAP(map, DCN, LOGL_ERROR, "Unable to allocate SCCP conn ID on %s\n", hsu->name);
|
||||
return new_scu_conn_id;
|
||||
@@ -125,7 +126,7 @@ int context_map_set_cnlink(struct hnbgw_context_map *map, struct hnbgw_cnlink *c
|
||||
|
||||
hash_add(hsu->hnbgw_context_map_by_conn_id, &map->hnbgw_sccp_user_entry, new_scu_conn_id);
|
||||
|
||||
LOGP(DRUA, LOGL_NOTICE, "New conn: %s '%s' RUA-%u <-> SCCP-%u %s%s%s %s l=%s<->r=%s\n",
|
||||
LOGP(DRUA, LOGL_INFO, "New conn: %s '%s' RUA-%u <-> SCCP-%u %s%s%s %s l=%s<->r=%s\n",
|
||||
osmo_sock_get_name2_c(OTC_SELECT, osmo_stream_srv_get_ofd(map->hnb_ctx->conn)->fd),
|
||||
hnb_context_name(map->hnb_ctx), map->rua_ctx_id,
|
||||
new_scu_conn_id,
|
||||
@@ -186,7 +187,7 @@ void context_map_hnb_released(struct hnbgw_context_map *map)
|
||||
|
||||
void context_map_cnlink_lost(struct hnbgw_context_map *map)
|
||||
{
|
||||
map_sccp_dispatch(map, MAP_SCCP_EV_RAN_LINK_LOST, NULL);
|
||||
map_sccp_dispatch(map, MAP_SCCP_EV_CN_LINK_LOST, NULL);
|
||||
}
|
||||
|
||||
void context_map_free(struct hnbgw_context_map *map)
|
||||
|
||||
@@ -24,19 +24,12 @@
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_common_cn.h>
|
||||
|
||||
#if ENABLE_PFCP
|
||||
#include <osmocom/pfcp/pfcp_cp_peer.h>
|
||||
#endif
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
#include <osmocom/hnbgw/ps_rab_ass_fsm.h>
|
||||
#include <osmocom/hnbgw/kpi.h>
|
||||
#include <osmocom/hnbgw/hnbgw_ranap.h>
|
||||
|
||||
enum map_rua_fsm_state {
|
||||
MAP_RUA_ST_INIT,
|
||||
@@ -128,70 +121,19 @@ static int map_rua_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
}
|
||||
}
|
||||
|
||||
static int destruct_ranap_cn_rx_co_ies(ranap_message *ranap_message_p)
|
||||
{
|
||||
ranap_cn_rx_co_free(ranap_message_p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decode RANAP message with convenient memory freeing: just talloc_free() the returned pointer..
|
||||
* Allocate a ranap_message from OTC_SELECT, decode RANAP msgb into it, attach a talloc destructor that calls
|
||||
* ranap_cn_rx_co_free() upon talloc_free(), and return the decoded ranap_message. */
|
||||
ranap_message *hnbgw_decode_ranap_co(struct msgb *ranap_msg)
|
||||
{
|
||||
int rc;
|
||||
ranap_message *message;
|
||||
|
||||
if (!msg_has_l2_data(ranap_msg))
|
||||
return NULL;
|
||||
message = talloc_zero(OTC_SELECT, ranap_message);
|
||||
rc = ranap_cn_rx_co_decode2(message, msgb_l2(ranap_msg), msgb_l2len(ranap_msg));
|
||||
if (rc != 0) {
|
||||
talloc_free(message);
|
||||
return NULL;
|
||||
}
|
||||
talloc_set_destructor(message, destruct_ranap_cn_rx_co_ies);
|
||||
return message;
|
||||
}
|
||||
|
||||
/* Dispatch RANAP message to SCCP, if any. */
|
||||
static int handle_rx_rua(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
{
|
||||
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;
|
||||
|
||||
ranap_message *message = hnbgw_decode_ranap_co(ranap_msg);
|
||||
if (message) {
|
||||
LOGPFSML(fi, LOGL_DEBUG, "rx from RUA: RANAP %s\n",
|
||||
get_value_string(ranap_procedure_code_vals, message->procedureCode));
|
||||
|
||||
kpi_ranap_process_ul(map, message);
|
||||
|
||||
if (!map->is_ps) {
|
||||
/* See if it is a RAB Assignment Response message from RUA to SCCP, where we need to change the user plane
|
||||
* information, for RTP mapping via MGW, or GTP mapping via UPF. */
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* mgw_fsm_handle_rab_ass_resp() takes ownership of prim->oph and (ranap) message */
|
||||
return mgw_fsm_handle_cs_rab_ass_resp(map, ranap_msg, message);
|
||||
}
|
||||
} else {
|
||||
#if ENABLE_PFCP
|
||||
if (hnb_gw_is_gtp_mapping_enabled()) {
|
||||
/* map->is_ps == true and PFCP is enabled in osmo-hnbgw.cfg */
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* ps_rab_ass_fsm takes ownership of prim->oph and RANAP message */
|
||||
return hnbgw_gtpmap_rx_rab_ass_resp(map, ranap_msg, message);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* It was not a RAB Assignment Response that needed to be intercepted. Forward as-is to SCCP. */
|
||||
return map_sccp_dispatch(map, MAP_SCCP_EV_TX_DATA_REQUEST, ranap_msg);
|
||||
return hnbgw_ranap_rx_data_ul(map, ranap_msg);
|
||||
}
|
||||
|
||||
static int forward_ranap_to_rua(struct hnbgw_context_map *map, struct msgb *ranap_msg)
|
||||
@@ -214,16 +156,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:
|
||||
@@ -284,15 +224,21 @@ static void map_rua_connected_action(struct osmo_fsm_inst *fi, uint32_t event, v
|
||||
return;
|
||||
|
||||
case MAP_RUA_EV_RX_DISCONNECT:
|
||||
/* received Disconnect from RUA. forward any payload to SCCP, and change state. */
|
||||
if (!map_sccp_is_active(map)) {
|
||||
/* If, unlikely, the SCCP is already gone, changing to MAP_RUA_ST_DISCONNECTED frees the
|
||||
* hnbgw_context_map. Avoid a use-after-free. */
|
||||
map_rua_fsm_state_chg(MAP_RUA_ST_DISCONNECTED);
|
||||
return;
|
||||
/* 3GPP TS 25.468 9.1.5: RUA has disconnected.
|
||||
* - Under normal conditions (cause=Normal) the RUA Disconnect contains a RANAP Iu-ReleaseComplete.
|
||||
* On SCCP, the Iu-ReleaseComplete should still be forwarded as N-Data SCCP Data Form 1),
|
||||
* and we will expect the CN to send an SCCP RLSD soon.
|
||||
* - Under error conditions, cause!=Normal and there's no RANAP message.
|
||||
* In that case, we need to tear down the associated SCCP link towards CN,
|
||||
* which in turn will tear down the upper layer Iu conn.
|
||||
*/
|
||||
if (msg_has_l2_data(ranap_msg)) {
|
||||
/* Forward any payload to SCCP before Disconnect. */
|
||||
handle_rx_rua(fi, ranap_msg);
|
||||
} else {
|
||||
map->rua_fi_ctx.rua_disconnect_err_condition = true;
|
||||
}
|
||||
map_rua_fsm_state_chg(MAP_RUA_ST_DISCONNECTED);
|
||||
handle_rx_rua(fi, ranap_msg);
|
||||
return;
|
||||
|
||||
case MAP_RUA_EV_HNB_LINK_LOST:
|
||||
@@ -312,13 +258,13 @@ static void map_rua_connected_action(struct osmo_fsm_inst *fi, uint32_t event, v
|
||||
}
|
||||
}
|
||||
|
||||
static void map_rua_free_if_done(struct hnbgw_context_map *map, uint32_t sccp_event)
|
||||
static void map_rua_free_if_done(struct hnbgw_context_map *map, uint32_t sccp_event, void *ev_data)
|
||||
{
|
||||
/* From RUA's POV, we can now free the hnbgw_context_map.
|
||||
* If SCCP is still active, tell it to disconnect -- in that case the SCCP side will call context_map_free().
|
||||
* If SCCP is no longer active, free this map. */
|
||||
if (map_sccp_is_active(map))
|
||||
map_sccp_dispatch(map, sccp_event, NULL);
|
||||
map_sccp_dispatch(map, sccp_event, ev_data);
|
||||
else
|
||||
context_map_free(map);
|
||||
}
|
||||
@@ -326,21 +272,38 @@ static void map_rua_free_if_done(struct hnbgw_context_map *map, uint32_t sccp_ev
|
||||
static void map_rua_disconnected_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
map_rua_free_if_done(map, MAP_SCCP_EV_RAN_DISC);
|
||||
map_rua_free_if_done(map, MAP_SCCP_EV_RAN_DISC, (void *)map->rua_fi_ctx.rua_disconnect_err_condition);
|
||||
}
|
||||
|
||||
static void map_rua_disconnected_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct msgb *ranap_msg = data;
|
||||
if (msg_has_l2_data(ranap_msg))
|
||||
LOGPFSML(fi, LOGL_ERROR, "RUA not connected, cannot dispatch RANAP message\n");
|
||||
/* Ignore all events. */
|
||||
struct msgb *ranap_msg;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case MAP_RUA_EV_TX_DIRECT_TRANSFER:
|
||||
/* This can happen if CN is buggy, or in general if there was a race
|
||||
* condition between us forwarding the release towards CN (SCCP Release
|
||||
* or RANAP Iu-ReleaseComplete) and CN sendig whatever to us. */
|
||||
ranap_msg = data;
|
||||
if (msg_has_l2_data(ranap_msg)) {
|
||||
LOGPFSML(fi, LOGL_NOTICE, "RUA already disconnected, skip forwarding DL RANAP msg (%u bytes)\n",
|
||||
msgb_l2len(ranap_msg));
|
||||
LOGPFSML(fi, LOGL_DEBUG, "%s\n", osmo_hexdump(msgb_l2(ranap_msg), msgb_l2len(ranap_msg)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MAP_RUA_EV_CN_DISC:
|
||||
case MAP_RUA_EV_HNB_LINK_LOST:
|
||||
/* Ignore events. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void map_rua_disrupted_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
map_rua_free_if_done(map, MAP_SCCP_EV_RAN_LINK_LOST);
|
||||
map_rua_free_if_done(map, MAP_SCCP_EV_RAN_LINK_LOST, NULL);
|
||||
}
|
||||
|
||||
void map_rua_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
@@ -386,6 +349,7 @@ static const struct osmo_fsm_state map_rua_fsm_states[] = {
|
||||
[MAP_RUA_ST_DISCONNECTED] = {
|
||||
.name = "disconnected",
|
||||
.in_event_mask = 0
|
||||
| S(MAP_RUA_EV_TX_DIRECT_TRANSFER)
|
||||
| S(MAP_RUA_EV_CN_DISC)
|
||||
| S(MAP_RUA_EV_HNB_LINK_LOST)
|
||||
,
|
||||
|
||||
@@ -27,18 +27,11 @@
|
||||
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
|
||||
#if ENABLE_PFCP
|
||||
#include <osmocom/pfcp/pfcp_cp_peer.h>
|
||||
#endif
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/hnbgw_ranap.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
#include <osmocom/hnbgw/ps_rab_ass_fsm.h>
|
||||
#include <osmocom/hnbgw/kpi.h>
|
||||
|
||||
enum map_sccp_fsm_state {
|
||||
MAP_SCCP_ST_INIT,
|
||||
@@ -88,7 +81,7 @@ void map_sccp_fsm_alloc(struct hnbgw_context_map *map)
|
||||
|
||||
OSMO_ASSERT(map->sccp_fi == NULL);
|
||||
map->sccp_fi = fi;
|
||||
|
||||
INIT_LLIST_HEAD(&map->sccp_fi_ctx.wait_cc_tx_msg_list);
|
||||
/* trigger the timeout */
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_INIT);
|
||||
}
|
||||
@@ -125,10 +118,8 @@ bool map_sccp_is_active(struct hnbgw_context_map *map)
|
||||
static int tx_sccp_cr(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct osmo_scu_prim *prim;
|
||||
int rc;
|
||||
|
||||
if (!map->cnlink || !map->cnlink->hnbgw_sccp_user) {
|
||||
if (!map->cnlink) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP Connection Request: no CN link\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -138,134 +129,83 @@ static int tx_sccp_cr(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
ranap_msg = hnbgw_ranap_msg_alloc("SCCP-CR-empty");
|
||||
}
|
||||
|
||||
prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_REQUEST, ranap_msg);
|
||||
prim->u.connect.called_addr = map->cnlink->remote_addr;
|
||||
prim->u.connect.calling_addr = map->cnlink->hnbgw_sccp_user->local_addr;
|
||||
prim->u.connect.sccp_class = 2;
|
||||
prim->u.connect.conn_id = map->scu_conn_id;
|
||||
|
||||
rc = osmo_sccp_user_sap_down_nofree(map->cnlink->hnbgw_sccp_user->sccp_user, &prim->oph);
|
||||
if (rc)
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP Connection Request to CN\n");
|
||||
return rc;
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_N_CONNECT_REQ);
|
||||
return hnbgw_sccp_user_tx_connect_req(map->cnlink->hnbgw_sccp_user,
|
||||
&map->cnlink->remote_addr,
|
||||
map->scu_conn_id,
|
||||
ranap_msg);
|
||||
}
|
||||
|
||||
static int tx_sccp_df1(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct osmo_scu_prim *prim;
|
||||
int rc;
|
||||
|
||||
if (!msg_has_l2_data(ranap_msg))
|
||||
return 0;
|
||||
|
||||
if (!map->cnlink || !map->cnlink->hnbgw_sccp_user) {
|
||||
if (!map->cnlink) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP Data Form 1: no CN link\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, ranap_msg);
|
||||
prim->u.data.conn_id = map->scu_conn_id;
|
||||
|
||||
rc = osmo_sccp_user_sap_down_nofree(map->cnlink->hnbgw_sccp_user->sccp_user, &prim->oph);
|
||||
if (rc)
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP Data Form 1 to CN\n");
|
||||
return rc;
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_N_DATA_REQ);
|
||||
return hnbgw_sccp_user_tx_data_req(map->cnlink->hnbgw_sccp_user,
|
||||
map->scu_conn_id,
|
||||
ranap_msg);
|
||||
}
|
||||
|
||||
static int tx_sccp_rlsd(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
|
||||
if (!map->cnlink || !map->cnlink->hnbgw_sccp_user) {
|
||||
if (!map->cnlink) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to send SCCP RLSD: no CN link\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return osmo_sccp_tx_disconn(map->cnlink->hnbgw_sccp_user->sccp_user, map->scu_conn_id, NULL, 0);
|
||||
}
|
||||
|
||||
static int destruct_ranap_ran_rx_co_ies(ranap_message *ranap_message_p)
|
||||
{
|
||||
ranap_ran_rx_co_free(ranap_message_p);
|
||||
return 0;
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_N_DISCONNECT_REQ);
|
||||
return hnbgw_sccp_user_tx_disconnect_req(map->cnlink->hnbgw_sccp_user,
|
||||
map->scu_conn_id);
|
||||
}
|
||||
|
||||
static int handle_rx_sccp(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
int rc;
|
||||
|
||||
/* If the FSM instance has already terminated, don't dispatch anything. */
|
||||
if (fi->proc.terminating)
|
||||
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;
|
||||
|
||||
/* See if it is a RAB Assignment Request message from SCCP to RUA, where we need to change the user plane
|
||||
* information, for RTP mapping via MGW, or GTP mapping via UPF. */
|
||||
ranap_message *message;
|
||||
message = talloc_zero(OTC_SELECT, ranap_message);
|
||||
rc = ranap_ran_rx_co_decode(message, message, msgb_l2(ranap_msg), msgb_l2len(ranap_msg));
|
||||
if (rc == 0) {
|
||||
talloc_set_destructor(message, destruct_ranap_ran_rx_co_ies);
|
||||
return hnbgw_ranap_rx_data_dl(map, ranap_msg);
|
||||
}
|
||||
|
||||
LOGPFSML(fi, LOGL_DEBUG, "rx from SCCP: RANAP %s\n",
|
||||
get_value_string(ranap_procedure_code_vals, message->procedureCode));
|
||||
static void wait_cc_tx_msg_list_enqueue(struct hnbgw_context_map *map, struct msgb *ranap_msg)
|
||||
{
|
||||
talloc_steal(map, ranap_msg);
|
||||
msgb_enqueue(&map->sccp_fi_ctx.wait_cc_tx_msg_list, ranap_msg);
|
||||
}
|
||||
|
||||
kpi_ranap_process_dl(map, message);
|
||||
|
||||
if (!map->is_ps) {
|
||||
/* Circuit-Switched. Set up mapping of RTP ports via MGW */
|
||||
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* mgw_fsm_alloc_and_handle_rab_ass_req() takes ownership of (ranap) message */
|
||||
return handle_cs_rab_ass_req(map, ranap_msg, message);
|
||||
case RANAP_ProcedureCode_id_Iu_Release:
|
||||
/* Any IU Release will terminate the MGW FSM, the message itsself is not passed to the
|
||||
* FSM code. It is just forwarded normally by map_rua_tx_dt() below. */
|
||||
mgw_fsm_release(map);
|
||||
break;
|
||||
}
|
||||
#if ENABLE_PFCP
|
||||
} else {
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* If a UPF is configured, handle the RAB Assignment via ps_rab_ass_fsm, and replace the
|
||||
* GTP F-TEIDs in the RAB Assignment message before passing it on to RUA. */
|
||||
if (hnb_gw_is_gtp_mapping_enabled()) {
|
||||
LOGP(DMAIN, LOGL_DEBUG,
|
||||
"RAB Assignment: setting up GTP tunnel mapping via UPF %s\n",
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, &g_hnbgw->pfcp.cp_peer->remote_addr));
|
||||
return hnbgw_gtpmap_rx_rab_ass_req(map, ranap_msg, message);
|
||||
}
|
||||
/* If no UPF is configured, directly forward the message as-is (no GTP mapping). */
|
||||
LOGP(DMAIN, LOGL_DEBUG, "RAB Assignment: no UPF configured, forwarding as-is\n");
|
||||
break;
|
||||
|
||||
case RANAP_ProcedureCode_id_Iu_Release:
|
||||
/* Any IU Release will terminate the MGW FSM, the message itsself is not passed to the
|
||||
* FSM code. It is just forwarded normally by map_rua_tx_dt() below. */
|
||||
hnbgw_gtpmap_release(map);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* It was not a RAB Assignment Request that needed to be intercepted. Forward as-is to RUA. */
|
||||
return map_rua_dispatch(map, MAP_RUA_EV_TX_DIRECT_TRANSFER, ranap_msg);
|
||||
static struct msgb *wait_cc_tx_msg_list_dequeue(struct hnbgw_context_map *map)
|
||||
{
|
||||
struct msgb *ranap_msg = msgb_dequeue(&map->sccp_fi_ctx.wait_cc_tx_msg_list);
|
||||
if (ranap_msg)
|
||||
talloc_steal(OTC_SELECT, ranap_msg);
|
||||
return ranap_msg;
|
||||
}
|
||||
|
||||
static void map_sccp_init_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct msgb *ranap_msg = NULL;
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct msgb *ranap_msg = data;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case MAP_SCCP_EV_TX_DATA_REQUEST:
|
||||
ranap_msg = data;
|
||||
/* In the INIT state, the first MAP_SCCP_EV_TX_DATA_REQUEST will be the RANAP message received from the
|
||||
* RUA Connect message. Send the SCCP CR and transition to WAIT_CC. */
|
||||
if (tx_sccp_cr(fi, ranap_msg) == 0)
|
||||
@@ -273,18 +213,22 @@ static void map_sccp_init_action(struct osmo_fsm_inst *fi, uint32_t event, void
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RAN_LINK_LOST:
|
||||
case MAP_SCCP_EV_RAN_DISC:
|
||||
case MAP_SCCP_EV_USER_ABORT:
|
||||
case MAP_SCCP_EV_CN_LINK_LOST:
|
||||
/* No CR has been sent yet, just go to disconnected state. */
|
||||
if (msg_has_l2_data(ranap_msg))
|
||||
LOG_MAP(map, DLSCCP, LOGL_ERROR, "SCCP not connected, cannot dispatch RANAP message\n");
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RAN_DISC:
|
||||
/* bool rua_disconnect_err_condition = !!data; */
|
||||
/* 3GPP TS 25.468 9.1.5: RUA has disconnected.
|
||||
* In this state we didn't send an SCCP CR yet, so nothing to be torn down on CN side. */
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RX_RELEASED:
|
||||
/* SCCP RLSD received from CN. This will never happen since we haven't even asked for a connection, but
|
||||
* for completeness: */
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_RLSD_CN_ORIGIN);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
return;
|
||||
|
||||
@@ -296,33 +240,41 @@ static void map_sccp_init_action(struct osmo_fsm_inst *fi, uint32_t event, void
|
||||
static void map_sccp_wait_cc_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct msgb *ranap_msg = data;
|
||||
struct msgb *ranap_msg = NULL;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case MAP_SCCP_EV_RX_CONNECTION_CONFIRM:
|
||||
ranap_msg = data;
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_CONNECTED);
|
||||
/* Usually doesn't but if the SCCP CC contained data, forward it to RUA */
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_TX_DATA_REQUEST:
|
||||
LOGPFSML(fi, LOGL_ERROR, "Connection not yet confirmed, cannot forward RANAP to CN\n");
|
||||
ranap_msg = data;
|
||||
LOGPFSML(fi, LOGL_INFO, "Caching RANAP msg from RUA while waiting for SCCP CC\n");
|
||||
wait_cc_tx_msg_list_enqueue(map, ranap_msg);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RAN_LINK_LOST:
|
||||
case MAP_SCCP_EV_RAN_DISC:
|
||||
case MAP_SCCP_EV_USER_ABORT:
|
||||
case MAP_SCCP_EV_CN_LINK_LOST:
|
||||
/* RUA connection was terminated. First wait for the CC before releasing the SCCP conn. */
|
||||
if (msg_has_l2_data(ranap_msg))
|
||||
LOGPFSML(fi, LOGL_ERROR, "Connection not yet confirmed, cannot forward RANAP to CN\n");
|
||||
map->please_disconnect = true;
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RAN_DISC:
|
||||
/* bool rua_disconnect_err_condition = !!data; */
|
||||
/* 3GPP TS 25.468 9.1.5: RUA has disconnected.
|
||||
* In this state we didn't send an SCCP CR yet, so nothing to be torn down on CN side. */
|
||||
map->please_disconnect = true;
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RX_RELEASED:
|
||||
ranap_msg = data;
|
||||
/* SCCP RLSD received from CN. This will never happen since we haven't even received a Connection
|
||||
* Confirmed, but for completeness: */
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_RLSD_CN_ORIGIN);
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
return;
|
||||
@@ -335,9 +287,15 @@ static void map_sccp_wait_cc_action(struct osmo_fsm_inst *fi, uint32_t event, vo
|
||||
static void map_sccp_connected_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct msgb *ranap_msg;
|
||||
|
||||
/* Now that SCCP conn is confirmed, forward pending msgs received from RUA side: */
|
||||
while ((ranap_msg = wait_cc_tx_msg_list_dequeue(map)))
|
||||
tx_sccp_df1(fi, ranap_msg);
|
||||
|
||||
if (map->please_disconnect) {
|
||||
/* SCCP has already been asked to disconnect, so disconnect now that the CC has been received. Send RLSD
|
||||
* to SCCP (without RANAP data) */
|
||||
/* SCCP has already been asked to disconnect, so disconnect now that the
|
||||
* CC has been received. Send RLSD to SCCP (without RANAP data) */
|
||||
tx_sccp_rlsd(fi);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
}
|
||||
@@ -345,26 +303,41 @@ static void map_sccp_connected_onenter(struct osmo_fsm_inst *fi, uint32_t prev_s
|
||||
|
||||
static void map_sccp_connected_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct msgb *ranap_msg = data;
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
struct msgb *ranap_msg = NULL;
|
||||
bool rua_disconnect_err_condition;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case MAP_SCCP_EV_RX_DATA_INDICATION:
|
||||
ranap_msg = data;
|
||||
/* forward RANAP from SCCP to RUA */
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_TX_DATA_REQUEST:
|
||||
ranap_msg = data;
|
||||
/* Someone (usually the RUA side) wants us to send a RANAP payload to CN via SCCP */
|
||||
tx_sccp_df1(fi, ranap_msg);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RAN_DISC:
|
||||
/* RUA has disconnected, and usually has sent an Iu-ReleaseComplete along with its RUA Disconnect. On
|
||||
* SCCP, the Iu-ReleaseComplete should still be forwarded as N-Data (SCCP Data Form 1), and we will
|
||||
* expect the CN to send an SCCP RLSD soon. */
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_WAIT_RLSD);
|
||||
tx_sccp_df1(fi, ranap_msg);
|
||||
rua_disconnect_err_condition = !!data;
|
||||
/* 3GPP TS 25.468 9.1.5: RUA has disconnected.
|
||||
* - Under normal conditions (cause=Normal) the RUA Disconnect
|
||||
* contained a RANAP Iu-ReleaseComplete which we already
|
||||
* handled here through MAP_SCCP_EV_TX_DATA_REQUEST.
|
||||
* On SCCP, We will expect the CN to send an SCCP RLSD soon.
|
||||
* - Under error conditions, cause!=Normal and there was no RANAP message.
|
||||
* In that case, we need to tear down the associated SCCP link towards CN,
|
||||
* which in turn will tear down the upper layer Iu conn.
|
||||
*/
|
||||
if (rua_disconnect_err_condition) {
|
||||
tx_sccp_rlsd(fi);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
} else {
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_WAIT_RLSD);
|
||||
}
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RAN_LINK_LOST:
|
||||
@@ -374,23 +347,20 @@ static void map_sccp_connected_action(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
/* The user is asking for disconnection, so there is no Iu Release in progress. Disconnect now. */
|
||||
case MAP_SCCP_EV_CN_LINK_LOST:
|
||||
/* The CN peer has sent a RANAP RESET, so the old link that this map ran on is lost */
|
||||
|
||||
/* There won't be any ranap_msg, but if a caller wants to dispatch a msg, forward it before
|
||||
* disconnecting. */
|
||||
tx_sccp_df1(fi, ranap_msg);
|
||||
tx_sccp_rlsd(fi);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RX_RELEASED:
|
||||
/* The CN sends an N-Disconnect (SCCP Released) out of the usual sequence. Not what we expected, but
|
||||
* handle it. */
|
||||
LOGPFSML(fi, LOGL_ERROR, "CN sends SCCP Released sooner than expected\n");
|
||||
ranap_msg = data;
|
||||
/* The CN sends an N-Disconnect (SCCP Released). */
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_RLSD_CN_ORIGIN);
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RX_CONNECTION_CONFIRM:
|
||||
ranap_msg = data;
|
||||
/* Already connected. Unusual, but if there is data just forward it. */
|
||||
LOGPFSML(fi, LOGL_ERROR, "Already connected, but received SCCP CC again\n");
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
@@ -411,11 +381,12 @@ static void map_sccp_wait_rlsd_onenter(struct osmo_fsm_inst *fi, uint32_t prev_s
|
||||
|
||||
static void map_sccp_wait_rlsd_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct msgb *ranap_msg = data;
|
||||
struct msgb *ranap_msg = NULL;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case MAP_SCCP_EV_RX_RELEASED:
|
||||
ranap_msg = data;
|
||||
/* The CN sends the expected SCCP RLSD.
|
||||
* Usually there is no data, but if there is just forward it.
|
||||
* Usually RUA is already disconnected, but let the RUA FSM decide about that. */
|
||||
@@ -424,12 +395,13 @@ static void map_sccp_wait_rlsd_action(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RX_DATA_INDICATION:
|
||||
ranap_msg = data;
|
||||
/* RUA is probably already disconnected, but let the RUA FSM decide about that. */
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_TX_DATA_REQUEST:
|
||||
case MAP_SCCP_EV_RAN_DISC:
|
||||
ranap_msg = data;
|
||||
/* Normally, RUA would already disconnected, but since SCCP is officially still connected, we can still
|
||||
* forward messages there. Already waiting for CN to send the SCCP RLSD. If there is a message, forward
|
||||
* it, and just continue to time out on the SCCP RLSD. */
|
||||
@@ -437,6 +409,7 @@ static void map_sccp_wait_rlsd_action(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
return;
|
||||
|
||||
case MAP_SCCP_EV_RX_CONNECTION_CONFIRM:
|
||||
ranap_msg = data;
|
||||
/* Already connected. Unusual, but if there is data just forward it. */
|
||||
LOGPFSML(fi, LOGL_ERROR, "Already connected, but received SCCP CC\n");
|
||||
handle_rx_sccp(fi, ranap_msg);
|
||||
@@ -445,6 +418,7 @@ static void map_sccp_wait_rlsd_action(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
case MAP_SCCP_EV_RAN_LINK_LOST:
|
||||
case MAP_SCCP_EV_USER_ABORT:
|
||||
case MAP_SCCP_EV_CN_LINK_LOST:
|
||||
case MAP_SCCP_EV_RAN_DISC:
|
||||
/* Stop waiting for RLSD, send RLSD now. */
|
||||
tx_sccp_rlsd(fi);
|
||||
map_sccp_fsm_state_chg(MAP_SCCP_ST_DISCONNECTED);
|
||||
@@ -509,6 +483,7 @@ void map_sccp_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cau
|
||||
{
|
||||
struct hnbgw_context_map *map = fi->priv;
|
||||
map->sccp_fi = NULL;
|
||||
msgb_queue_free(&map->sccp_fi_ctx.wait_cc_tx_msg_list);
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
401
src/osmo-hnbgw/hnb.c
Normal file
401
src/osmo-hnbgw/hnb.c
Normal file
@@ -0,0 +1,401 @@
|
||||
/* HNB related code */
|
||||
|
||||
/* (C) 2015,2024 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2016-2025 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/sctp.h>
|
||||
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
|
||||
#include <osmocom/gsm/gsm23236.h>
|
||||
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/hnbgw/umts_cell_id.h>
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_hnbap.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
|
||||
/* update the active RAB duration rate_ctr for given HNB */
|
||||
void hnb_store_rab_durations(struct hnb_context *hnb)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
struct timespec now;
|
||||
uint64_t elapsed_cs_rab_ms = 0;
|
||||
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
/* iterate over all context_maps (subscribers) */
|
||||
llist_for_each_entry(map, &hnb->map_list, hnb_list) {
|
||||
/* skip any PS maps, we care about CS RABs only here */
|
||||
if (map->is_ps)
|
||||
continue;
|
||||
elapsed_cs_rab_ms += mgw_fsm_get_elapsed_ms(map, &now);
|
||||
}
|
||||
|
||||
/* Export to rate countes. */
|
||||
rate_ctr_add(HNBP_CTR(hnb->persistent, HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL), elapsed_cs_rab_ms);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* HNB Context
|
||||
***********************************************************************/
|
||||
|
||||
/* look-up HNB context by id. Used from CTRL */
|
||||
static struct hnb_context *hnb_context_by_id(uint32_t cid)
|
||||
{
|
||||
struct hnb_context *hnb;
|
||||
|
||||
llist_for_each_entry(hnb, &g_hnbgw->hnb_list, list) {
|
||||
if (hnb->id.cid == cid)
|
||||
return hnb;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look-up HNB context by identity_info. Used from VTY */
|
||||
struct hnb_context *hnb_context_by_identity_info(const char *identity_info)
|
||||
{
|
||||
struct hnb_context *hnb;
|
||||
|
||||
llist_for_each_entry(hnb, &g_hnbgw->hnb_list, list) {
|
||||
if (strcmp(identity_info, hnb->identity_info) == 0)
|
||||
return hnb;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int hnb_read_cb(struct osmo_stream_srv *conn);
|
||||
static int hnb_closed_cb(struct osmo_stream_srv *conn);
|
||||
|
||||
static struct hnb_context *hnb_context_alloc(struct osmo_stream_srv_link *link, int new_fd)
|
||||
{
|
||||
struct hnb_context *ctx;
|
||||
|
||||
ctx = talloc_zero(g_hnbgw, struct hnb_context);
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
INIT_LLIST_HEAD(&ctx->map_list);
|
||||
|
||||
ctx->conn = osmo_stream_srv_create(g_hnbgw, link, new_fd, hnb_read_cb, hnb_closed_cb, ctx);
|
||||
if (!ctx->conn) {
|
||||
LOGP(DMAIN, LOGL_INFO, "error while creating connection\n");
|
||||
talloc_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
llist_add_tail(&ctx->list, &g_hnbgw->hnb_list);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
const char *hnb_context_name(struct hnb_context *ctx)
|
||||
{
|
||||
char *result;
|
||||
if (!ctx)
|
||||
return "NULL";
|
||||
|
||||
if (ctx->conn) {
|
||||
char hostbuf_r[INET6_ADDRSTRLEN];
|
||||
char portbuf_r[6];
|
||||
int fd = osmo_stream_srv_get_ofd(ctx->conn)->fd;
|
||||
|
||||
/* get remote addr */
|
||||
if (osmo_sock_get_ip_and_port(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r, sizeof(portbuf_r), false) == 0)
|
||||
result = talloc_asprintf(OTC_SELECT, "%s:%s", hostbuf_r, portbuf_r);
|
||||
else
|
||||
result = "?";
|
||||
} else {
|
||||
result = "disconnected";
|
||||
}
|
||||
|
||||
if (g_hnbgw->config.log_prefix_hnb_id)
|
||||
result = talloc_asprintf(OTC_SELECT, "%s %s", result, ctx->identity_info);
|
||||
else
|
||||
result = talloc_asprintf(OTC_SELECT, "%s %s", result, umts_cell_id_to_str(&ctx->id));
|
||||
return result;
|
||||
}
|
||||
|
||||
void hnb_context_release_ue_state(struct hnb_context *ctx)
|
||||
{
|
||||
struct hnbgw_context_map *map, *map2;
|
||||
|
||||
/* deactivate all context maps */
|
||||
llist_for_each_entry_safe(map, map2, &ctx->map_list, hnb_list) {
|
||||
context_map_hnb_released(map);
|
||||
/* hnbgw_context_map will remove itself from lists when it is ready. */
|
||||
}
|
||||
}
|
||||
|
||||
void hnb_context_release(struct hnb_context *ctx)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
LOGHNB(ctx, DMAIN, LOGL_INFO, "Releasing HNB context\n");
|
||||
|
||||
if (ctx->persistent) {
|
||||
struct timespec tp;
|
||||
int rc;
|
||||
rc = osmo_clock_gettime(CLOCK_MONOTONIC, &tp);
|
||||
ctx->persistent->updowntime = (rc < 0) ? 0 : tp.tv_sec;
|
||||
}
|
||||
|
||||
/* remove from the list of HNB contexts */
|
||||
llist_del(&ctx->list);
|
||||
|
||||
hnb_context_release_ue_state(ctx);
|
||||
|
||||
if (ctx->conn) { /* we own a conn, we must free it: */
|
||||
LOGHNB(ctx, DMAIN, LOGL_INFO, "Closing HNB SCTP connection %s\n",
|
||||
osmo_sock_get_name2(osmo_stream_srv_get_ofd(ctx->conn)->fd));
|
||||
/* Avoid our closed_cb calling hnb_context_release() again: */
|
||||
osmo_stream_srv_set_data(ctx->conn, NULL);
|
||||
osmo_stream_srv_destroy(ctx->conn);
|
||||
} /* else: we are called from closed_cb, so conn is being freed separately */
|
||||
|
||||
/* hnbgw_context_map are still listed in ctx->map_list, but we are freeing ctx. Remove all entries from the
|
||||
* list, but keep the hnbgw_context_map around for graceful release. They are also listed under
|
||||
* hnbgw_cnlink->map_list, and will remove themselves when ready. */
|
||||
while ((map = llist_first_entry_or_null(&ctx->map_list, struct hnbgw_context_map, hnb_list))) {
|
||||
llist_del(&map->hnb_list);
|
||||
map->hnb_ctx = NULL;
|
||||
}
|
||||
|
||||
/* remove back reference from hnb_persistent to context */
|
||||
if (ctx->persistent)
|
||||
hnb_persistent_deregistered(ctx->persistent);
|
||||
|
||||
talloc_free(ctx);
|
||||
}
|
||||
|
||||
unsigned long long hnb_get_updowntime(const struct hnb_context *ctx)
|
||||
{
|
||||
if (!ctx->persistent)
|
||||
return 0;
|
||||
return hnbp_get_updowntime(ctx->persistent);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* SCTP Socket / stream handling
|
||||
***********************************************************************/
|
||||
|
||||
static int hnb_read_cb(struct osmo_stream_srv *conn)
|
||||
{
|
||||
struct hnb_context *hnb = osmo_stream_srv_get_data(conn);
|
||||
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
|
||||
struct msgb *msg = msgb_alloc(IUH_MSGB_SIZE, "Iuh rx");
|
||||
int rc;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
OSMO_ASSERT(hnb);
|
||||
/* we store a reference to the HomeNodeB in the msg->dest for the
|
||||
* benefit of various downstream processing functions */
|
||||
msg->dst = hnb;
|
||||
|
||||
rc = osmo_stream_srv_recv(conn, msg);
|
||||
/* Notification received */
|
||||
if (msgb_sctp_msg_flags(msg) & OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION) {
|
||||
union sctp_notification *notif = (union sctp_notification *)msgb_data(msg);
|
||||
rc = 0;
|
||||
switch (notif->sn_header.sn_type) {
|
||||
case SCTP_ASSOC_CHANGE:
|
||||
switch (notif->sn_assoc_change.sac_state) {
|
||||
case SCTP_COMM_LOST:
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE,
|
||||
"sctp_recvmsg(%s) = SCTP_COMM_LOST, closing conn\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
break;
|
||||
case SCTP_RESTART:
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE, "HNB SCTP conn RESTARTed, marking as HNBAP-unregistered\n");
|
||||
hnb->hnb_registered = false;
|
||||
hnb_context_release_ue_state(hnb);
|
||||
/* The tx queue may be quite full after an SCTP RESTART: (SYS#6113)
|
||||
* The link may have been flaky (a possible reason for the peer restarting the conn) and
|
||||
* hence the kernel socket Tx queue may be full (no ACKs coming back) and our own userspace
|
||||
* queue may contain plenty of oldish messages to be sent. Since the HNB will re-register after
|
||||
* this, we simply drop all those old messages: */
|
||||
osmo_stream_srv_clear_tx_queue(conn);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SCTP_SHUTDOWN_EVENT:
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE,
|
||||
"sctp_recvmsg(%s) = SCTP_SHUTDOWN_EVENT, closing conn\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
} else if (rc == -EAGAIN) {
|
||||
/* Older versions of osmo_stream_srv_recv() not supporting
|
||||
* msgb_sctp_msg_flags() may still return -EAGAIN when an sctp
|
||||
* notification is received. */
|
||||
rc = 0;
|
||||
goto out;
|
||||
} else if (rc < 0) {
|
||||
LOGHNB(hnb, DMAIN, LOGL_ERROR, "Error during sctp_recvmsg(%s)\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
goto out;
|
||||
} else if (rc == 0) {
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE, "Connection closed sctp_recvmsg(%s) = 0\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
goto out;
|
||||
} else {
|
||||
msgb_put(msg, rc);
|
||||
}
|
||||
|
||||
switch (msgb_sctp_ppid(msg)) {
|
||||
case IUH_PPI_HNBAP:
|
||||
hnb->hnbap_stream = msgb_sctp_stream(msg);
|
||||
rc = hnbgw_hnbap_rx(hnb, msg);
|
||||
break;
|
||||
case IUH_PPI_RUA:
|
||||
if (!hnb->hnb_registered) {
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE, "Discarding RUA as HNB is not registered\n");
|
||||
goto out;
|
||||
}
|
||||
hnb->rua_stream = msgb_sctp_stream(msg);
|
||||
rc = hnbgw_rua_rx(hnb, msg);
|
||||
break;
|
||||
case IUH_PPI_SABP:
|
||||
case IUH_PPI_RNA:
|
||||
case IUH_PPI_PUA:
|
||||
LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unimplemented SCTP PPID=%lu received\n", msgb_sctp_ppid(msg));
|
||||
rc = 0;
|
||||
break;
|
||||
default:
|
||||
LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unknown SCTP PPID=%lu received\n", msgb_sctp_ppid(msg));
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnb_closed_cb(struct osmo_stream_srv *conn)
|
||||
{
|
||||
struct hnb_context *hnb = osmo_stream_srv_get_data(conn);
|
||||
if (!hnb)
|
||||
return 0; /* hnb_context is being freed, nothing do be done */
|
||||
|
||||
/* hnb: conn became broken, let's release the associated hnb.
|
||||
* conn object is being freed after closed_cb(), so unassign it from hnb
|
||||
* if available to avoid it freeing it again: */
|
||||
hnb->conn = NULL;
|
||||
hnb_context_release(hnb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! call-back when the listen FD has something to read */
|
||||
int hnbgw_rua_accept_cb(struct osmo_stream_srv_link *srv, int fd)
|
||||
{
|
||||
struct hnb_context *ctx;
|
||||
|
||||
LOGP(DMAIN, LOGL_INFO, "New HNB SCTP connection %s\n",
|
||||
osmo_sock_get_name2(fd));
|
||||
|
||||
ctx = hnb_context_alloc(srv, fd);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_RO(hnb_info, "info");
|
||||
static int get_hnb_info(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct hnb_context *hnb = data;
|
||||
|
||||
cmd->reply = talloc_strdup(cmd, hnb->identity_info);
|
||||
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_RO(hnbs, "num-hnb");
|
||||
static int get_hnbs(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
cmd->reply = talloc_asprintf(cmd, "%u", llist_count(&g_hnbgw->hnb_list));
|
||||
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
int hnb_ctrl_cmds_install(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_hnbs);
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_HNB, &cmd_hnb_info);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int hnb_ctrl_node_lookup(void *data, vector vline, int *node_type, void **node_data, int *i)
|
||||
{
|
||||
const char *token = vector_slot(vline, *i);
|
||||
struct hnb_context *hnb;
|
||||
long num;
|
||||
|
||||
switch (*node_type) {
|
||||
case CTRL_NODE_ROOT:
|
||||
if (strcmp(token, "hnb") != 0)
|
||||
return 0;
|
||||
|
||||
(*i)++;
|
||||
|
||||
if (!ctrl_parse_get_num(vline, *i, &num))
|
||||
return -ERANGE;
|
||||
|
||||
hnb = hnb_context_by_id(num);
|
||||
if (!hnb)
|
||||
return -ENODEV;
|
||||
|
||||
*node_data = hnb;
|
||||
*node_type = CTRL_NODE_HNB;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
403
src/osmo-hnbgw/hnb_persistent.c
Normal file
403
src/osmo-hnbgw/hnb_persistent.c
Normal file
@@ -0,0 +1,403 @@
|
||||
/* HNB persistent related code */
|
||||
|
||||
/* (C) 2015,2024 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2016-2025 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/sctp.h>
|
||||
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
#include <osmocom/core/jhash.h>
|
||||
|
||||
#include <osmocom/gsm/gsm23236.h>
|
||||
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
|
||||
/***********************************************************************
|
||||
* HNB Persistent Data
|
||||
***********************************************************************/
|
||||
|
||||
const struct rate_ctr_desc hnb_ctr_description[] = {
|
||||
[HNB_CTR_IUH_ESTABLISHED] = {
|
||||
"iuh:established", "Number of times Iuh link was established" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_ERR_IND_UL] = {
|
||||
"ranap:ps:error_ind:ul", "Received ERROR Indications in Uplink (PS Domain)" },
|
||||
[HNB_CTR_RANAP_CS_ERR_IND_UL] = {
|
||||
"ranap:cs:error_ind:ul", "Received ERROR Indications in Uplink (PS Domain)" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RESET_REQ_UL] = {
|
||||
"ranap:ps:reset_req:ul", "Received RESET Requests in Uplink (PS Domain)" },
|
||||
[HNB_CTR_RANAP_CS_RESET_REQ_UL] = {
|
||||
"ranap:cs:reset_req:ul", "Received RESET Requests in Uplink (CS Domain)" },
|
||||
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_REQ] = {
|
||||
"ranap:ps:rab_act:req", "PS RAB Activations requested" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_REQ] = {
|
||||
"ranap:cs:rab_act:req", "CS RAB Activations requested" },
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_REQ_UNEXP] = {
|
||||
"ranap:ps:rab_act:req_unexp", "PS RAB Activations requested in unexpected state" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_REQ_UNEXP] = {
|
||||
"ranap:cs:rab_act:req_unexp", "CS RAB Activations requested in unexpected state" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_CNF] = {
|
||||
"ranap:ps:rab_act:cnf", "PS RAB Activations confirmed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_CNF] = {
|
||||
"ranap:cs:rab_act:cnf", "CS RAB Activations confirmed" },
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_CNF_UNEXP] = {
|
||||
"ranap:ps:rab_act:cnf_unexp", "PS RAB Activations confirmed in unexpected state" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_CNF_UNEXP] = {
|
||||
"ranap:cs:rab_act:cnf_unexp", "CS RAB Activations confirmed in unexpected state" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_FAIL] = {
|
||||
"ranap:ps:rab_act:fail", "PS RAB Activations failed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_FAIL] = {
|
||||
"ranap:cs:rab_act:fail", "CS RAB Activations failed" },
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_FAIL_UNEXP] = {
|
||||
"ranap:ps:rab_act:fail_unexp", "PS RAB Activations failed in unexpected state" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_FAIL_UNEXP] = {
|
||||
"ranap:cs:rab_act:fail_unexp", "CS RAB Activations failed in unexpected state" },
|
||||
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_MOD_REQ] = {
|
||||
"ranap:ps:rab_mod:req", "PS RAB Modifications requested" },
|
||||
[HNB_CTR_RANAP_CS_RAB_MOD_REQ] = {
|
||||
"ranap:cs:rab_mod:req", "CS RAB Modifications requested" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_MOD_CNF] = {
|
||||
"ranap:ps:rab_mod:cnf", "PS RAB Modifications confirmed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_MOD_CNF] = {
|
||||
"ranap:cs:rab_mod:cnf", "CS RAB Modifications confirmed" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_MOD_FAIL] = {
|
||||
"ranap:ps:rab_mod:fail", "PS RAB Modifications failed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_MOD_FAIL] = {
|
||||
"ranap:cs:rab_mod:fail", "CS RAB Modifications failed" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_REQ] = {
|
||||
"ranap:ps:rab_rel:req:normal", "PS RAB Release requested (by CN), normal" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_REQ] = {
|
||||
"ranap:cs:rab_rel:req:normal", "CS RAB Release requested (by CN), normal" },
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL] = {
|
||||
"ranap:ps:rab_rel:req:abnormal", "PS RAB Release requested (by CN), abnormal" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_REQ_ABNORMAL] = {
|
||||
"ranap:cs:rab_rel:req:abnormal", "CS RAB Release requested (by CN), abnormal" },
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_REQ_UNEXP] = {
|
||||
"ranap:ps:rab_rel:req:unexp", "PS RAB Release requested (by CN) in unexpected state" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_REQ_UNEXP] = {
|
||||
"ranap:cs:rab_rel:req:unexp", "CS RAB Release requested (by CN) in unexpected state" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_CNF] = {
|
||||
"ranap:ps:rab_rel:cnf", "PS RAB Release confirmed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_CNF] = {
|
||||
"ranap:cs:rab_rel:cnf", "CS RAB Release confirmed" },
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_CNF_UNEXP] = {
|
||||
"ranap:ps:rab_rel:cnf_unexp", "PS RAB Release confirmed in unexpected state" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_CNF_UNEXP] = {
|
||||
"ranap:cs:rab_rel:cnf_unexp", "CS RAB Release confirmed in unexpected state" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_FAIL] = {
|
||||
"ranap:ps:rab_rel:fail", "PS RAB Release failed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_FAIL] = {
|
||||
"ranap:cs:rab_rel:fail", "CS RAB Release failed" },
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_FAIL_UNEXP] = {
|
||||
"ranap:ps:rab_rel:fail_unexp", "PS RAB Release failed in unexpected state" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_FAIL_UNEXP] = {
|
||||
"ranap:cs:rab_rel:fail_unexp", "CS RAB Release failed in unexpected state" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT] = {
|
||||
"ranap:ps:rab_rel:implicit:normal", "PS RAB Release implicit (during Iu Release), normal" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT] = {
|
||||
"ranap:cs:rab_rel:implicit:normal", "CS RAB Release implicit (during Iu Release), normal" },
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL] = {
|
||||
"ranap:ps:rab_rel:implicit:abnormal", "PS RAB Release implicit (during Iu Release), abnormal" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT_ABNORMAL] = {
|
||||
"ranap:cs:rab_rel:implicit:abnormal", "CS RAB Release implicit (during Iu Release), abnormal" },
|
||||
|
||||
[HNB_CTR_RUA_ERR_IND] = {
|
||||
"rua:error_ind", "Received RUA Error Indications" },
|
||||
|
||||
[HNB_CTR_RUA_PS_CONNECT_UL] = {
|
||||
"rua:ps:connect:ul", "Received RUA Connect requests (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_CONNECT_UL] = {
|
||||
"rua:cs:connect:ul", "Received RUA Connect requests (CS Domain)" },
|
||||
|
||||
[HNB_CTR_RUA_PS_DISCONNECT_UL] = {
|
||||
"rua:ps:disconnect:ul", "Received RUA Disconnect requests in uplink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DISCONNECT_UL] = {
|
||||
"rua:cs:disconnect:ul", "Received RUA Disconnect requests in uplink (CS Domain)" },
|
||||
[HNB_CTR_RUA_PS_DISCONNECT_DL] = {
|
||||
"rua:ps:disconnect:dl", "Transmitted RUA Disconnect requests in downlink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DISCONNECT_DL] = {
|
||||
"rua:cs:disconnect:dl", "Transmitted RUA Disconnect requests in downlink (CS Domain)" },
|
||||
|
||||
[HNB_CTR_RUA_PS_DT_UL] = {
|
||||
"rua:ps:direct_transfer:ul", "Received RUA DirectTransfer in uplink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DT_UL] = {
|
||||
"rua:cs:direct_transfer:ul", "Received RUA DirectTransfer in uplink (CS Domain)" },
|
||||
[HNB_CTR_RUA_PS_DT_DL] = {
|
||||
"rua:ps:direct_transfer:dl", "Transmitted RUA DirectTransfer in downlink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DT_DL] = {
|
||||
"rua:cs:direct_transfer:dl", "Transmitted RUA DirectTransfer in downlink (CS Domain)" },
|
||||
|
||||
[HNB_CTR_RUA_UDT_UL] = {
|
||||
"rua:unit_data:ul", "Received RUA UnitData (UDT) in uplink" },
|
||||
[HNB_CTR_RUA_UDT_DL] = {
|
||||
"rua:unit_data:dl", "Transmitted RUA UnitData (UDT) in downlink" },
|
||||
|
||||
[HNB_CTR_PS_PAGING_ATTEMPTED] = {
|
||||
"paging:ps:attempted", "Transmitted PS Paging requests" },
|
||||
[HNB_CTR_CS_PAGING_ATTEMPTED] = {
|
||||
"paging:cs:attempted", "Transmitted CS Paging requests" },
|
||||
|
||||
[HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL] = {
|
||||
"rab:cs:active_milliseconds:total", "Cumulative number of milliseconds of CS RAB activity" },
|
||||
|
||||
[HNB_CTR_DTAP_CS_LU_REQ] = { "dtap:cs:location_update:req", "CS Location Update Requests" },
|
||||
[HNB_CTR_DTAP_CS_LU_ACC] = { "dtap:cs:location_update:accept", "CS Location Update Accepts" },
|
||||
[HNB_CTR_DTAP_CS_LU_REJ] = { "dtap:cs:location_update:reject", "CS Location Update Rejects" },
|
||||
|
||||
[HNB_CTR_DTAP_PS_ATT_REQ] = { "dtap:ps:attach:req", "PS Attach Requests" },
|
||||
[HNB_CTR_DTAP_PS_ATT_ACK] = { "dtap:ps:attach:accept", "PS Attach Accepts" },
|
||||
[HNB_CTR_DTAP_PS_ATT_REJ] = { "dtap:ps:attach:reject", "PS Attach Rejects" },
|
||||
|
||||
[HNB_CTR_DTAP_PS_RAU_REQ] = { "dtap:ps:routing_area_update:req", "PS Routing Area Update Requests" },
|
||||
[HNB_CTR_DTAP_PS_RAU_ACK] = { "dtap:ps:routing_area_update:accept", "PS Routing Area Update Accepts" },
|
||||
[HNB_CTR_DTAP_PS_RAU_REJ] = { "dtap:ps:routing_area_update:reject", "PS Routing Area Update Rejects" },
|
||||
|
||||
[HNB_CTR_GTPU_PACKETS_UL] = {
|
||||
"gtpu:packets:ul",
|
||||
"Count of GTP-U packets received from the HNB",
|
||||
},
|
||||
[HNB_CTR_GTPU_TOTAL_BYTES_UL] = {
|
||||
"gtpu:total_bytes:ul",
|
||||
"Count of total GTP-U bytes received from the HNB, including the GTP-U/UDP/IP headers",
|
||||
},
|
||||
[HNB_CTR_GTPU_UE_BYTES_UL] = {
|
||||
"gtpu:ue_bytes:ul",
|
||||
"Assuming an IP header length of 20 bytes, GTP-U bytes received from the HNB, excluding the GTP-U/UDP/IP headers",
|
||||
},
|
||||
[HNB_CTR_GTPU_PACKETS_DL] = {
|
||||
"gtpu:packets:dl",
|
||||
"Count of GTP-U packets sent to the HNB",
|
||||
},
|
||||
[HNB_CTR_GTPU_TOTAL_BYTES_DL] = {
|
||||
"gtpu:total_bytes:dl",
|
||||
"Count of total GTP-U bytes sent to the HNB, including the GTP-U/UDP/IP headers",
|
||||
},
|
||||
[HNB_CTR_GTPU_UE_BYTES_DL] = {
|
||||
"gtpu:ue_bytes:dl",
|
||||
"Assuming an IP header length of 20 bytes, GTP-U bytes sent to the HNB, excluding the GTP-U/UDP/IP headers",
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
const struct rate_ctr_group_desc hnb_ctrg_desc = {
|
||||
"hnb",
|
||||
"hNodeB",
|
||||
OSMO_STATS_CLASS_GLOBAL,
|
||||
ARRAY_SIZE(hnb_ctr_description),
|
||||
hnb_ctr_description,
|
||||
};
|
||||
|
||||
const struct osmo_stat_item_desc hnb_stat_desc[] = {
|
||||
[HNB_STAT_UPTIME_SECONDS] = { "uptime:seconds", "Seconds of uptime", "s", 60, 0 },
|
||||
};
|
||||
|
||||
const struct osmo_stat_item_group_desc hnb_statg_desc = {
|
||||
.group_name_prefix = "hnb",
|
||||
.group_description = "hNodeB",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_items = ARRAY_SIZE(hnb_stat_desc),
|
||||
.item_desc = hnb_stat_desc,
|
||||
};
|
||||
|
||||
static void hnb_persistent_disconnected_timeout_cb(void *data)
|
||||
{
|
||||
hnb_persistent_free(data);
|
||||
}
|
||||
|
||||
static void hnb_persistent_disconnected_timeout_schedule(struct hnb_persistent *hnbp)
|
||||
{
|
||||
unsigned long period_s = osmo_tdef_get(hnbgw_T_defs, -35, OSMO_TDEF_S, 60*60*24*7);
|
||||
if (period_s < 1) {
|
||||
LOG_HNBP(hnbp, LOGL_INFO,
|
||||
"timer X35 is zero, not setting a disconnected timeout for this hnb-persistent instance.\n");
|
||||
return;
|
||||
}
|
||||
/* It is fine if the timer is already active, osmo_timer_del() is done implicitly by the osmo_timer API. */
|
||||
osmo_timer_setup(&hnbp->disconnected_timeout, hnb_persistent_disconnected_timeout_cb, hnbp);
|
||||
osmo_timer_schedule(&hnbp->disconnected_timeout, period_s, 0);
|
||||
}
|
||||
|
||||
struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id)
|
||||
{
|
||||
struct hnb_persistent *hnbp = talloc_zero(g_hnbgw, struct hnb_persistent);
|
||||
if (!hnbp)
|
||||
return NULL;
|
||||
|
||||
hnbp->id = *id;
|
||||
hnbp->id_str = talloc_strdup(hnbp, umts_cell_id_to_str(id));
|
||||
hnbp->ctrs = rate_ctr_group_alloc(hnbp, &hnb_ctrg_desc, 0);
|
||||
if (!hnbp->ctrs)
|
||||
goto out_free;
|
||||
rate_ctr_group_set_name(hnbp->ctrs, hnbp->id_str);
|
||||
hnbp->statg = osmo_stat_item_group_alloc(hnbp, &hnb_statg_desc, 0);
|
||||
if (!hnbp->statg)
|
||||
goto out_free_ctrs;
|
||||
osmo_stat_item_group_set_name(hnbp->statg, hnbp->id_str);
|
||||
|
||||
llist_add(&hnbp->list, &g_hnbgw->hnb_persistent_list);
|
||||
hash_add(g_hnbgw->hnb_persistent_by_id, &hnbp->node_by_id, umts_cell_id_hash(&hnbp->id));
|
||||
|
||||
if (g_hnbgw->nft_kpi.active)
|
||||
nft_kpi_hnb_persistent_add(hnbp);
|
||||
|
||||
/* Normally the disconnected timer runs only when the hNodeB is not currently connected on Iuh. This here is paranoia:
|
||||
* In case we have to HNBAP HNB Register Reject, the disconnected timer should be active on this unused hnbp.
|
||||
* On success, hnb_persistent_registered() will stop the disconnected timer directly after this. */
|
||||
hnb_persistent_disconnected_timeout_schedule(hnbp);
|
||||
|
||||
return hnbp;
|
||||
|
||||
out_free_ctrs:
|
||||
rate_ctr_group_free(hnbp->ctrs);
|
||||
out_free:
|
||||
talloc_free(hnbp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct hnb_persistent *hnb_persistent_find_by_id(const struct umts_cell_id *id)
|
||||
{
|
||||
struct hnb_persistent *hnbp;
|
||||
uint32_t id_hash = umts_cell_id_hash(id);
|
||||
hash_for_each_possible(g_hnbgw->hnb_persistent_by_id, hnbp, node_by_id, id_hash) {
|
||||
if (umts_cell_id_equal(&hnbp->id, id))
|
||||
return hnbp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Read the peer's remote IP address from the Iuh conn's fd, and set up GTP-U counters for that remote address. */
|
||||
static void hnb_persistent_update_remote_addr(struct hnb_persistent *hnbp)
|
||||
{
|
||||
socklen_t socklen;
|
||||
struct osmo_sockaddr osa;
|
||||
struct osmo_sockaddr_str remote_str;
|
||||
int fd;
|
||||
|
||||
fd = osmo_stream_srv_get_fd(hnbp->ctx->conn);
|
||||
if (fd < 0) {
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "no active socket fd, cannot set up traffic counters\n");
|
||||
return;
|
||||
}
|
||||
|
||||
socklen = sizeof(struct osmo_sockaddr);
|
||||
if (getpeername(fd, &osa.u.sa, &socklen)) {
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "cannot read remote address, cannot set up traffic counters\n");
|
||||
return;
|
||||
}
|
||||
if (osmo_sockaddr_str_from_osa(&remote_str, &osa)) {
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "cannot parse remote address, cannot set up traffic counters\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* We got the remote address from the Iuh link (RUA), and now we are blatantly assuming that the hNodeB has its
|
||||
* GTP endpoint on the same IP address, just with UDP port 2152 (the fixed GTP port as per 3GPP spec). */
|
||||
remote_str.port = 2152;
|
||||
|
||||
if (nft_kpi_hnb_start(hnbp, &remote_str))
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "failed to set up traffic counters\n");
|
||||
}
|
||||
|
||||
/* Whenever HNBAP registers a HNB, hnbgw_hnbap.c calls this function to let the hnb_persistent update its state to the
|
||||
* (new) remote address being active. When calling this function, a hnbp->ctx should be present, with an active
|
||||
* osmo_stream_srv conn. */
|
||||
void hnb_persistent_registered(struct hnb_persistent *hnbp)
|
||||
{
|
||||
if (!hnbp->ctx) {
|
||||
LOG_HNBP(hnbp, LOGL_ERROR, "hnb_persistent_registered() invoked, but there is no hnb_ctx\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* The hNodeB is now connected, i.e. not disconnected. */
|
||||
osmo_timer_del(&hnbp->disconnected_timeout);
|
||||
|
||||
/* start counting traffic */
|
||||
if (g_hnbgw->nft_kpi.active)
|
||||
hnb_persistent_update_remote_addr(hnbp);
|
||||
}
|
||||
|
||||
/* Whenever a HNB is regarded as no longer registered (HNBAP HNB De-Register, or the Iuh link drops), this function is
|
||||
* called to to let the hnb_persistent update its state to the hNodeB being disconnected. Clear the ctx->persistent and
|
||||
* hnbp->ctx relations; do not delete the hnb_persistent instance. */
|
||||
void hnb_persistent_deregistered(struct hnb_persistent *hnbp)
|
||||
{
|
||||
/* clear out cross references of hnb_context and hnb_persistent */
|
||||
if (hnbp->ctx) {
|
||||
if (hnbp->ctx->persistent == hnbp)
|
||||
hnbp->ctx->persistent = NULL;
|
||||
hnbp->ctx = NULL;
|
||||
}
|
||||
|
||||
/* stop counting traffic */
|
||||
nft_kpi_hnb_stop(hnbp);
|
||||
|
||||
/* The hNodeB is now disconnected. Clear out hnb_persistent when the disconnected timeout has passed. */
|
||||
hnb_persistent_disconnected_timeout_schedule(hnbp);
|
||||
}
|
||||
|
||||
void hnb_persistent_free(struct hnb_persistent *hnbp)
|
||||
{
|
||||
/* FIXME: check if in use? */
|
||||
osmo_timer_del(&hnbp->disconnected_timeout);
|
||||
nft_kpi_hnb_stop(hnbp);
|
||||
nft_kpi_hnb_persistent_remove(hnbp);
|
||||
osmo_stat_item_group_free(hnbp->statg);
|
||||
rate_ctr_group_free(hnbp->ctrs);
|
||||
llist_del(&hnbp->list);
|
||||
hash_del(&hnbp->node_by_id);
|
||||
talloc_free(hnbp);
|
||||
}
|
||||
|
||||
/* return the amount of time the HNB is up (hnbp->ctx != NULL) or down (hnbp->ctx == NULL) */
|
||||
unsigned long long hnbp_get_updowntime(const struct hnb_persistent *hnbp)
|
||||
{
|
||||
struct timespec tp;
|
||||
|
||||
if (!hnbp->updowntime)
|
||||
return 0;
|
||||
|
||||
if (osmo_clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
|
||||
return 0;
|
||||
|
||||
return difftime(tp.tv_sec, hnbp->updowntime);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/* kitchen sink for OsmoHNBGW implementation */
|
||||
|
||||
/* (C) 2015,2024 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2016-2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* (C) 2016-2025 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -19,12 +19,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/sctp.h>
|
||||
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
#include <osmocom/core/jhash.h>
|
||||
|
||||
#include <osmocom/vty/vty.h>
|
||||
|
||||
@@ -37,12 +40,15 @@
|
||||
#include <osmocom/pfcp/pfcp_proto.h>
|
||||
#endif
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_hnbap.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#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;
|
||||
|
||||
@@ -52,25 +58,17 @@ const struct value_string ranap_domain_names[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
/* update the active RAB duration rate_ctr for given HNB */
|
||||
static void hnb_store_rab_durations(struct hnb_context *hnb)
|
||||
|
||||
/* timer call-back: Update the HNB_STAT_UPTIME_SECONDS stat item of each hnb_persistent */
|
||||
static void hnbgw_store_hnb_uptime(void *data)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
struct timespec now;
|
||||
uint64_t elapsed_cs_rab_ms = 0;
|
||||
struct hnb_persistent *hnbp;
|
||||
|
||||
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
/* iterate over all context_maps (subscribers) */
|
||||
llist_for_each_entry(map, &hnb->map_list, hnb_list) {
|
||||
/* skip any PS maps, we care about CS RABs only here */
|
||||
if (map->is_ps)
|
||||
continue;
|
||||
elapsed_cs_rab_ms += mgw_fsm_get_elapsed_ms(map, &now);
|
||||
llist_for_each_entry(hnbp, &g_hnbgw->hnb_persistent_list, list) {
|
||||
HNBP_STAT_SET(hnbp, HNB_STAT_UPTIME_SECONDS, hnbp->ctx != NULL ? hnbp_get_updowntime(hnbp) : 0);
|
||||
}
|
||||
|
||||
/* Export to rate countes. */
|
||||
rate_ctr_add(HNBP_CTR(hnb->persistent, HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL), elapsed_cs_rab_ms);
|
||||
osmo_timer_schedule(&g_hnbgw->store_uptime_timer, STORE_UPTIME_INTERVAL, 0);
|
||||
}
|
||||
|
||||
static void hnbgw_store_hnb_rab_durations(void *data)
|
||||
@@ -87,672 +85,13 @@ static void hnbgw_store_hnb_rab_durations(void *data)
|
||||
osmo_timer_schedule(&g_hnbgw->hnb_store_rab_durations_timer, HNB_STORE_RAB_DURATIONS_INTERVAL, 0);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* UE Context
|
||||
***********************************************************************/
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
***********************************************************************/
|
||||
|
||||
/* look-up HNB context by id. Used from CTRL */
|
||||
static struct hnb_context *hnb_context_by_id(uint32_t cid)
|
||||
{
|
||||
struct hnb_context *hnb;
|
||||
|
||||
llist_for_each_entry(hnb, &g_hnbgw->hnb_list, list) {
|
||||
if (hnb->id.cid == cid)
|
||||
return hnb;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look-up HNB context by identity_info. Used from VTY */
|
||||
struct hnb_context *hnb_context_by_identity_info(const char *identity_info)
|
||||
{
|
||||
struct hnb_context *hnb;
|
||||
|
||||
llist_for_each_entry(hnb, &g_hnbgw->hnb_list, list) {
|
||||
if (strcmp(identity_info, hnb->identity_info) == 0)
|
||||
return hnb;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int hnb_read_cb(struct osmo_stream_srv *conn);
|
||||
static int hnb_closed_cb(struct osmo_stream_srv *conn);
|
||||
|
||||
static struct hnb_context *hnb_context_alloc(struct osmo_stream_srv_link *link, int new_fd)
|
||||
{
|
||||
struct hnb_context *ctx;
|
||||
|
||||
ctx = talloc_zero(g_hnbgw, struct hnb_context);
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
INIT_LLIST_HEAD(&ctx->map_list);
|
||||
|
||||
ctx->conn = osmo_stream_srv_create(g_hnbgw, link, new_fd, hnb_read_cb, hnb_closed_cb, ctx);
|
||||
if (!ctx->conn) {
|
||||
LOGP(DMAIN, LOGL_INFO, "error while creating connection\n");
|
||||
talloc_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
llist_add_tail(&ctx->list, &g_hnbgw->hnb_list);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
const char *umts_cell_id_name(const struct umts_cell_id *ucid)
|
||||
{
|
||||
const char *fmtstr = "%03u-%02u-L%u-R%u-S%u-C%u";
|
||||
|
||||
if (g_hnbgw->config.plmn.mnc_3_digits)
|
||||
fmtstr = "%03u-%03u-L%u-R%u-S%u-C%u";
|
||||
|
||||
return talloc_asprintf(OTC_SELECT, fmtstr, ucid->mcc, ucid->mnc, ucid->lac, ucid->rac,
|
||||
ucid->sac, ucid->cid);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
if (rc < 0)
|
||||
return -errno;
|
||||
|
||||
if (rc != 6)
|
||||
return -EINVAL;
|
||||
|
||||
if (ucid->mcc > 999)
|
||||
return -EINVAL;
|
||||
|
||||
if (ucid->mnc > 999)
|
||||
return -EINVAL;
|
||||
|
||||
if (ucid->lac == 0 || ucid->lac == 0xffff)
|
||||
return -EINVAL;
|
||||
|
||||
/* CellIdentity in the ASN.1 syntax is a bit-string of 28 bits length */
|
||||
if (ucid->cid >= (1 << 28))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *hnb_context_name(struct hnb_context *ctx)
|
||||
{
|
||||
char *result;
|
||||
if (!ctx)
|
||||
return "NULL";
|
||||
|
||||
if (ctx->conn) {
|
||||
char hostbuf_r[INET6_ADDRSTRLEN];
|
||||
char portbuf_r[6];
|
||||
int fd = osmo_stream_srv_get_ofd(ctx->conn)->fd;
|
||||
|
||||
/* get remote addr */
|
||||
if (osmo_sock_get_ip_and_port(fd, hostbuf_r, sizeof(hostbuf_r), portbuf_r, sizeof(portbuf_r), false) == 0)
|
||||
result = talloc_asprintf(OTC_SELECT, "%s:%s", hostbuf_r, portbuf_r);
|
||||
else
|
||||
result = "?";
|
||||
} else {
|
||||
result = "disconnected";
|
||||
}
|
||||
|
||||
if (g_hnbgw->config.log_prefix_hnb_id)
|
||||
result = talloc_asprintf(OTC_SELECT, "%s %s", result, ctx->identity_info);
|
||||
else
|
||||
result = talloc_asprintf(OTC_SELECT, "%s %s", result, umts_cell_id_name(&ctx->id));
|
||||
return result;
|
||||
}
|
||||
|
||||
void hnb_context_release_ue_state(struct hnb_context *ctx)
|
||||
{
|
||||
struct hnbgw_context_map *map, *map2;
|
||||
|
||||
/* deactivate all context maps */
|
||||
llist_for_each_entry_safe(map, map2, &ctx->map_list, hnb_list) {
|
||||
context_map_hnb_released(map);
|
||||
/* hnbgw_context_map will remove itself from lists when it is ready. */
|
||||
}
|
||||
ue_context_free_by_hnb(ctx);
|
||||
}
|
||||
|
||||
void hnb_context_release(struct hnb_context *ctx)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
LOGHNB(ctx, DMAIN, LOGL_INFO, "Releasing HNB context\n");
|
||||
|
||||
if (ctx->persistent) {
|
||||
struct timespec tp;
|
||||
int rc;
|
||||
rc = osmo_clock_gettime(CLOCK_MONOTONIC, &tp);
|
||||
ctx->persistent->updowntime = (rc < 0) ? 0 : tp.tv_sec;
|
||||
}
|
||||
|
||||
/* remove from the list of HNB contexts */
|
||||
llist_del(&ctx->list);
|
||||
|
||||
hnb_context_release_ue_state(ctx);
|
||||
|
||||
if (ctx->conn) { /* we own a conn, we must free it: */
|
||||
LOGHNB(ctx, DMAIN, LOGL_INFO, "Closing HNB SCTP connection %s\n",
|
||||
osmo_sock_get_name2(osmo_stream_srv_get_ofd(ctx->conn)->fd));
|
||||
/* Avoid our closed_cb calling hnb_context_release() again: */
|
||||
osmo_stream_srv_set_data(ctx->conn, NULL);
|
||||
osmo_stream_srv_destroy(ctx->conn);
|
||||
} /* else: we are called from closed_cb, so conn is being freed separately */
|
||||
|
||||
/* hnbgw_context_map are still listed in ctx->map_list, but we are freeing ctx. Remove all entries from the
|
||||
* list, but keep the hnbgw_context_map around for graceful release. They are also listed under
|
||||
* hnbgw_cnlink->map_list, and will remove themselves when ready. */
|
||||
while ((map = llist_first_entry_or_null(&ctx->map_list, struct hnbgw_context_map, hnb_list))) {
|
||||
llist_del(&map->hnb_list);
|
||||
map->hnb_ctx = NULL;
|
||||
}
|
||||
|
||||
/* remove back reference from hnb_persistent to context */
|
||||
if (ctx->persistent)
|
||||
ctx->persistent->ctx = NULL;
|
||||
|
||||
talloc_free(ctx);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* HNB Persistent Data
|
||||
***********************************************************************/
|
||||
|
||||
const struct rate_ctr_desc hnb_ctr_description[] = {
|
||||
[HNB_CTR_IUH_ESTABLISHED] = {
|
||||
"iuh:established", "Number of times Iuh link was established" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_ERR_IND_UL] = {
|
||||
"ranap:ps:error_ind:ul", "Received ERROR Indications in Uplink (PS Domain)" },
|
||||
[HNB_CTR_RANAP_CS_ERR_IND_UL] = {
|
||||
"ranap:cs:error_ind:ul", "Received ERROR Indications in Uplink (PS Domain)" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RESET_REQ_UL] = {
|
||||
"ranap:ps:reset_req:ul", "Received RESET Requests in Uplink (PS Domain)" },
|
||||
[HNB_CTR_RANAP_CS_RESET_REQ_UL] = {
|
||||
"ranap:cs:reset_req:ul", "Received RESET Requests in Uplink (CS Domain)" },
|
||||
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_REQ] = {
|
||||
"ranap:ps:rab_act:req", "PS RAB Activations requested" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_REQ] = {
|
||||
"ranap:cs:rab_act:req", "CS RAB Activations requested" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_CNF] = {
|
||||
"ranap:ps:rab_act:cnf", "PS RAB Activations confirmed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_CNF] = {
|
||||
"ranap:cs:rab_act:cnf", "CS RAB Activations confirmed" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_ACT_FAIL] = {
|
||||
"ranap:ps:rab_act:fail", "PS RAB Activations failed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_ACT_FAIL] = {
|
||||
"ranap:cs:rab_act:fail", "CS RAB Activations failed" },
|
||||
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_MOD_REQ] = {
|
||||
"ranap:ps:rab_mod:req", "PS RAB Modifications requested" },
|
||||
[HNB_CTR_RANAP_CS_RAB_MOD_REQ] = {
|
||||
"ranap:cs:rab_mod:req", "CS RAB Modifications requested" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_MOD_CNF] = {
|
||||
"ranap:ps:rab_mod:cnf", "PS RAB Modifications confirmed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_MOD_CNF] = {
|
||||
"ranap:cs:rab_mod:cnf", "CS RAB Modifications confirmed" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_MOD_FAIL] = {
|
||||
"ranap:ps:rab_mod:fail", "PS RAB Modifications failed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_MOD_FAIL] = {
|
||||
"ranap:cs:rab_mod:fail", "CS RAB Modifications failed" },
|
||||
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_REQ] = {
|
||||
"ranap:ps:rab_rel:req", "PS RAB Release requested" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_REQ] = {
|
||||
"ranap:cs:rab_rel:req", "CS RAB Release requested" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_CNF] = {
|
||||
"ranap:ps:rab_rel:cnf", "PS RAB Release confirmed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_CNF] = {
|
||||
"ranap:cs:rab_rel:cnf", "CS RAB Release confirmed" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_FAIL] = {
|
||||
"ranap:ps:rab_rel:fail", "PS RAB Release failed" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_FAIL] = {
|
||||
"ranap:cs:rab_rel:fail", "CS RAB Release failed" },
|
||||
|
||||
[HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT] = {
|
||||
"ranap:ps:rab_rel:implicit", "PS RAB Release implicit (during Iu Release)" },
|
||||
[HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT] = {
|
||||
"ranap:cs:rab_rel:implicit", "CS RAB Release implicit (during Iu Release)" },
|
||||
|
||||
[HNB_CTR_RUA_ERR_IND] = {
|
||||
"rua:error_ind", "Received RUA Error Indications" },
|
||||
|
||||
[HNB_CTR_RUA_PS_CONNECT_UL] = {
|
||||
"rua:ps:connect:ul", "Received RUA Connect requests (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_CONNECT_UL] = {
|
||||
"rua:cs:connect:ul", "Received RUA Connect requests (CS Domain)" },
|
||||
|
||||
[HNB_CTR_RUA_PS_DISCONNECT_UL] = {
|
||||
"rua:ps:disconnect:ul", "Received RUA Disconnect requests in uplink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DISCONNECT_UL] = {
|
||||
"rua:cs:disconnect:ul", "Received RUA Disconnect requests in uplink (CS Domain)" },
|
||||
[HNB_CTR_RUA_PS_DISCONNECT_DL] = {
|
||||
"rua:ps:disconnect:dl", "Transmitted RUA Disconnect requests in downlink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DISCONNECT_DL] = {
|
||||
"rua:cs:disconnect:dl", "Transmitted RUA Disconnect requests in downlink (CS Domain)" },
|
||||
|
||||
[HNB_CTR_RUA_PS_DT_UL] = {
|
||||
"rua:ps:direct_transfer:ul", "Received RUA DirectTransfer in uplink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DT_UL] = {
|
||||
"rua:cs:direct_transfer:ul", "Received RUA DirectTransfer in uplink (CS Domain)" },
|
||||
[HNB_CTR_RUA_PS_DT_DL] = {
|
||||
"rua:ps:direct_transfer:dl", "Transmitted RUA DirectTransfer in downlink (PS Domain)" },
|
||||
[HNB_CTR_RUA_CS_DT_DL] = {
|
||||
"rua:cs:direct_transfer:dl", "Transmitted RUA DirectTransfer in downlink (CS Domain)" },
|
||||
|
||||
[HNB_CTR_RUA_UDT_UL] = {
|
||||
"rua:unit_data:ul", "Received RUA UnitData (UDT) in uplink" },
|
||||
[HNB_CTR_RUA_UDT_DL] = {
|
||||
"rua:unit_data:dl", "Transmitted RUA UnitData (UDT) in downlink" },
|
||||
|
||||
[HNB_CTR_PS_PAGING_ATTEMPTED] = {
|
||||
"paging:ps:attempted", "Transmitted PS Paging requests" },
|
||||
[HNB_CTR_CS_PAGING_ATTEMPTED] = {
|
||||
"paging:cs:attempted", "Transmitted CS Paging requests" },
|
||||
|
||||
[HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL] = {
|
||||
"rab:cs:active_milliseconds:total", "Cumulative number of milliseconds of CS RAB activity" },
|
||||
};
|
||||
|
||||
const struct rate_ctr_group_desc hnb_ctrg_desc = {
|
||||
"hnb",
|
||||
"hNodeB",
|
||||
OSMO_STATS_CLASS_GLOBAL,
|
||||
ARRAY_SIZE(hnb_ctr_description),
|
||||
hnb_ctr_description,
|
||||
};
|
||||
|
||||
const struct osmo_stat_item_desc hnb_stat_desc[] = {
|
||||
[HNB_STAT_UPTIME_SECONDS] = { "uptime:seconds", "Seconds of uptime", "s", 60, 0 },
|
||||
};
|
||||
|
||||
const struct osmo_stat_item_group_desc hnb_statg_desc = {
|
||||
.group_name_prefix = "hnb",
|
||||
.group_description = "hNodeB",
|
||||
.class_id = OSMO_STATS_CLASS_GLOBAL,
|
||||
.num_items = ARRAY_SIZE(hnb_stat_desc),
|
||||
.item_desc = hnb_stat_desc,
|
||||
};
|
||||
|
||||
struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id)
|
||||
{
|
||||
struct hnb_persistent *hnbp = talloc_zero(g_hnbgw, struct hnb_persistent);
|
||||
if (!hnbp)
|
||||
return NULL;
|
||||
|
||||
hnbp->id = *id;
|
||||
hnbp->id_str = talloc_strdup(hnbp, umts_cell_id_name(id));
|
||||
hnbp->ctrs = rate_ctr_group_alloc(hnbp, &hnb_ctrg_desc, 0);
|
||||
if (!hnbp->ctrs)
|
||||
goto out_free;
|
||||
rate_ctr_group_set_name(hnbp->ctrs, hnbp->id_str);
|
||||
hnbp->statg = osmo_stat_item_group_alloc(hnbp, &hnb_statg_desc, 0);
|
||||
if (!hnbp->statg)
|
||||
goto out_free_ctrs;
|
||||
osmo_stat_item_group_set_name(hnbp->statg, hnbp->id_str);
|
||||
|
||||
llist_add(&hnbp->list, &g_hnbgw->hnb_persistent_list);
|
||||
|
||||
return hnbp;
|
||||
|
||||
out_free_ctrs:
|
||||
rate_ctr_group_free(hnbp->ctrs);
|
||||
out_free:
|
||||
talloc_free(hnbp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct hnb_persistent *hnb_persistent_find_by_id(const struct umts_cell_id *id)
|
||||
{
|
||||
struct hnb_persistent *hnbp;
|
||||
|
||||
llist_for_each_entry(hnbp, &g_hnbgw->hnb_persistent_list, list) {
|
||||
if (umts_cell_id_equal(&hnbp->id, id))
|
||||
return hnbp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void hnb_persistent_free(struct hnb_persistent *hnbp)
|
||||
{
|
||||
/* FIXME: check if in use? */
|
||||
llist_del(&hnbp->list);
|
||||
talloc_free(hnbp);
|
||||
}
|
||||
|
||||
/* return the amount of time the HNB is up (hnbp->ctx != NULL) or down (hnbp->ctx == NULL) */
|
||||
static unsigned long long hnbp_get_updowntime(const struct hnb_persistent *hnbp)
|
||||
{
|
||||
struct timespec tp;
|
||||
|
||||
if (!hnbp->updowntime)
|
||||
return 0;
|
||||
|
||||
if (osmo_clock_gettime(CLOCK_MONOTONIC, &tp) != 0)
|
||||
return 0;
|
||||
|
||||
return difftime(tp.tv_sec, hnbp->updowntime);
|
||||
}
|
||||
|
||||
unsigned long long hnb_get_updowntime(const struct hnb_context *ctx)
|
||||
{
|
||||
if (!ctx->persistent)
|
||||
return 0;
|
||||
return hnbp_get_updowntime(ctx->persistent);
|
||||
}
|
||||
|
||||
/* timer call-back: Update the HNB_STAT_UPTIME_SECONDS stat item of each hnb_persistent */
|
||||
static void hnbgw_store_hnb_uptime(void *data)
|
||||
{
|
||||
struct hnb_persistent *hnbp;
|
||||
|
||||
llist_for_each_entry(hnbp, &g_hnbgw->hnb_persistent_list, list) {
|
||||
HNBP_STAT_SET(hnbp, HNB_STAT_UPTIME_SECONDS, hnbp->ctx != NULL ? hnbp_get_updowntime(hnbp) : 0);
|
||||
}
|
||||
|
||||
osmo_timer_schedule(&g_hnbgw->store_uptime_timer, STORE_UPTIME_INTERVAL, 0);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* SCTP Socket / stream handling
|
||||
***********************************************************************/
|
||||
|
||||
static int hnb_read_cb(struct osmo_stream_srv *conn)
|
||||
{
|
||||
struct hnb_context *hnb = osmo_stream_srv_get_data(conn);
|
||||
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(conn);
|
||||
struct msgb *msg = msgb_alloc(IUH_MSGB_SIZE, "Iuh rx");
|
||||
int rc;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
OSMO_ASSERT(hnb);
|
||||
/* we store a reference to the HomeNodeB in the msg->dest for the
|
||||
* benefit of various downstream processing functions */
|
||||
msg->dst = hnb;
|
||||
|
||||
rc = osmo_stream_srv_recv(conn, msg);
|
||||
/* Notification received */
|
||||
if (msgb_sctp_msg_flags(msg) & OSMO_STREAM_SCTP_MSG_FLAGS_NOTIFICATION) {
|
||||
union sctp_notification *notif = (union sctp_notification *)msgb_data(msg);
|
||||
rc = 0;
|
||||
switch (notif->sn_header.sn_type) {
|
||||
case SCTP_ASSOC_CHANGE:
|
||||
switch (notif->sn_assoc_change.sac_state) {
|
||||
case SCTP_COMM_LOST:
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE,
|
||||
"sctp_recvmsg(%s) = SCTP_COMM_LOST, closing conn\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
break;
|
||||
case SCTP_RESTART:
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE, "HNB SCTP conn RESTARTed, marking as HNBAP-unregistered\n");
|
||||
hnb->hnb_registered = false;
|
||||
hnb_context_release_ue_state(hnb);
|
||||
/* The tx queue may be quite full after an SCTP RESTART: (SYS#6113)
|
||||
* The link may have been flaky (a possible reason for the peer restarting the conn) and
|
||||
* hence the kernel socket Tx queue may be full (no ACKs coming back) and our own userspace
|
||||
* queue may contain plenty of oldish messages to be sent. Since the HNB will re-register after
|
||||
* this, we simply drop all those old messages: */
|
||||
osmo_stream_srv_clear_tx_queue(conn);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SCTP_SHUTDOWN_EVENT:
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE,
|
||||
"sctp_recvmsg(%s) = SCTP_SHUTDOWN_EVENT, closing conn\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
} else if (rc == -EAGAIN) {
|
||||
/* Older versions of osmo_stream_srv_recv() not supporting
|
||||
* msgb_sctp_msg_flags() may still return -EAGAIN when an sctp
|
||||
* notification is received. */
|
||||
rc = 0;
|
||||
goto out;
|
||||
} else if (rc < 0) {
|
||||
LOGHNB(hnb, DMAIN, LOGL_ERROR, "Error during sctp_recvmsg(%s)\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
goto out;
|
||||
} else if (rc == 0) {
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE, "Connection closed sctp_recvmsg(%s) = 0\n",
|
||||
osmo_sock_get_name2(ofd->fd));
|
||||
osmo_stream_srv_destroy(conn);
|
||||
rc = -EBADF;
|
||||
goto out;
|
||||
} else {
|
||||
msgb_put(msg, rc);
|
||||
}
|
||||
|
||||
switch (msgb_sctp_ppid(msg)) {
|
||||
case IUH_PPI_HNBAP:
|
||||
hnb->hnbap_stream = msgb_sctp_stream(msg);
|
||||
rc = hnbgw_hnbap_rx(hnb, msg);
|
||||
break;
|
||||
case IUH_PPI_RUA:
|
||||
if (!hnb->hnb_registered) {
|
||||
LOGHNB(hnb, DMAIN, LOGL_NOTICE, "Discarding RUA as HNB is not registered\n");
|
||||
goto out;
|
||||
}
|
||||
hnb->rua_stream = msgb_sctp_stream(msg);
|
||||
rc = hnbgw_rua_rx(hnb, msg);
|
||||
break;
|
||||
case IUH_PPI_SABP:
|
||||
case IUH_PPI_RNA:
|
||||
case IUH_PPI_PUA:
|
||||
LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unimplemented SCTP PPID=%lu received\n", msgb_sctp_ppid(msg));
|
||||
rc = 0;
|
||||
break;
|
||||
default:
|
||||
LOGHNB(hnb, DMAIN, LOGL_ERROR, "Unknown SCTP PPID=%lu received\n", msgb_sctp_ppid(msg));
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnb_closed_cb(struct osmo_stream_srv *conn)
|
||||
{
|
||||
struct hnb_context *hnb = osmo_stream_srv_get_data(conn);
|
||||
if (!hnb)
|
||||
return 0; /* hnb_context is being freed, nothing do be done */
|
||||
|
||||
/* hnb: conn became broken, let's release the associated hnb.
|
||||
* conn object is being freed after closed_cb(), so unassign it from hnb
|
||||
* if available to avoid it freeing it again: */
|
||||
hnb->conn = NULL;
|
||||
hnb_context_release(hnb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! call-back when the listen FD has something to read */
|
||||
int hnbgw_rua_accept_cb(struct osmo_stream_srv_link *srv, int fd)
|
||||
{
|
||||
struct hnb_context *ctx;
|
||||
|
||||
LOGP(DMAIN, LOGL_INFO, "New HNB SCTP connection %s\n",
|
||||
osmo_sock_get_name2(fd));
|
||||
|
||||
ctx = hnb_context_alloc(srv, fd);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_RO(hnb_info, "info");
|
||||
static int get_hnb_info(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
struct hnb_context *hnb = data;
|
||||
|
||||
cmd->reply = talloc_strdup(cmd, hnb->identity_info);
|
||||
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
CTRL_CMD_DEFINE_RO(hnbs, "num-hnb");
|
||||
static int get_hnbs(struct ctrl_cmd *cmd, void *data)
|
||||
{
|
||||
cmd->reply = talloc_asprintf(cmd, "%u", llist_count(&g_hnbgw->hnb_list));
|
||||
|
||||
return CTRL_CMD_REPLY;
|
||||
}
|
||||
|
||||
int hnb_ctrl_cmds_install(void)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_hnbs);
|
||||
rc |= ctrl_cmd_install(CTRL_NODE_HNB, &cmd_hnb_info);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int hnb_ctrl_node_lookup(void *data, vector vline, int *node_type, void **node_data, int *i)
|
||||
{
|
||||
const char *token = vector_slot(vline, *i);
|
||||
struct hnb_context *hnb;
|
||||
long num;
|
||||
|
||||
switch (*node_type) {
|
||||
case CTRL_NODE_ROOT:
|
||||
if (strcmp(token, "hnb") != 0)
|
||||
return 0;
|
||||
|
||||
(*i)++;
|
||||
|
||||
if (!ctrl_parse_get_num(vline, *i, &num))
|
||||
return -ERANGE;
|
||||
|
||||
hnb = hnb_context_by_id(num);
|
||||
if (!hnb)
|
||||
return -ENODEV;
|
||||
|
||||
*node_data = hnb;
|
||||
*node_type = CTRL_NODE_HNB;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return g_hnbgw->next_ue_ctx_id++;
|
||||
}
|
||||
|
||||
int hnbgw_mgw_setup(void)
|
||||
@@ -847,6 +186,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 = {
|
||||
@@ -869,6 +213,7 @@ void g_hnbgw_alloc(void *ctx)
|
||||
/* strdup so we can easily talloc_free in the VTY code */
|
||||
g_hnbgw->config.iuh_local_ip = talloc_strdup(g_hnbgw, HNBGW_LOCAL_IP_DEFAULT);
|
||||
g_hnbgw->config.iuh_local_port = IUH_DEFAULT_SCTP_PORT;
|
||||
g_hnbgw->config.hnbap_allow_tmsi = true;
|
||||
g_hnbgw->config.log_prefix_hnb_id = true;
|
||||
g_hnbgw->config.accept_all_hnb = true;
|
||||
|
||||
@@ -877,8 +222,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);
|
||||
@@ -888,35 +235,8 @@ void g_hnbgw_alloc(void *ctx)
|
||||
g_hnbgw->config.pfcp.remote_port = OSMO_PFCP_PORT;
|
||||
#endif
|
||||
|
||||
g_hnbgw->sccp.cnpool_iucs = (struct hnbgw_cnpool){
|
||||
.domain = DOMAIN_CS,
|
||||
.pool_name = "iucs",
|
||||
.peer_name = "msc",
|
||||
.default_remote_pc = DEFAULT_PC_MSC,
|
||||
.vty = {
|
||||
.nri_bitlen = OSMO_NRI_BITLEN_DEFAULT,
|
||||
.null_nri_ranges = osmo_nri_ranges_alloc(g_hnbgw),
|
||||
},
|
||||
.cnlink_ctrg_desc = &msc_ctrg_desc,
|
||||
|
||||
.ctrs = rate_ctr_group_alloc(g_hnbgw, &iucs_ctrg_desc, 0),
|
||||
};
|
||||
INIT_LLIST_HEAD(&g_hnbgw->sccp.cnpool_iucs.cnlinks);
|
||||
|
||||
g_hnbgw->sccp.cnpool_iups = (struct hnbgw_cnpool){
|
||||
.domain = DOMAIN_PS,
|
||||
.pool_name = "iups",
|
||||
.peer_name = "sgsn",
|
||||
.default_remote_pc = DEFAULT_PC_SGSN,
|
||||
.vty = {
|
||||
.nri_bitlen = OSMO_NRI_BITLEN_DEFAULT,
|
||||
.null_nri_ranges = osmo_nri_ranges_alloc(g_hnbgw),
|
||||
},
|
||||
.cnlink_ctrg_desc = &sgsn_ctrg_desc,
|
||||
|
||||
.ctrs = rate_ctr_group_alloc(g_hnbgw, &iups_ctrg_desc, 0),
|
||||
};
|
||||
INIT_LLIST_HEAD(&g_hnbgw->sccp.cnpool_iups.cnlinks);
|
||||
g_hnbgw->sccp.cnpool_iucs = hnbgw_cnpool_alloc(DOMAIN_CS);
|
||||
g_hnbgw->sccp.cnpool_iups = hnbgw_cnpool_alloc(DOMAIN_PS);
|
||||
|
||||
osmo_timer_setup(&g_hnbgw->store_uptime_timer, hnbgw_store_hnb_uptime, g_hnbgw);
|
||||
osmo_timer_schedule(&g_hnbgw->store_uptime_timer, STORE_UPTIME_INTERVAL, 0);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/* hnb-gw specific code for HNBAP */
|
||||
/* hnb-gw specific code for HNBAP, 3GPP TS 25.469 */
|
||||
|
||||
/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <unistd.h>
|
||||
@@ -32,6 +33,8 @@
|
||||
#include <osmocom/hnbap/hnbap_common.h>
|
||||
#include <osmocom/ranap/iu_helpers.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbap/hnbap_ies_defs.h>
|
||||
|
||||
@@ -170,7 +173,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;
|
||||
@@ -181,18 +184,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;
|
||||
}
|
||||
|
||||
@@ -203,13 +208,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;
|
||||
|
||||
@@ -270,6 +280,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);
|
||||
@@ -311,6 +329,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() */
|
||||
@@ -322,15 +343,13 @@ static int hnbgw_tx_ue_register_rej(struct hnb_context *hnb, HNBAP_UE_Identity_t
|
||||
return hnbgw_hnbap_tx(hnb, msg);
|
||||
}
|
||||
|
||||
static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Identity_t *ue_id)
|
||||
static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Identity_t *ue_id, uint32_t context_id)
|
||||
{
|
||||
HNBAP_UERegisterAccept_t accept_out;
|
||||
HNBAP_UERegisterAcceptIEs_t accept;
|
||||
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));
|
||||
@@ -376,11 +395,7 @@ static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Ident
|
||||
tmsi = ntohl(tmsi);
|
||||
LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "HNBAP register with TMSI %x\n", tmsi);
|
||||
|
||||
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, context_id);
|
||||
|
||||
memset(&accept_out, 0, sizeof(accept_out));
|
||||
rc = hnbap_encode_ueregisteraccepties(&accept_out, &accept);
|
||||
@@ -413,11 +428,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;
|
||||
}
|
||||
|
||||
@@ -428,13 +440,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);
|
||||
@@ -453,21 +467,38 @@ static int hnbgw_rx_hnb_deregister(struct hnb_context *ctx, ANY_t *in)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_asn1_octet_string_empty(const OCTET_STRING_t *val)
|
||||
{
|
||||
return !val || !val->buf || !val->size;
|
||||
}
|
||||
|
||||
static bool is_asn1_bit_string_empty(const BIT_STRING_t *val)
|
||||
{
|
||||
return !val || !val->buf || !val->size;
|
||||
}
|
||||
|
||||
static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
{
|
||||
struct hnb_persistent *hnbp;
|
||||
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) {
|
||||
if (rc < 0
|
||||
/* CID#465551: make sure that actual values ended up in the asn1 octet strings: */
|
||||
|| is_asn1_octet_string_empty(&ies.lac)
|
||||
|| is_asn1_octet_string_empty(&ies.sac)
|
||||
|| is_asn1_octet_string_empty(&ies.rac)
|
||||
|| is_asn1_bit_string_empty(&ies.cellIdentity)
|
||||
|| is_asn1_octet_string_empty(&ies.plmNidentity)) {
|
||||
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Failure to decode HNB-REGISTER-REQ: rc=%d\n", rc);
|
||||
cause.present = HNBAP_Cause_PR_protocol;
|
||||
cause.choice.radioNetwork = HNBAP_CauseProtocol_unspecified;
|
||||
@@ -477,14 +508,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)
|
||||
@@ -497,6 +539,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;
|
||||
|
||||
@@ -510,22 +553,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;
|
||||
@@ -552,6 +586,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);
|
||||
@@ -561,10 +597,8 @@ 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;
|
||||
char imsi[16];
|
||||
HNBAP_Cause_t cause = {};
|
||||
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
|
||||
int rc;
|
||||
|
||||
rc = hnbap_decode_ueregisterrequesties(&ies, in);
|
||||
@@ -600,7 +634,7 @@ static int hnbgw_rx_ue_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
case HNBAP_UE_Identity_PR_tMSILAI:
|
||||
case HNBAP_UE_Identity_PR_pTMSIRAI:
|
||||
if (g_hnbgw->config.hnbap_allow_tmsi) {
|
||||
rc = hnbgw_tx_ue_register_acc_tmsi(ctx, &ies.uE_Identity);
|
||||
rc = hnbgw_tx_ue_register_acc_tmsi(ctx, &ies.uE_Identity, get_next_ue_ctx_id());
|
||||
} else {
|
||||
cause.present = HNBAP_Cause_PR_radioNetwork;
|
||||
cause.choice.radioNetwork = HNBAP_CauseRadioNetwork_invalid_UE_identity;
|
||||
@@ -620,18 +654,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;
|
||||
@@ -640,8 +666,7 @@ free_and_return_rc:
|
||||
static int hnbgw_rx_ue_deregister(struct hnb_context *ctx, ANY_t *in)
|
||||
{
|
||||
HNBAP_UEDe_RegisterIEs_t ies;
|
||||
HNBAP_Cause_t cause;
|
||||
struct ue_context *ue;
|
||||
HNBAP_Cause_t cause = {};
|
||||
int rc;
|
||||
uint32_t ctxid;
|
||||
|
||||
@@ -664,9 +689,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);
|
||||
@@ -739,8 +761,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/hnbgw/hnbgw_ranap.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
|
||||
@@ -50,10 +50,8 @@ static const struct tlv_definition gsm48_gmm_att_tlvdef = {
|
||||
},
|
||||
};
|
||||
|
||||
static void decode_gmm_tlv(struct osmo_mobile_identity *mi,
|
||||
struct osmo_routing_area_id *old_ra,
|
||||
int *nri,
|
||||
const uint8_t *tlv_data, size_t tlv_len, bool allow_hex)
|
||||
static void decode_gmm_tlv(struct osmo_mobile_identity *mi, int *nri,
|
||||
const uint8_t *tlv_data, size_t tlv_len, bool allow_hex)
|
||||
{
|
||||
struct tlv_parsed tp;
|
||||
struct tlv_p_entry *e;
|
||||
@@ -66,8 +64,14 @@ static void decode_gmm_tlv(struct osmo_mobile_identity *mi,
|
||||
*nri <<= 2;
|
||||
*nri |= e->val[1] >> 6;
|
||||
}
|
||||
|
||||
/* RAU Req: 9.4.14.5 P-TMSI (Iu mode only): "This IE shall be included by the MS." */
|
||||
e = TLVP_GET(&tp, GSM48_IE_GMM_ALLOC_PTMSI);
|
||||
if (mi && e)
|
||||
osmo_mobile_identity_decode(mi, e->val, e->len, allow_hex);
|
||||
}
|
||||
|
||||
/* Parse 3GPP TS 24.008 § 9.4.1 Attach request */
|
||||
static int mobile_identity_decode_from_gmm_att_req(struct osmo_mobile_identity *mi,
|
||||
struct osmo_routing_area_id *old_ra,
|
||||
int *nri,
|
||||
@@ -82,11 +86,16 @@ static int mobile_identity_decode_from_gmm_att_req(struct osmo_mobile_identity *
|
||||
uint8_t ms_ra_acc_cap_len;
|
||||
int rc;
|
||||
|
||||
if (l3_len < 26)
|
||||
return -ENOSPC;
|
||||
|
||||
/* MS network capability 10.5.5.12 */
|
||||
msnc_len = *cur++;
|
||||
if (l3_len < (msnc_len + (cur - l3_data)))
|
||||
return -ENOSPC;
|
||||
cur += msnc_len;
|
||||
|
||||
/* aTTACH Type 10.5.5.2 */
|
||||
/* aTTACH Type 10.5.5.2 + Ciphering key sequence number 10.5.1.2 */
|
||||
cur++;
|
||||
|
||||
/* DRX parameter 10.5.5.6 */
|
||||
@@ -95,10 +104,9 @@ static int mobile_identity_decode_from_gmm_att_req(struct osmo_mobile_identity *
|
||||
/* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */
|
||||
mi_len = *cur++;
|
||||
mi_data = cur;
|
||||
cur += mi_len;
|
||||
|
||||
if (cur >= end)
|
||||
if (l3_len < (mi_len + (cur - l3_data)))
|
||||
return -ENOSPC;
|
||||
cur += mi_len;
|
||||
|
||||
rc = osmo_mobile_identity_decode(mi, mi_data, mi_len, allow_hex);
|
||||
if (rc)
|
||||
@@ -112,15 +120,19 @@ static int mobile_identity_decode_from_gmm_att_req(struct osmo_mobile_identity *
|
||||
|
||||
/* MS Radio Access Capability 10.5.5.12a */
|
||||
ms_ra_acc_cap_len = *cur++;
|
||||
if (l3_len < (ms_ra_acc_cap_len + (cur - l3_data)))
|
||||
return -ENOSPC;
|
||||
cur += ms_ra_acc_cap_len;
|
||||
|
||||
if (cur > end)
|
||||
return -ENOSPC;
|
||||
if (l3_len == (cur - l3_data))
|
||||
return 0; /* No Optional TLV section */
|
||||
|
||||
decode_gmm_tlv(mi, old_ra, nri, cur, end - cur, allow_hex);
|
||||
/* Mobile identity = NULL: already obtained from Mandatory IE above.*/
|
||||
decode_gmm_tlv(NULL, nri, cur, end - cur, allow_hex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse 24.008 9.4.14 RAU Request */
|
||||
static int mobile_identity_decode_from_gmm_rau_req(struct osmo_mobile_identity *mi,
|
||||
struct osmo_routing_area_id *old_ra,
|
||||
int *nri,
|
||||
@@ -132,12 +144,15 @@ static int mobile_identity_decode_from_gmm_rau_req(struct osmo_mobile_identity *
|
||||
uint8_t ms_ra_acc_cap_len;
|
||||
int rc;
|
||||
|
||||
/* Update Type 10.5.5.18 */
|
||||
cur++;
|
||||
if (cur >= end)
|
||||
/* all mandatory fields + variable length MS Radio Cap (min value) would be 15 bytes.
|
||||
* But even short radio capabilities we should handle with 14 bytes */
|
||||
if (l3_len < 14)
|
||||
return -ENOSPC;
|
||||
|
||||
/* Old routing area identification 10.5.5.15 */
|
||||
/* V: Update Type 10.5.5.18 */
|
||||
cur++;
|
||||
|
||||
/* V: Old routing area identification 10.5.5.15 */
|
||||
rc = osmo_routing_area_id_decode(old_ra, cur, end - cur);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
@@ -145,71 +160,46 @@ static int mobile_identity_decode_from_gmm_rau_req(struct osmo_mobile_identity *
|
||||
if (cur >= end)
|
||||
return -ENOSPC;
|
||||
|
||||
/* MS Radio Access Capability 10.5.5.12a */
|
||||
/* LV: MS Radio Access Capability 10.5.5.12a */
|
||||
ms_ra_acc_cap_len = *cur++;
|
||||
if (l3_len < (ms_ra_acc_cap_len + (cur - l3_data)))
|
||||
return -ENOSPC;
|
||||
cur += ms_ra_acc_cap_len;
|
||||
|
||||
if (cur > end)
|
||||
return -ENOSPC;
|
||||
|
||||
decode_gmm_tlv(mi, old_ra, nri, cur, end - cur, allow_hex);
|
||||
if (l3_len == (cur - l3_data))
|
||||
return 0; /* No Optional TLV section */
|
||||
decode_gmm_tlv(mi, nri, cur, end - cur, allow_hex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int peek_l3_ul_nas(struct hnbgw_context_map *map, const uint8_t *nas_pdu, size_t len,
|
||||
const struct osmo_plmn_id *local_plmn)
|
||||
/* CS MM: Determine mobile identity, from_other_plmn, is_emerg. */
|
||||
static int peek_l3_ul_nas_cs(struct hnbgw_context_map *map, const uint8_t *nas_pdu, size_t len,
|
||||
const struct osmo_plmn_id *local_plmn)
|
||||
{
|
||||
const struct gsm48_hdr *gh;
|
||||
int8_t pdisc;
|
||||
uint8_t mtype;
|
||||
const struct gsm48_hdr *gh = (const struct gsm48_hdr *)nas_pdu;
|
||||
struct osmo_location_area_id old_lai;
|
||||
const struct gsm48_loc_upd_req *lu;
|
||||
struct gsm48_service_request *cm;
|
||||
struct osmo_location_area_id old_lai;
|
||||
struct osmo_routing_area_id old_ra = {};
|
||||
int nri = -1;
|
||||
|
||||
map->l3 = (struct hnbgw_l3_peek){
|
||||
.gmm_nri_container = -1,
|
||||
};
|
||||
osmo_mobile_identity_decode_from_l3_buf(&map->l3.mi, nas_pdu, len, false);
|
||||
|
||||
/* Get the mobile identity from CS MM -- the PS GMM happens further down.
|
||||
* This will return an error for GMM messages, ignore that. */
|
||||
if (!map->is_ps)
|
||||
osmo_mobile_identity_decode_from_l3_buf(&map->l3.mi, nas_pdu, len, false);
|
||||
|
||||
/* Get is_emerg and from_other_plmn */
|
||||
if (len < sizeof(*gh)) {
|
||||
LOGP(DCN, LOGL_ERROR, "Layer 3 message too short for header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gh = (void *)nas_pdu;
|
||||
pdisc = gsm48_hdr_pdisc(gh);
|
||||
mtype = gsm48_hdr_msg_type(gh);
|
||||
|
||||
map->l3.gsm48_pdisc = pdisc;
|
||||
map->l3.gsm48_msg_type = mtype;
|
||||
|
||||
/* Determine from_other_plmn and is_emerg */
|
||||
switch (pdisc) {
|
||||
switch (map->l3.gsm48_pdisc) {
|
||||
case GSM48_PDISC_MM:
|
||||
|
||||
switch (mtype) {
|
||||
/* Get is_emerg and from_other_plmn */
|
||||
switch (map->l3.gsm48_msg_type) {
|
||||
case GSM48_MT_MM_LOC_UPD_REQUEST:
|
||||
if (len < sizeof(*gh) + sizeof(*lu)) {
|
||||
LOGP(DCN, LOGL_ERROR, "LU Req message too short\n");
|
||||
break;
|
||||
}
|
||||
|
||||
lu = (struct gsm48_loc_upd_req *)gh->data;
|
||||
gsm48_decode_lai2(&lu->lai, &old_lai);
|
||||
|
||||
map->l3.from_other_plmn = (osmo_plmn_cmp(&old_lai.plmn, local_plmn) != 0);
|
||||
if (map->l3.from_other_plmn)
|
||||
LOGP(DRUA, LOGL_INFO, "LU from other PLMN: old LAI=%s my PLMN=%s\n",
|
||||
osmo_plmn_name_c(OTC_SELECT, &old_lai.plmn),
|
||||
osmo_plmn_name_c(OTC_SELECT, local_plmn));
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case GSM48_MT_MM_CM_SERV_REQ:
|
||||
if (len < sizeof(*gh) + sizeof(*cm)) {
|
||||
@@ -219,64 +209,88 @@ static int peek_l3_ul_nas(struct hnbgw_context_map *map, const uint8_t *nas_pdu,
|
||||
cm = (struct gsm48_service_request *)&gh->data[0];
|
||||
map->l3.is_emerg = (cm->cm_service_type == GSM48_CMSERV_EMERGENCY);
|
||||
LOGP(DRUA, LOGL_DEBUG, "CM Service is_emerg=%d\n", map->l3.is_emerg);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* PS GMM: Determine mobile identity, gmm_nri_container, from_other_plmn and is_emerg */
|
||||
static int peek_l3_ul_nas_ps(struct hnbgw_context_map *map, const uint8_t *nas_pdu, size_t len,
|
||||
const struct osmo_plmn_id *local_plmn)
|
||||
{
|
||||
struct osmo_routing_area_id old_ra = {};
|
||||
int nri = -1;
|
||||
|
||||
switch (map->l3.gsm48_pdisc) {
|
||||
case GSM48_PDISC_MM_GPRS:
|
||||
switch (mtype) {
|
||||
switch (map->l3.gsm48_msg_type) {
|
||||
case GSM48_MT_GMM_ATTACH_REQ:
|
||||
mobile_identity_decode_from_gmm_att_req(&map->l3.mi, &old_ra, &nri, nas_pdu, len, false);
|
||||
|
||||
LOGP(DRUA, LOGL_DEBUG, "GMM Attach Req mi=%s old_ra=%s nri:%d=0x%x\n",
|
||||
osmo_mobile_identity_to_str_c(OTC_SELECT, &map->l3.mi),
|
||||
osmo_rai_name2_c(OTC_SELECT, &old_ra),
|
||||
nri, nri);
|
||||
|
||||
if (old_ra.lac.plmn.mcc && osmo_plmn_cmp(&old_ra.lac.plmn, local_plmn)) {
|
||||
map->l3.from_other_plmn = true;
|
||||
LOGP(DRUA, LOGL_INFO, "GMM Attach Req from other PLMN: old RAI=%s my PLMN=%s\n",
|
||||
osmo_rai_name2_c(OTC_SELECT, &old_ra),
|
||||
osmo_plmn_name_c(OTC_SELECT, local_plmn));
|
||||
}
|
||||
|
||||
if (nri >= 0)
|
||||
map->l3.gmm_nri_container = nri;
|
||||
|
||||
break;
|
||||
return 0;
|
||||
|
||||
case GSM48_MT_GMM_RA_UPD_REQ:
|
||||
mobile_identity_decode_from_gmm_rau_req(&map->l3.mi, &old_ra, &nri, nas_pdu, len, false);
|
||||
|
||||
LOGP(DRUA, LOGL_DEBUG, "GMM Routing Area Upd Req mi=%s old_ra=%s nri:%d=0x%x\n",
|
||||
osmo_mobile_identity_to_str_c(OTC_SELECT, &map->l3.mi),
|
||||
osmo_rai_name2_c(OTC_SELECT, &old_ra),
|
||||
nri, nri);
|
||||
|
||||
if (old_ra.lac.plmn.mcc && osmo_plmn_cmp(&old_ra.lac.plmn, local_plmn)) {
|
||||
map->l3.from_other_plmn = true;
|
||||
LOGP(DRUA, LOGL_INFO, "GMM Routing Area Upd Req from other PLMN: old RAI=%s my PLMN=%s\n",
|
||||
osmo_rai_name2_c(OTC_SELECT, &old_ra),
|
||||
osmo_plmn_name_c(OTC_SELECT, local_plmn));
|
||||
}
|
||||
|
||||
if (nri >= 0)
|
||||
map->l3.gmm_nri_container = nri;
|
||||
|
||||
break;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int peek_l3_ul_nas(struct hnbgw_context_map *map, const uint8_t *nas_pdu, size_t len,
|
||||
const struct osmo_plmn_id *local_plmn)
|
||||
{
|
||||
const struct gsm48_hdr *gh = (const struct gsm48_hdr *)nas_pdu;
|
||||
|
||||
map->l3 = (struct hnbgw_l3_peek){
|
||||
.gmm_nri_container = -1,
|
||||
.mi = {
|
||||
.type = GSM_MI_TYPE_NONE,
|
||||
.tmsi = GSM_RESERVED_TMSI,
|
||||
},
|
||||
};
|
||||
|
||||
if (len < sizeof(*gh)) {
|
||||
LOGP(DCN, LOGL_ERROR, "Layer 3 message too short for header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
map->l3.gsm48_pdisc = gsm48_hdr_pdisc(gh);
|
||||
map->l3.gsm48_msg_type = gsm48_hdr_msg_type(gh);
|
||||
|
||||
if (map->is_ps)
|
||||
return peek_l3_ul_nas_ps(map, nas_pdu, len, local_plmn);
|
||||
return peek_l3_ul_nas_cs(map, nas_pdu, len, local_plmn);
|
||||
}
|
||||
|
||||
static int peek_l3_ul_initial_ue(struct hnbgw_context_map *map, const RANAP_InitialUE_MessageIEs_t *ies)
|
||||
{
|
||||
struct osmo_plmn_id local_plmn;
|
||||
@@ -300,7 +314,7 @@ static int peek_l3_ul_initial_ue(struct hnbgw_context_map *map, const RANAP_Init
|
||||
* This is relevant for CN pooling, to decide which CN link to map the RUA context to. */
|
||||
int hnbgw_peek_l3_ul(struct hnbgw_context_map *map, struct msgb *ranap_msg)
|
||||
{
|
||||
ranap_message *message = hnbgw_decode_ranap_co(ranap_msg);
|
||||
ranap_message *message = hnbgw_decode_ranap_cn_co(ranap_msg);
|
||||
if (!message) {
|
||||
LOGP(DCN, LOGL_ERROR, "Failed to decode RANAP PDU\n");
|
||||
return -EINVAL;
|
||||
|
||||
@@ -20,12 +20,27 @@
|
||||
*/
|
||||
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/core/stats.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
#include <osmocom/pfcp/pfcp_endpoint.h>
|
||||
#include <osmocom/pfcp/pfcp_cp_peer.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/ps_rab_fsm.h>
|
||||
#include <osmocom/hnbgw/hnbgw_pfcp.h>
|
||||
|
||||
static const struct osmo_stat_item_desc hnbgw_upf_stat_item_description[] = {
|
||||
[HNBGW_UPF_STAT_ASSOCIATED] = { "pfcp_associated", "Associated to UPF through PFCP", OSMO_STAT_ITEM_NO_UNIT, 16, 0},
|
||||
};
|
||||
|
||||
static const struct osmo_stat_item_group_desc hnbgw_upf_statg_desc = {
|
||||
"upf",
|
||||
"UPF Peer Statistics",
|
||||
OSMO_STATS_CLASS_PEER,
|
||||
ARRAY_SIZE(hnbgw_upf_stat_item_description),
|
||||
hnbgw_upf_stat_item_description,
|
||||
};
|
||||
|
||||
static void pfcp_set_msg_ctx(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m, struct osmo_pfcp_msg *req)
|
||||
{
|
||||
@@ -62,6 +77,12 @@ static void pfcp_rx_msg(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m,
|
||||
}
|
||||
}
|
||||
|
||||
static void pfcp_cp_peer_assoc_cb(struct osmo_pfcp_cp_peer *cp_peer, bool associated)
|
||||
{
|
||||
LOGP(DLPFCP, LOGL_NOTICE, "PFCP Peer associated: %s\n", associated ? "true" : "false");
|
||||
HNBGW_UPF_STAT_SET(HNBGW_UPF_STAT_ASSOCIATED, associated ? 1 : 0);
|
||||
}
|
||||
|
||||
int hnbgw_pfcp_init(void)
|
||||
{
|
||||
struct osmo_pfcp_endpoint_cfg cfg;
|
||||
@@ -81,6 +102,12 @@ int hnbgw_pfcp_init(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_hnbgw->pfcp.statg = osmo_stat_item_group_alloc(g_hnbgw, &hnbgw_upf_statg_desc, 0);
|
||||
if (!g_hnbgw->pfcp.statg) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Failed creating UPF stats item group\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfg = (struct osmo_pfcp_endpoint_cfg){
|
||||
.set_msg_ctx_cb = pfcp_set_msg_ctx,
|
||||
.rx_msg_cb = pfcp_rx_msg,
|
||||
@@ -136,6 +163,11 @@ int hnbgw_pfcp_init(void)
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Cannot allocate PFCP CP Peer FSM\n");
|
||||
return -1;
|
||||
}
|
||||
if (osmo_pfcp_cp_peer_set_associated_cb(g_hnbgw->pfcp.cp_peer, pfcp_cp_peer_assoc_cb)) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Cannot Set PFCP CP Peer associated callback\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (osmo_pfcp_cp_peer_associate(g_hnbgw->pfcp.cp_peer)) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Cannot start PFCP CP Peer FSM\n");
|
||||
return -1;
|
||||
@@ -143,3 +175,10 @@ int hnbgw_pfcp_init(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hnbgw_pfcp_release(void)
|
||||
{
|
||||
if (!hnb_gw_is_gtp_mapping_enabled())
|
||||
return;
|
||||
osmo_stat_item_group_free(g_hnbgw->pfcp.statg);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* hnb-gw specific code for RANAP */
|
||||
/* hnb-gw specific code for RANAP, 3GPP TS 25.413 */
|
||||
|
||||
/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
@@ -18,10 +18,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include "config.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
@@ -29,12 +26,33 @@
|
||||
|
||||
#include "asn1helpers.h"
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
#include <osmocom/ranap/ranap_common_cn.h>
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/ranap/ranap_msg_factory.h>
|
||||
|
||||
#if ENABLE_PFCP
|
||||
#include <osmocom/pfcp/pfcp_cp_peer.h>
|
||||
#endif
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
#include <osmocom/hnbgw/ps_rab_ass_fsm.h>
|
||||
#include <osmocom/hnbgw/kpi.h>
|
||||
|
||||
/*****************************************************************************
|
||||
* Processing of RANAP from the endpoint towards RAN (hNodeB), acting as CN
|
||||
*****************************************************************************/
|
||||
|
||||
static int ranap_tx_udt_dl_reset_ack(struct hnb_context *hnb, RANAP_CN_DomainIndicator_t domain)
|
||||
{
|
||||
struct msgb *msg;
|
||||
@@ -209,6 +227,455 @@ int hnbgw_ranap_rx_udt_ul(struct msgb *msg, uint8_t *data, size_t len)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int destruct_ranap_cn_rx_co_ies(ranap_message *ranap_message_p)
|
||||
{
|
||||
ranap_cn_rx_co_free(ranap_message_p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decode UL RANAP message with convenient memory freeing: just talloc_free() the returned pointer..
|
||||
* Allocate a ranap_message from OTC_SELECT, decode RANAP msgb into it, attach a talloc destructor that calls
|
||||
* ranap_cn_rx_co_free() upon talloc_free(), and return the decoded ranap_message. */
|
||||
ranap_message *hnbgw_decode_ranap_cn_co(struct msgb *ranap_msg)
|
||||
{
|
||||
int rc;
|
||||
ranap_message *message;
|
||||
|
||||
if (!msg_has_l2_data(ranap_msg))
|
||||
return NULL;
|
||||
message = talloc_zero(OTC_SELECT, ranap_message);
|
||||
rc = ranap_cn_rx_co_decode2(message, msgb_l2(ranap_msg), msgb_l2len(ranap_msg));
|
||||
if (rc != 0) {
|
||||
talloc_free(message);
|
||||
return NULL;
|
||||
}
|
||||
talloc_set_destructor(message, destruct_ranap_cn_rx_co_ies);
|
||||
return message;
|
||||
}
|
||||
|
||||
/* Process a received RANAP PDU through SCCP DATA.ind coming from CN (MSC/SGSN)
|
||||
* ranap_msg is owned by OTC_SELECT. */
|
||||
int hnbgw_ranap_rx_data_ul(struct hnbgw_context_map *map, struct msgb *ranap_msg)
|
||||
{
|
||||
OSMO_ASSERT(map);
|
||||
OSMO_ASSERT(msg_has_l2_data(ranap_msg));
|
||||
|
||||
ranap_message *message = hnbgw_decode_ranap_cn_co(ranap_msg);
|
||||
if (message) {
|
||||
LOG_MAP(map, DHNB, LOGL_DEBUG, "rx from RUA: RANAP %s\n",
|
||||
get_value_string(ranap_procedure_code_vals, message->procedureCode));
|
||||
|
||||
kpi_ranap_process_ul(map, message);
|
||||
|
||||
if (!map->is_ps) {
|
||||
/* See if it is a RAB Assignment Response message from RUA to SCCP, where we need to change the user plane
|
||||
* information, for RTP mapping via MGW, or GTP mapping via UPF. */
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* mgw_fsm_handle_rab_ass_resp() may take ownership of "ranap_msg" (prim->oph) and "message" */
|
||||
return mgw_fsm_handle_cs_rab_ass_resp(map, ranap_msg, message);
|
||||
}
|
||||
} else {
|
||||
#if ENABLE_PFCP
|
||||
if (hnb_gw_is_gtp_mapping_enabled()) {
|
||||
/* map->is_ps == true and PFCP is enabled in osmo-hnbgw.cfg */
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* ps_rab_ass_fsm() may take ownership of "ranap_msg" (prim->oph) and "message" */
|
||||
return hnbgw_gtpmap_rx_rab_ass_resp(map, ranap_msg, message);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* It was not a RAB Assignment Response that needed to be intercepted. Forward as-is to SCCP. */
|
||||
return map_sccp_dispatch(map, MAP_SCCP_EV_TX_DATA_REQUEST, ranap_msg);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Processing of RANAP from the endpoint towards CN (MSC/SGSN), acting as RAN
|
||||
*****************************************************************************/
|
||||
|
||||
static int cn_ranap_rx_reset_cmd(struct hnbgw_cnlink *cnlink,
|
||||
const struct osmo_scu_unitdata_param *unitdata,
|
||||
RANAP_InitiatingMessage_t *imsg)
|
||||
{
|
||||
RANAP_CN_DomainIndicator_t domain;
|
||||
RANAP_ResetIEs_t ies;
|
||||
int rc;
|
||||
|
||||
rc = ranap_decode_reseties(&ies, &imsg->value);
|
||||
domain = ies.cN_DomainIndicator;
|
||||
ranap_free_reseties(&ies);
|
||||
|
||||
if (rc) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Rx RESET: cannot decode IEs\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cnlink->pool->domain != domain) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Rx RESET indicates domain %s, but this is %s on domain %s\n",
|
||||
ranap_domain_name(domain), cnlink->name, ranap_domain_name(cnlink->pool->domain));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cnlink_rx_reset_cmd(cnlink);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct value_string ranap_paging_area_id_names[] = {
|
||||
{ RANAP_PagingAreaID_PR_NOTHING, "NOTHING" },
|
||||
{ RANAP_PagingAreaID_PR_lAI, "LAI" },
|
||||
{ RANAP_PagingAreaID_PR_rAI, "RAI" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static bool hnb_paging_area_id_match(const struct hnb_context *hnb,
|
||||
enum RANAP_PagingAreaID_PR t,
|
||||
const struct osmo_routing_area_id *rai)
|
||||
{
|
||||
switch (t) {
|
||||
case RANAP_PagingAreaID_PR_NOTHING:
|
||||
return true;
|
||||
case RANAP_PagingAreaID_PR_rAI:
|
||||
if (hnb->id.rac != rai->rac)
|
||||
return false;
|
||||
/* fall through */
|
||||
case RANAP_PagingAreaID_PR_lAI:
|
||||
if (hnb->id.lac != rai->lac.lac)
|
||||
return false;
|
||||
if (osmo_plmn_cmp(&hnb->id.plmn, &rai->lac.plmn))
|
||||
return false;
|
||||
/* fall through */
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int lai_from_RANAP_RANAP_LAI(struct osmo_location_area_id *lai, const RANAP_LAI_t *ranap_lai)
|
||||
{
|
||||
if (ranap_lai->pLMNidentity.size < 3)
|
||||
return -EINVAL;
|
||||
osmo_plmn_from_bcd(ranap_lai->pLMNidentity.buf, &lai->plmn);
|
||||
lai->lac = asn1str_to_u16(&ranap_lai->lAC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rai_from_RANAP_PagingAreaID(struct osmo_routing_area_id *rai, const RANAP_PagingAreaID_t *paid)
|
||||
{
|
||||
switch (paid->present) {
|
||||
case RANAP_PagingAreaID_PR_NOTHING:
|
||||
break;
|
||||
case RANAP_PagingAreaID_PR_lAI:
|
||||
return lai_from_RANAP_RANAP_LAI(&rai->lac, &paid->choice.lAI);
|
||||
case RANAP_PagingAreaID_PR_rAI:
|
||||
rai->rac = asn1str_to_u8(&paid->choice.rAI.rAC);
|
||||
return lai_from_RANAP_RANAP_LAI(&rai->lac, &paid->choice.rAI.lAI);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 3GPP TS 25.413 8.15 */
|
||||
static int cn_ranap_rx_paging_cmd(struct hnbgw_cnlink *cnlink,
|
||||
RANAP_InitiatingMessage_t *imsg,
|
||||
const uint8_t *data, unsigned int len)
|
||||
{
|
||||
RANAP_PagingIEs_t ies;
|
||||
RANAP_CN_DomainIndicator_t domain;
|
||||
const char *errmsg;
|
||||
struct hnb_context *hnb;
|
||||
bool is_ps = cnlink->pool->domain == DOMAIN_PS;
|
||||
bool forwarded = false;
|
||||
bool page_area_present;
|
||||
struct osmo_routing_area_id page_rai = {};
|
||||
|
||||
if (ranap_decode_pagingies(&ies, &imsg->value) < 0) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR,
|
||||
"Rx Paging from CN: decoding RANAP IEs failed\n");
|
||||
return -1;
|
||||
}
|
||||
domain = ies.cN_DomainIndicator;
|
||||
page_area_present = (ies.presenceMask & PAGINGIES_RANAP_PAGINGAREAID_PRESENT);
|
||||
|
||||
if (cnlink->pool->domain != domain) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR,
|
||||
"Rx Paging from CN: message indicates domain %s, but cnlink is on domain %s\n",
|
||||
ranap_domain_name(domain),
|
||||
ranap_domain_name(cnlink->pool->domain));
|
||||
goto free_ies_ret;
|
||||
}
|
||||
|
||||
if (page_area_present) {
|
||||
if (rai_from_RANAP_PagingAreaID(&page_rai, &ies.pagingAreaID) < 0) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR,
|
||||
"Rx Paging from CN: decoding RANAP IE Paging Area ID failed, broadcasting to all HNBs\n");
|
||||
/* fail over to broadcast... */
|
||||
page_area_present = false;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_DEBUG,
|
||||
"Rx Paging from CN: %s PagingAreaID: %s %s\n",
|
||||
ranap_domain_name(domain),
|
||||
page_area_present ?
|
||||
get_value_string(ranap_paging_area_id_names, ies.pagingAreaID.present) :
|
||||
"NOT_PRESENT",
|
||||
osmo_rai_name2(&page_rai)
|
||||
);
|
||||
|
||||
llist_for_each_entry(hnb, &g_hnbgw->hnb_list, list) {
|
||||
if (!hnb->hnb_registered)
|
||||
continue;
|
||||
if (page_area_present &&
|
||||
!hnb_paging_area_id_match(hnb, ies.pagingAreaID.present, &page_rai))
|
||||
continue;
|
||||
|
||||
if (is_ps)
|
||||
HNBP_CTR_INC(hnb->persistent, HNB_CTR_PS_PAGING_ATTEMPTED);
|
||||
else
|
||||
HNBP_CTR_INC(hnb->persistent, HNB_CTR_CS_PAGING_ATTEMPTED);
|
||||
if (rua_tx_udt(hnb, data, len) == 0)
|
||||
forwarded = true;
|
||||
}
|
||||
|
||||
if (forwarded) {
|
||||
/* If Paging command was forwarded anywhere, store a record for it, to match paging response: */
|
||||
errmsg = cnlink_paging_add_ranap(cnlink, &ies);
|
||||
if (errmsg) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR,
|
||||
"Rx Paging from CN: %s. Skip storing paging record."
|
||||
" Later on, the Paging Response may be forwarded to the wrong CN peer.\n",
|
||||
errmsg);
|
||||
goto free_ies_ret;
|
||||
}
|
||||
}
|
||||
ranap_free_pagingies(&ies);
|
||||
return 0;
|
||||
|
||||
free_ies_ret:
|
||||
ranap_free_pagingies(&ies);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ranap_rx_udt_dl_initiating_msg(struct hnbgw_cnlink *cnlink,
|
||||
const struct osmo_scu_unitdata_param *unitdata,
|
||||
RANAP_InitiatingMessage_t *imsg,
|
||||
const uint8_t *data, unsigned int len)
|
||||
{
|
||||
switch (imsg->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_Reset:
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_RESET);
|
||||
return cn_ranap_rx_reset_cmd(cnlink, unitdata, imsg);
|
||||
case RANAP_ProcedureCode_id_Paging:
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_PAGING);
|
||||
return cn_ranap_rx_paging_cmd(cnlink, imsg, data, len);
|
||||
case RANAP_ProcedureCode_id_OverloadControl: /* Overload ind */
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_OVERLOAD_IND);
|
||||
break;
|
||||
case RANAP_ProcedureCode_id_ErrorIndication: /* Error ind */
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_ERROR_IND);
|
||||
break;
|
||||
case RANAP_ProcedureCode_id_ResetResource: /* request */
|
||||
case RANAP_ProcedureCode_id_InformationTransfer:
|
||||
case RANAP_ProcedureCode_id_DirectInformationTransfer:
|
||||
case RANAP_ProcedureCode_id_UplinkInformationExchange:
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_UNSUPPORTED);
|
||||
LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
|
||||
"Procedure %ld from CN, ignoring\n", imsg->procedureCode);
|
||||
break;
|
||||
default:
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_UNKNOWN);
|
||||
LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
|
||||
"Procedure %ld from CN, ignoring\n", imsg->procedureCode);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cn_ranap_rx_reset_ack(struct hnbgw_cnlink *cnlink,
|
||||
RANAP_SuccessfulOutcome_t *omsg)
|
||||
{
|
||||
RANAP_CN_DomainIndicator_t domain;
|
||||
RANAP_ResetAcknowledgeIEs_t ies;
|
||||
int rc;
|
||||
|
||||
rc = ranap_decode_resetacknowledgeies(&ies, &omsg->value);
|
||||
domain = ies.cN_DomainIndicator;
|
||||
ranap_free_resetacknowledgeies(&ies);
|
||||
|
||||
if (rc) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Rx RESET ACK: cannot decode IEs\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cnlink->pool->domain != domain) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_ERROR, "Rx RESET ACK indicates domain %s, but this is %s on domain %s\n",
|
||||
ranap_domain_name(domain), cnlink->name, ranap_domain_name(cnlink->pool->domain));
|
||||
return -1;
|
||||
}
|
||||
|
||||
cnlink_rx_reset_ack(cnlink);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ranap_rx_udt_dl_successful_msg(struct hnbgw_cnlink *cnlink,
|
||||
RANAP_SuccessfulOutcome_t *omsg)
|
||||
{
|
||||
switch (omsg->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_Reset: /* Reset acknowledge */
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_RESET);
|
||||
return cn_ranap_rx_reset_ack(cnlink, omsg);
|
||||
case RANAP_ProcedureCode_id_ResetResource: /* response */
|
||||
case RANAP_ProcedureCode_id_InformationTransfer:
|
||||
case RANAP_ProcedureCode_id_DirectInformationTransfer:
|
||||
case RANAP_ProcedureCode_id_UplinkInformationExchange:
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_UNSUPPORTED);
|
||||
LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
|
||||
"Procedure %ld from CN, ignoring\n", omsg->procedureCode);
|
||||
break;
|
||||
default:
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_RANAP_RX_UDT_UNKNOWN);
|
||||
LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
|
||||
"Procedure %ld from CN, ignoring\n", omsg->procedureCode);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _hnbgw_ranap_rx_udt_dl(struct hnbgw_cnlink *cnlink,
|
||||
const struct osmo_scu_unitdata_param *unitdata,
|
||||
RANAP_RANAP_PDU_t *pdu, const uint8_t *data, unsigned int len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (pdu->present) {
|
||||
case RANAP_RANAP_PDU_PR_initiatingMessage:
|
||||
rc = ranap_rx_udt_dl_initiating_msg(cnlink, unitdata, &pdu->choice.initiatingMessage, data, len);
|
||||
break;
|
||||
case RANAP_RANAP_PDU_PR_successfulOutcome:
|
||||
rc = ranap_rx_udt_dl_successful_msg(cnlink, &pdu->choice.successfulOutcome);
|
||||
break;
|
||||
case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
|
||||
LOGP(DRANAP, LOGL_NOTICE, "Received unsupported RANAP "
|
||||
"unsuccessful outcome procedure %ld from CN, ignoring\n",
|
||||
pdu->choice.unsuccessfulOutcome.procedureCode);
|
||||
rc = -ENOTSUP;
|
||||
break;
|
||||
default:
|
||||
LOGP(DRANAP, LOGL_NOTICE, "Received suspicious RANAP "
|
||||
"presence %u from CN, ignoring\n", pdu->present);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int hnbgw_ranap_rx_udt_dl(struct hnbgw_cnlink *cnlink, const struct osmo_scu_unitdata_param *unitdata,
|
||||
const uint8_t *data, unsigned int len)
|
||||
{
|
||||
RANAP_RANAP_PDU_t _pdu, *pdu = &_pdu;
|
||||
asn_dec_rval_t dec_ret;
|
||||
int rc;
|
||||
|
||||
memset(pdu, 0, sizeof(*pdu));
|
||||
dec_ret = aper_decode(NULL, &asn_DEF_RANAP_RANAP_PDU, (void **) &pdu,
|
||||
data, len, 0, 0);
|
||||
if (dec_ret.code != RC_OK) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Error in RANAP ASN.1 decode\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = _hnbgw_ranap_rx_udt_dl(cnlink, unitdata, pdu, data, len);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RANAP_PDU, pdu);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int destruct_ranap_ran_rx_co_ies(ranap_message *ranap_message_p)
|
||||
{
|
||||
ranap_ran_rx_co_free(ranap_message_p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Decode DL RANAP message with convenient memory freeing: just talloc_free() the returned pointer..
|
||||
* Allocate a ranap_message from OTC_SELECT, decode RANAP msgb into it, attach a talloc destructor that calls
|
||||
* ranap_cn_rx_co_free() upon talloc_free(), and return the decoded ranap_message. */
|
||||
static ranap_message *hnbgw_decode_ranap_ran_co(struct msgb *ranap_msg)
|
||||
{
|
||||
int rc;
|
||||
ranap_message *message;
|
||||
|
||||
if (!msg_has_l2_data(ranap_msg))
|
||||
return NULL;
|
||||
message = talloc_zero(OTC_SELECT, ranap_message);
|
||||
rc = ranap_ran_rx_co_decode(NULL, message, msgb_l2(ranap_msg), msgb_l2len(ranap_msg));
|
||||
if (rc != 0) {
|
||||
talloc_free(message);
|
||||
return NULL;
|
||||
}
|
||||
talloc_set_destructor(message, destruct_ranap_ran_rx_co_ies);
|
||||
return message;
|
||||
}
|
||||
|
||||
/* Process a received RANAP PDU through SCCP DATA.ind coming from CN (MSC/SGSN)
|
||||
* ranap_msg is owned by OTC_SELECT. */
|
||||
int hnbgw_ranap_rx_data_dl(struct hnbgw_context_map *map, struct msgb *ranap_msg)
|
||||
{
|
||||
OSMO_ASSERT(map);
|
||||
OSMO_ASSERT(msg_has_l2_data(ranap_msg));
|
||||
|
||||
/* See if it is a RAB Assignment Request message from SCCP to RUA, where we need to change the user plane
|
||||
* information, for RTP mapping via MGW, or GTP mapping via UPF. */
|
||||
ranap_message *message = hnbgw_decode_ranap_ran_co(ranap_msg);
|
||||
if (message) {
|
||||
LOG_MAP(map, DCN, LOGL_DEBUG, "rx from SCCP: RANAP %s\n",
|
||||
get_value_string(ranap_procedure_code_vals, message->procedureCode));
|
||||
|
||||
kpi_ranap_process_dl(map, message);
|
||||
|
||||
if (!map->is_ps) {
|
||||
/* Circuit-Switched. Set up mapping of RTP ports via MGW */
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* mgw_fsm_alloc_and_handle_rab_ass_req() takes ownership of (ranap) message */
|
||||
return handle_cs_rab_ass_req(map, ranap_msg, message);
|
||||
case RANAP_ProcedureCode_id_Iu_Release:
|
||||
/* Any IU Release will terminate the MGW FSM, the message itsself is not passed to the
|
||||
* FSM code. It is just forwarded normally by map_rua_tx_dt() below. */
|
||||
mgw_fsm_release(map);
|
||||
break;
|
||||
}
|
||||
#if ENABLE_PFCP
|
||||
} else {
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* If a UPF is configured, handle the RAB Assignment via ps_rab_ass_fsm, and replace the
|
||||
* GTP F-TEIDs in the RAB Assignment message before passing it on to RUA. */
|
||||
if (hnb_gw_is_gtp_mapping_enabled()) {
|
||||
LOG_MAP(map, DCN, LOGL_DEBUG,
|
||||
"RAB Assignment: setting up GTP tunnel mapping via UPF %s\n",
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, osmo_pfcp_cp_peer_get_remote_addr(g_hnbgw->pfcp.cp_peer)));
|
||||
return hnbgw_gtpmap_rx_rab_ass_req(map, ranap_msg, message);
|
||||
}
|
||||
/* If no UPF is configured, directly forward the message as-is (no GTP mapping). */
|
||||
LOG_MAP(map, DCN, LOGL_DEBUG, "RAB Assignment: no UPF configured, forwarding as-is\n");
|
||||
break;
|
||||
|
||||
case RANAP_ProcedureCode_id_Iu_Release:
|
||||
/* Any IU Release will terminate the MGW FSM, the message itsself is not passed to the
|
||||
* FSM code. It is just forwarded normally by map_rua_tx_dt() below. */
|
||||
hnbgw_gtpmap_release(map);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* It was not a RAB Assignment Request that needed to be intercepted. Forward as-is to RUA. */
|
||||
return map_rua_dispatch(map, MAP_RUA_EV_TX_DIRECT_TRANSFER, ranap_msg);
|
||||
}
|
||||
|
||||
int hnbgw_ranap_init(void)
|
||||
{
|
||||
return 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* hnb-gw specific code for RUA (Ranap User Adaption) */
|
||||
/* hnb-gw specific code for RUA (Ranap User Adaption), 3GPP TS 25.468 */
|
||||
|
||||
/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
@@ -33,6 +33,8 @@
|
||||
|
||||
#include "asn1helpers.h"
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/hnbgw_ranap.h>
|
||||
#include <osmocom/rua/rua_common.h>
|
||||
@@ -40,7 +42,6 @@
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/hnbap/HNBAP_CN-DomainIndicator.h>
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
|
||||
static int hnbgw_rua_tx(struct hnb_context *ctx, struct msgb *msg)
|
||||
{
|
||||
@@ -166,6 +167,20 @@ int rua_tx_disc(struct hnb_context *hnb, int is_ps, uint32_t context_id,
|
||||
return hnbgw_rua_tx(hnb, msg);
|
||||
}
|
||||
|
||||
/* Send Disconnect to RUA without RANAP data */
|
||||
static void rua_tx_disc_conn_fail(struct hnb_context *hnb, bool is_ps, uint32_t context_id)
|
||||
{
|
||||
RUA_Cause_t rua_cause = {
|
||||
.present = RUA_Cause_PR_radioNetwork,
|
||||
.choice.radioNetwork = RUA_CauseRadioNetwork_connect_failed,
|
||||
};
|
||||
|
||||
LOG_HNBP(hnb->persistent, LOGL_INFO, "Tx RUA Disconnect\n");
|
||||
|
||||
if (rua_tx_disc(hnb, is_ps, context_id, &rua_cause, NULL, 0))
|
||||
LOG_HNBP(hnb->persistent, LOGL_ERROR, "Failed to send Disconnect to RUA\n");
|
||||
}
|
||||
|
||||
static struct value_string rua_procedure_code_names[] = {
|
||||
{ RUA_ProcedureCode_id_Connect, "Connect" },
|
||||
{ RUA_ProcedureCode_id_DirectTransfer, "DirectTransfer" },
|
||||
@@ -228,7 +243,6 @@ static int rua_to_scu(struct hnb_context *hnb,
|
||||
struct msgb *ranap_msg = NULL;
|
||||
struct hnbgw_context_map *map = NULL;
|
||||
bool is_ps;
|
||||
int logl;
|
||||
|
||||
switch (cN_DomainIndicator) {
|
||||
case RUA_CN_DomainIndicator_cs_domain:
|
||||
@@ -253,14 +267,13 @@ static int rua_to_scu(struct hnb_context *hnb,
|
||||
}
|
||||
|
||||
map = context_map_find_by_rua_ctx_id(hnb, context_id, is_ps);
|
||||
logl = LOGL_ERROR;
|
||||
|
||||
switch (rua_procedure) {
|
||||
case RUA_ProcedureCode_id_Connect:
|
||||
/* A Connect message can only be the first message for an unused RUA context */
|
||||
if (map) {
|
||||
/* Already established this RUA context. But then how can it be a Connect message. */
|
||||
LOGHNB(hnb, DRUA, LOGL_ERROR, "rx RUA %s for already active RUA context %u\n",
|
||||
LOGHNB(hnb, DRUA, LOGL_NOTICE, "rx RUA %s for already active RUA context %u\n",
|
||||
rua_procedure_code_name(rua_procedure), context_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -270,6 +283,7 @@ static int rua_to_scu(struct hnb_context *hnb,
|
||||
LOGHNB(hnb, DRUA, LOGL_ERROR,
|
||||
"Failed to create context map for %s: rx RUA %s with %u bytes RANAP data\n",
|
||||
is_ps ? "IuPS" : "IuCS", rua_procedure_code_name(rua_procedure), data ? len : 0);
|
||||
rua_tx_disc_conn_fail(hnb, is_ps, context_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
@@ -278,13 +292,19 @@ static int rua_to_scu(struct hnb_context *hnb,
|
||||
/* For RUA Disconnect, do not spam the ERROR log. It is just a stray Disconnect, no harm done.
|
||||
* Context: some CN are known to rapidly tear down SCCP without waiting for RUA to disconnect gracefully
|
||||
* (IU Release Complete). Such CN would cause ERROR logging for each and every released context map. */
|
||||
logl = LOGL_DEBUG;
|
||||
/* fall thru */
|
||||
if (!map) {
|
||||
LOGHNB(hnb, DRUA, LOGL_DEBUG, "rx RUA %s for unknown RUA context %u\n",
|
||||
rua_procedure_code_name(rua_procedure), context_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Any message other than Connect must have a valid RUA context */
|
||||
if (!map) {
|
||||
LOGHNB(hnb, DRUA, logl, "rx RUA %s for unknown RUA context %u\n",
|
||||
LOGHNB(hnb, DRUA, LOGL_NOTICE, "rx RUA %s for unknown RUA context %u\n",
|
||||
rua_procedure_code_name(rua_procedure), context_id);
|
||||
rua_tx_disc_conn_fail(hnb, is_ps, context_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
566
src/osmo-hnbgw/hnbgw_sccp.c
Normal file
566
src/osmo-hnbgw/hnbgw_sccp.c
Normal file
@@ -0,0 +1,566 @@
|
||||
/* hnb-gw specific code for SCCP, ITU Q.711 - Q.714 */
|
||||
|
||||
/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2025 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
#include <osmocom/sigtran/protocol/sua.h>
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/hnbgw_ranap.h>
|
||||
|
||||
/***********************************************************************
|
||||
* Incoming primitives from SCCP User SAP
|
||||
***********************************************************************/
|
||||
|
||||
static bool cnlink_matches(const struct hnbgw_cnlink *cnlink, const struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *remote_addr)
|
||||
{
|
||||
if (cnlink->hnbgw_sccp_user != hsu)
|
||||
return false;
|
||||
if (osmo_sccp_addr_cmp(&cnlink->remote_addr, remote_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct hnbgw_cnlink *hnbgw_cnlink_find_by_addr(const struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_sccp_addr *remote_addr)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iucs->cnlinks, entry) {
|
||||
if (cnlink_matches(cnlink, hsu, remote_addr))
|
||||
return cnlink;
|
||||
}
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iups->cnlinks, entry) {
|
||||
if (cnlink_matches(cnlink, hsu, remote_addr))
|
||||
return cnlink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct hnbgw_cnlink *cnlink_from_addr(struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *calling_addr,
|
||||
const struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = NULL;
|
||||
cnlink = hnbgw_cnlink_find_by_addr(hsu, calling_addr);
|
||||
if (!cnlink) {
|
||||
LOG_HSU(hsu, DRANAP, LOGL_ERROR, "Rx from unknown SCCP peer: %s: %s\n",
|
||||
osmo_sccp_inst_addr_name(osmo_ss7_get_sccp(hsu->ss7), calling_addr),
|
||||
osmo_scu_prim_hdr_name_c(OTC_SELECT, oph));
|
||||
return NULL;
|
||||
}
|
||||
return cnlink;
|
||||
}
|
||||
|
||||
static struct hnbgw_context_map *map_from_conn_id(struct hnbgw_sccp_user *hsu, uint32_t conn_id,
|
||||
const struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
hash_for_each_possible(hsu->hnbgw_context_map_by_conn_id, map, hnbgw_sccp_user_entry, conn_id) {
|
||||
if (map->scu_conn_id == conn_id)
|
||||
return map;
|
||||
}
|
||||
LOGP(DRANAP, LOGL_ERROR, "Rx for unknown SCCP connection ID: %u: %s\n",
|
||||
conn_id, osmo_scu_prim_hdr_name_c(OTC_SELECT, oph));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int handle_cn_unitdata(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_unitdata_param *param,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = cnlink_from_addr(hsu, ¶m->calling_addr, oph);
|
||||
if (!cnlink)
|
||||
return -ENOENT;
|
||||
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_SCCP_N_UNITDATA_IND);
|
||||
|
||||
if (param->called_addr.ssn != OSMO_SCCP_SSN_RANAP) {
|
||||
LOGP(DCN, LOGL_NOTICE, "N-UNITDATA.ind for unknown SSN %u\n",
|
||||
param->called_addr.ssn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return hnbgw_ranap_rx_udt_dl(cnlink, param, msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
}
|
||||
|
||||
static void handle_notice_ind(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_notice_param *param,
|
||||
const struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
|
||||
cnlink = cnlink_from_addr(hsu, ¶m->calling_addr, oph);
|
||||
if (!cnlink) {
|
||||
LOGP(DCN, LOGL_DEBUG, "(calling_addr=%s) N-NOTICE.ind cause=%u='%s' importance=%u didn't match any cnlink, ignoring\n",
|
||||
osmo_sccp_addr_dump(¶m->calling_addr),
|
||||
param->cause, osmo_sccp_return_cause_name(param->cause),
|
||||
param->importance);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE, "N-NOTICE.ind cause=%u='%s' importance=%u\n",
|
||||
param->cause, osmo_sccp_return_cause_name(param->cause),
|
||||
param->importance);
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_SCCP_N_NOTICE_IND);
|
||||
switch (param->cause) {
|
||||
case SCCP_RETURN_CAUSE_SUBSYSTEM_CONGESTION:
|
||||
case SCCP_RETURN_CAUSE_NETWORK_CONGESTION:
|
||||
/* Transient failures (hopefully), keep going. */
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Messages are not arriving to destination of cnlink. Kick it back to DISC state. */
|
||||
cnlink_set_disconnected(cnlink);
|
||||
}
|
||||
|
||||
static int handle_cn_conn_conf(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_connect_param *param,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
map = map_from_conn_id(hsu, param->conn_id, oph);
|
||||
if (!map || !map->cnlink)
|
||||
return -ENOENT;
|
||||
|
||||
LOGP(DCN, LOGL_DEBUG, "handle_cn_conn_conf() conn_id=%d, addrs: called=%s calling=%s responding=%s\n",
|
||||
param->conn_id,
|
||||
hnbgw_cnlink_sccp_addr_to_str(map->cnlink, ¶m->called_addr),
|
||||
hnbgw_cnlink_sccp_addr_to_str(map->cnlink, ¶m->calling_addr),
|
||||
hnbgw_cnlink_sccp_addr_to_str(map->cnlink, ¶m->responding_addr));
|
||||
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_N_CONNECT_CNF);
|
||||
|
||||
map_sccp_dispatch(map, MAP_SCCP_EV_RX_CONNECTION_CONFIRM, oph->msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_cn_data_ind(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_data_param *param,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
map = map_from_conn_id(hsu, param->conn_id, oph);
|
||||
if (!map || !map->cnlink)
|
||||
return -ENOENT;
|
||||
|
||||
return map_sccp_dispatch(map, MAP_SCCP_EV_RX_DATA_INDICATION, oph->msg);
|
||||
}
|
||||
|
||||
static int handle_cn_disc_ind(struct hnbgw_sccp_user *hsu,
|
||||
const struct osmo_scu_disconn_param *param,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
char cause_buf[128];
|
||||
|
||||
map = map_from_conn_id(hsu, param->conn_id, oph);
|
||||
if (!map || !map->cnlink)
|
||||
return -ENOENT;
|
||||
|
||||
LOGP(DCN, LOGL_DEBUG, "handle_cn_disc_ind() conn_id=%u responding_addr=%s cause=%s\n",
|
||||
param->conn_id,
|
||||
hnbgw_cnlink_sccp_addr_to_str(map->cnlink, ¶m->responding_addr),
|
||||
osmo_sua_sccp_cause_name(param->cause, cause_buf, sizeof(cause_buf)));
|
||||
|
||||
CNLINK_CTR_INC(map->cnlink, CNLINK_CTR_SCCP_N_DISCONNECT_IND);
|
||||
|
||||
return map_sccp_dispatch(map, MAP_SCCP_EV_RX_RELEASED, oph->msg);
|
||||
}
|
||||
|
||||
static struct hnbgw_cnlink *_cnlink_find_by_remote_pc(struct hnbgw_cnpool *cnpool, struct osmo_ss7_instance *cs7, uint32_t pc)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) {
|
||||
if (!cnlink->hnbgw_sccp_user)
|
||||
continue;
|
||||
if (cnlink->hnbgw_sccp_user->ss7 != cs7)
|
||||
continue;
|
||||
if ((cnlink->remote_addr.presence & OSMO_SCCP_ADDR_T_PC) == 0)
|
||||
continue;
|
||||
if (cnlink->remote_addr.pc != pc)
|
||||
continue;
|
||||
return cnlink;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find a cnlink by its remote sigtran point code on a given cs7 instance. */
|
||||
static struct hnbgw_cnlink *cnlink_find_by_remote_pc(struct osmo_ss7_instance *cs7, uint32_t pc)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
cnlink = _cnlink_find_by_remote_pc(g_hnbgw->sccp.cnpool_iucs, cs7, pc);
|
||||
if (!cnlink)
|
||||
cnlink = _cnlink_find_by_remote_pc(g_hnbgw->sccp.cnpool_iups, cs7, pc);
|
||||
return cnlink;
|
||||
}
|
||||
|
||||
static void handle_pcstate_ind(struct hnbgw_sccp_user *hsu, const struct osmo_scu_pcstate_param *pcst)
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
bool connected;
|
||||
bool disconnected;
|
||||
struct osmo_ss7_instance *cs7 = hsu->ss7;
|
||||
|
||||
LOGP(DCN, LOGL_DEBUG, "N-PCSTATE ind: affected_pc=%u=%s sp_status=%s remote_sccp_status=%s\n",
|
||||
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc),
|
||||
osmo_sccp_sp_status_name(pcst->sp_status),
|
||||
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
|
||||
|
||||
/* If we don't care about that point-code, ignore PCSTATE. */
|
||||
cnlink = cnlink_find_by_remote_pc(cs7, pcst->affected_pc);
|
||||
if (!cnlink)
|
||||
return;
|
||||
|
||||
CNLINK_CTR_INC(cnlink, CNLINK_CTR_SCCP_N_PCSTATE_IND);
|
||||
|
||||
/* See if this marks the point code to have become available, or to have been lost.
|
||||
*
|
||||
* I want to detect two events:
|
||||
* - connection event (both indicators say PC is reachable).
|
||||
* - disconnection event (at least one indicator says the PC is not reachable).
|
||||
*
|
||||
* There are two separate incoming indicators with various possible values -- the incoming events can be:
|
||||
*
|
||||
* - neither connection nor disconnection indicated -- just indicating congestion
|
||||
* connected == false, disconnected == false --> do nothing.
|
||||
* - both incoming values indicate that we are connected
|
||||
* --> trigger connected
|
||||
* - both indicate we are disconnected
|
||||
* --> trigger disconnected
|
||||
* - one value indicates 'connected', the other indicates 'disconnected'
|
||||
* --> trigger disconnected
|
||||
*
|
||||
* Congestion could imply that we're connected, but it does not indicate that a PC's reachability changed, so no need to
|
||||
* trigger on that.
|
||||
*/
|
||||
connected = false;
|
||||
disconnected = false;
|
||||
|
||||
switch (pcst->sp_status) {
|
||||
case OSMO_SCCP_SP_S_ACCESSIBLE:
|
||||
connected = true;
|
||||
break;
|
||||
case OSMO_SCCP_SP_S_INACCESSIBLE:
|
||||
disconnected = true;
|
||||
break;
|
||||
default:
|
||||
case OSMO_SCCP_SP_S_CONGESTED:
|
||||
/* Neither connecting nor disconnecting */
|
||||
break;
|
||||
}
|
||||
|
||||
switch (pcst->remote_sccp_status) {
|
||||
case OSMO_SCCP_REM_SCCP_S_AVAILABLE:
|
||||
if (!disconnected)
|
||||
connected = true;
|
||||
break;
|
||||
case OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN:
|
||||
case OSMO_SCCP_REM_SCCP_S_UNEQUIPPED:
|
||||
case OSMO_SCCP_REM_SCCP_S_INACCESSIBLE:
|
||||
disconnected = true;
|
||||
connected = false;
|
||||
break;
|
||||
default:
|
||||
case OSMO_SCCP_REM_SCCP_S_CONGESTED:
|
||||
/* Neither connecting nor disconnecting */
|
||||
break;
|
||||
}
|
||||
|
||||
if (disconnected && cnlink_is_conn_ready(cnlink)) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE,
|
||||
"now unreachable: N-PCSTATE ind: pc=%u=%s sp_status=%s remote_sccp_status=%s\n",
|
||||
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc),
|
||||
osmo_sccp_sp_status_name(pcst->sp_status),
|
||||
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
|
||||
/* A previously usable cnlink has disconnected. Kick it back to DISC state. */
|
||||
cnlink_set_disconnected(cnlink);
|
||||
} else if (connected && !cnlink_is_conn_ready(cnlink)) {
|
||||
LOG_CNLINK(cnlink, DCN, LOGL_NOTICE,
|
||||
"now available: N-PCSTATE ind: pc=%u=%s sp_status=%s remote_sccp_status=%s\n",
|
||||
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc),
|
||||
osmo_sccp_sp_status_name(pcst->sp_status),
|
||||
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
|
||||
/* A previously unusable cnlink has become reachable. Trigger immediate RANAP RESET -- we would resend a
|
||||
* RESET either way, but we might as well do it now to speed up connecting. */
|
||||
cnlink_resend_reset(cnlink);
|
||||
}
|
||||
}
|
||||
|
||||
/* Entry point for primitives coming up from SCCP User SAP.
|
||||
* Ownership of oph->msg is transferred to us. */
|
||||
static int sccp_sap_up(struct osmo_prim_hdr *oph, void *ctx)
|
||||
{
|
||||
struct osmo_sccp_user *scu = ctx;
|
||||
struct hnbgw_sccp_user *hsu;
|
||||
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DCN, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph));
|
||||
|
||||
if (!scu) {
|
||||
LOGP(DCN, LOGL_ERROR,
|
||||
"sccp_sap_up(): NULL osmo_sccp_user, cannot send prim (sap %u prim %u op %d)\n",
|
||||
oph->sap, oph->primitive, oph->operation);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hsu = osmo_sccp_user_get_priv(scu);
|
||||
if (!hsu) {
|
||||
LOGP(DCN, LOGL_ERROR,
|
||||
"sccp_sap_up(): NULL hnbgw_sccp_user, cannot send prim (sap %u prim %u op %d)\n",
|
||||
oph->sap, oph->primitive, oph->operation);
|
||||
return -1;
|
||||
}
|
||||
|
||||
talloc_steal(OTC_SELECT, oph->msg);
|
||||
|
||||
switch (OSMO_PRIM_HDR(oph)) {
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
|
||||
rc = handle_cn_unitdata(hsu, &prim->u.unitdata, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_NOTICE, PRIM_OP_INDICATION):
|
||||
handle_notice_ind(hsu, &prim->u.notice, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
|
||||
rc = handle_cn_conn_conf(hsu, &prim->u.connect, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
|
||||
rc = handle_cn_data_ind(hsu, &prim->u.data, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
|
||||
rc = handle_cn_disc_ind(hsu, &prim->u.disconnect, oph);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION):
|
||||
handle_pcstate_ind(hsu, &prim->u.pcstate);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGP(DCN, LOGL_ERROR,
|
||||
"Received unknown prim %u from SCCP USER SAP\n",
|
||||
OSMO_PRIM_HDR(oph));
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Submit primitives to SCCP User SAP
|
||||
***********************************************************************/
|
||||
|
||||
int hnbgw_sccp_user_tx_unitdata_req(struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *called_addr, struct msgb *ranap_msg)
|
||||
{
|
||||
if (!hsu) {
|
||||
LOGP(DCN, LOGL_ERROR, "Failed to send SCCP N-UNITDATA.req: no SCCP User\n");
|
||||
return -1;
|
||||
}
|
||||
OSMO_ASSERT(called_addr);
|
||||
return osmo_sccp_tx_unitdata_msg(hsu->sccp_user,
|
||||
&hsu->local_addr,
|
||||
called_addr,
|
||||
ranap_msg);
|
||||
}
|
||||
|
||||
int hnbgw_sccp_user_tx_connect_req(struct hnbgw_sccp_user *hsu, const struct osmo_sccp_addr *called_addr, uint32_t scu_conn_id, struct msgb *ranap_msg)
|
||||
{
|
||||
struct osmo_scu_prim *prim;
|
||||
int rc;
|
||||
|
||||
if (!hsu) {
|
||||
LOGP(DCN, LOGL_ERROR, "Failed to send SCCP N-CONNECT.req(%u): no SCCP User\n", scu_conn_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(called_addr);
|
||||
|
||||
prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_REQUEST, ranap_msg);
|
||||
prim->u.connect.called_addr = *called_addr;
|
||||
prim->u.connect.calling_addr = hsu->local_addr;
|
||||
prim->u.connect.sccp_class = 2;
|
||||
prim->u.connect.conn_id = scu_conn_id;
|
||||
|
||||
rc = osmo_sccp_user_sap_down_nofree(hsu->sccp_user, &prim->oph);
|
||||
if (rc)
|
||||
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to send SCCP Connection Request to CN\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
int hnbgw_sccp_user_tx_data_req(struct hnbgw_sccp_user *hsu, uint32_t scu_conn_id, struct msgb *ranap_msg)
|
||||
{
|
||||
struct osmo_scu_prim *prim;
|
||||
int rc;
|
||||
|
||||
if (!hsu) {
|
||||
LOGP(DCN, LOGL_ERROR, "Failed to send SCCP N-DATA.req(%u): no SCCP User\n", scu_conn_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, ranap_msg);
|
||||
prim->u.data.conn_id = scu_conn_id;
|
||||
|
||||
rc = osmo_sccp_user_sap_down_nofree(hsu->sccp_user, &prim->oph);
|
||||
if (rc)
|
||||
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to send SCCP N-DATA.req(%u)\n", scu_conn_id);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int hnbgw_sccp_user_tx_disconnect_req(struct hnbgw_sccp_user *hsu, uint32_t scu_conn_id)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!hsu) {
|
||||
LOGP(DCN, LOGL_ERROR, "Failed to send SCCP N-DISCONNECT.req(%u): no SCCP User\n", scu_conn_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = osmo_sccp_tx_disconn(hsu->sccp_user, scu_conn_id, NULL,
|
||||
SCCP_RELEASE_CAUSE_END_USER_ORIGINATED);
|
||||
if (rc)
|
||||
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to send SCCP N-DISCONNECT.req(%u)\n", scu_conn_id);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* struct hnbgw_sccp_user lifecycle:
|
||||
***********************************************************************/
|
||||
|
||||
static int hnbgw_sccp_user_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
|
||||
{
|
||||
struct hnbgw_sccp_user *hsu = e->use_count->talloc_object;
|
||||
int32_t total;
|
||||
int level;
|
||||
|
||||
if (!e->use)
|
||||
return -EINVAL;
|
||||
|
||||
total = osmo_use_count_total(&hsu->use_count);
|
||||
|
||||
if (total == 0
|
||||
|| (total == 1 && old_use_count == 0 && e->count == 1))
|
||||
level = LOGL_INFO;
|
||||
else
|
||||
level = LOGL_DEBUG;
|
||||
|
||||
LOGPSRC(DCN, level, file, line,
|
||||
"%s: %s %s: now used by %s\n",
|
||||
hsu->name,
|
||||
(e->count - old_use_count) > 0 ? "+" : "-",
|
||||
e->use,
|
||||
osmo_use_count_to_str_c(OTC_SELECT, &hsu->use_count));
|
||||
|
||||
if (e->count < 0)
|
||||
return -ERANGE;
|
||||
|
||||
if (total == 0)
|
||||
talloc_free(hsu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hnbgw_sccp_user_talloc_destructor(struct hnbgw_sccp_user *hsu)
|
||||
{
|
||||
if (hsu->sccp_user) {
|
||||
osmo_sccp_user_unbind(hsu->sccp_user);
|
||||
hsu->sccp_user = NULL;
|
||||
}
|
||||
llist_del(&hsu->entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct hnbgw_sccp_user *hnbgw_sccp_user_alloc(int ss7_id)
|
||||
{
|
||||
struct osmo_sccp_instance *sccp;
|
||||
uint32_t local_pc;
|
||||
struct hnbgw_sccp_user *hsu;
|
||||
|
||||
hsu = talloc_zero(g_hnbgw, struct hnbgw_sccp_user);
|
||||
OSMO_ASSERT(hsu);
|
||||
*hsu = (struct hnbgw_sccp_user){
|
||||
.name = talloc_asprintf(hsu, "cs7-%u-sccp-OsmoHNBGW", ss7_id),
|
||||
.use_count = {
|
||||
.talloc_object = hsu,
|
||||
.use_cb = hnbgw_sccp_user_use_cb,
|
||||
},
|
||||
};
|
||||
hash_init(hsu->hnbgw_context_map_by_conn_id);
|
||||
llist_add_tail(&hsu->entry, &g_hnbgw->sccp.users);
|
||||
talloc_set_destructor(hsu, hnbgw_sccp_user_talloc_destructor);
|
||||
|
||||
sccp = osmo_sccp_simple_client_on_ss7_id(g_hnbgw,
|
||||
ss7_id,
|
||||
hsu->name,
|
||||
DEFAULT_PC_HNBGW,
|
||||
OSMO_SS7_ASP_PROT_M3UA,
|
||||
0,
|
||||
"localhost",
|
||||
-1,
|
||||
"localhost");
|
||||
if (!sccp) {
|
||||
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to configure SCCP on 'cs7 instance %u'\n",
|
||||
ss7_id);
|
||||
goto free_hsu_ret;
|
||||
}
|
||||
hsu->ss7 = osmo_sccp_get_ss7(sccp);
|
||||
LOG_HSU(hsu, DCN, LOGL_NOTICE, "created SCCP instance on cs7 instance %u\n", osmo_ss7_instance_get_id(hsu->ss7));
|
||||
|
||||
/* Bind the SCCP user, using the cs7 instance's default point-code if one is configured, or osmo-hnbgw's default
|
||||
* local PC. */
|
||||
local_pc = osmo_ss7_instance_get_primary_pc(hsu->ss7);
|
||||
if (!osmo_ss7_pc_is_valid(local_pc))
|
||||
local_pc = DEFAULT_PC_HNBGW;
|
||||
|
||||
LOG_HSU(hsu, DCN, LOGL_DEBUG, "binding OsmoHNBGW user to cs7 instance %u, local PC %u = %s\n",
|
||||
osmo_ss7_instance_get_id(hsu->ss7), local_pc, osmo_ss7_pointcode_print(hsu->ss7, local_pc));
|
||||
|
||||
char *sccp_user_name = talloc_asprintf(hsu, "%s-RANAP", hsu->name);
|
||||
hsu->sccp_user = osmo_sccp_user_bind_pc(sccp, sccp_user_name, sccp_sap_up, OSMO_SCCP_SSN_RANAP, local_pc);
|
||||
talloc_free(sccp_user_name);
|
||||
if (!hsu->sccp_user) {
|
||||
LOG_HSU(hsu, DCN, LOGL_ERROR, "Failed to init SCCP User\n");
|
||||
goto free_hsu_ret;
|
||||
}
|
||||
|
||||
osmo_sccp_make_addr_pc_ssn(&hsu->local_addr, local_pc, OSMO_SCCP_SSN_RANAP);
|
||||
osmo_sccp_user_set_priv(hsu->sccp_user, hsu);
|
||||
|
||||
return hsu;
|
||||
|
||||
free_hsu_ret:
|
||||
talloc_free(hsu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -31,10 +31,13 @@
|
||||
|
||||
#include <osmocom/hnbgw/vty.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#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>
|
||||
@@ -83,7 +86,7 @@ DEFUN(cfg_hnbgw_iucs, cfg_hnbgw_iucs_cmd,
|
||||
"iucs", "Configure IuCS options")
|
||||
{
|
||||
vty->node = IUCS_NODE;
|
||||
vty->index = &g_hnbgw->sccp.cnpool_iucs;
|
||||
vty->index = g_hnbgw->sccp.cnpool_iucs;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -97,7 +100,7 @@ DEFUN(cfg_hnbgw_iups, cfg_hnbgw_iups_cmd,
|
||||
"iups", "Configure IuPS options")
|
||||
{
|
||||
vty->node = IUPS_NODE;
|
||||
vty->index = &g_hnbgw->sccp.cnpool_iups;
|
||||
vty->index = g_hnbgw->sccp.cnpool_iups;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -146,12 +149,13 @@ static void _show_cnlink(struct vty *vty, struct hnbgw_cnlink *cnlink)
|
||||
return;
|
||||
}
|
||||
|
||||
vty_out(vty, "%s <->",
|
||||
vty_out(vty, "%s: %s <->",
|
||||
cnlink->name,
|
||||
osmo_sccp_user_name(cnlink->hnbgw_sccp_user->sccp_user));
|
||||
vty_out(vty, " %s%s%s%s",
|
||||
cnlink->use.remote_addr_name ? : "",
|
||||
cnlink->use.remote_addr_name ? "=" : "",
|
||||
cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr),
|
||||
hnbgw_cnlink_sccp_addr_to_str(cnlink, &cnlink->remote_addr),
|
||||
VTY_NEWLINE);
|
||||
|
||||
rt = osmo_ss7_route_lookup(ss7, cnlink->remote_addr.pc);
|
||||
@@ -164,10 +168,10 @@ DEFUN(show_cnlink, show_cnlink_cmd, "show cnlink",
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink;
|
||||
vty_out(vty, "IuCS: ");
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iucs.cnlinks, entry)
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iucs->cnlinks, entry)
|
||||
_show_cnlink(vty, cnlink);
|
||||
vty_out(vty, "IuPS: ");
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iups.cnlinks, entry)
|
||||
llist_for_each_entry(cnlink, &g_hnbgw->sccp.cnpool_iups->cnlinks, entry)
|
||||
_show_cnlink(vty, cnlink);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -209,8 +213,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 +232,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 +276,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);
|
||||
@@ -350,7 +337,7 @@ DEFUN(cfg_hnbgw_iuh_hnbap_allow_tmsi, cfg_hnbgw_iuh_hnbap_allow_tmsi_cmd,
|
||||
"hnbap-allow-tmsi (0|1)",
|
||||
"Allow HNBAP UE Register messages with TMSI or PTMSI identity\n"
|
||||
"Only accept IMSI identity, reject TMSI or PTMSI\n"
|
||||
"Accept IMSI, TMSI or PTMSI as UE identity\n")
|
||||
"Accept IMSI, TMSI or PTMSI as UE identity (default)\n")
|
||||
{
|
||||
g_hnbgw->config.hnbap_allow_tmsi = (*argv[0] == '1');
|
||||
return CMD_SUCCESS;
|
||||
@@ -525,7 +512,7 @@ DEFUN(cfg_msc_nr, cfg_msc_nr_cmd,
|
||||
"Configure an IuCS link to an MSC\n"
|
||||
"MSC nr\n")
|
||||
{
|
||||
return cnlink_nr(vty, &g_hnbgw->sccp.cnpool_iucs, argc, argv);
|
||||
return cnlink_nr(vty, g_hnbgw->sccp.cnpool_iucs, argc, argv);
|
||||
}
|
||||
|
||||
/* 'sgsn 0' */
|
||||
@@ -534,12 +521,24 @@ DEFUN(cfg_sgsn_nr, cfg_sgsn_nr_cmd,
|
||||
"Configure an IuPS link to an SGSN\n"
|
||||
"SGSN nr\n")
|
||||
{
|
||||
return cnlink_nr(vty, &g_hnbgw->sccp.cnpool_iups, argc, argv);
|
||||
return cnlink_nr(vty, g_hnbgw->sccp.cnpool_iups, argc, argv);
|
||||
}
|
||||
|
||||
/* 'msc 0' / 'remote-addr my-msc' and
|
||||
* 'sgsn 0' / 'remote-addr my-sgsn'
|
||||
*/
|
||||
DEFUN(cfg_cnlink_name,
|
||||
cfg_cnlink_name_cmd,
|
||||
"name NAME",
|
||||
"Set user defined name for this msc/sgsn\n"
|
||||
"The user defined name to be set for this msc/sgsn\n")
|
||||
{
|
||||
struct hnbgw_cnlink *cnlink = vty->index;
|
||||
if (hnbgw_cnlink_set_name(cnlink, argv[0]) < 0)
|
||||
return CMD_WARNING;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_cnlink_remote_addr,
|
||||
cfg_cnlink_remote_addr_cmd,
|
||||
"remote-addr NAME",
|
||||
@@ -730,14 +729,14 @@ DEFUN(show_nri, show_nri_cmd,
|
||||
* nri null add ...
|
||||
*/
|
||||
vty_out(vty, "hnbgw%s", VTY_NEWLINE);
|
||||
cnpool_write_nri(vty, &g_hnbgw->sccp.cnpool_iucs, true);
|
||||
cnpool_write_nri(vty, &g_hnbgw->sccp.cnpool_iups, true);
|
||||
cnpool_write_nri(vty, g_hnbgw->sccp.cnpool_iucs, true);
|
||||
cnpool_write_nri(vty, g_hnbgw->sccp.cnpool_iups, true);
|
||||
|
||||
/* msc 0
|
||||
* nri add ...
|
||||
*/
|
||||
cnlinks_write_nri(vty, &g_hnbgw->sccp.cnpool_iucs, true);
|
||||
cnlinks_write_nri(vty, &g_hnbgw->sccp.cnpool_iups, true);
|
||||
cnlinks_write_nri(vty, g_hnbgw->sccp.cnpool_iucs, true);
|
||||
cnlinks_write_nri(vty, g_hnbgw->sccp.cnpool_iups, true);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -753,9 +752,9 @@ DEFUN_HIDDEN(cnpool_roundrobin_next, cnpool_roundrobin_next_cmd,
|
||||
{
|
||||
struct hnbgw_cnpool *cnpool;
|
||||
if (!strcmp("msc", argv[0]))
|
||||
cnpool = &g_hnbgw->sccp.cnpool_iucs;
|
||||
cnpool = g_hnbgw->sccp.cnpool_iucs;
|
||||
else
|
||||
cnpool = &g_hnbgw->sccp.cnpool_iups;
|
||||
cnpool = g_hnbgw->sccp.cnpool_iups;
|
||||
cnpool->round_robin_next_nr = atoi(argv[1]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -774,9 +773,9 @@ DEFUN(cnlink_ranap_reset, cnlink_ranap_reset_cmd,
|
||||
int nr = atoi(argv[1]);
|
||||
|
||||
if (!strcmp("msc", msc_sgsn))
|
||||
cnpool = &g_hnbgw->sccp.cnpool_iucs;
|
||||
cnpool = g_hnbgw->sccp.cnpool_iucs;
|
||||
else
|
||||
cnpool = &g_hnbgw->sccp.cnpool_iups;
|
||||
cnpool = g_hnbgw->sccp.cnpool_iups;
|
||||
|
||||
cnlink = cnlink_get_nr(cnpool, nr, false);
|
||||
if (!cnlink) {
|
||||
@@ -815,11 +814,11 @@ DEFUN(cfg_config_apply_sccp, cfg_config_apply_sccp_cmd,
|
||||
{
|
||||
struct hnbgw_cnpool *cnpool;
|
||||
|
||||
cnpool = &g_hnbgw->sccp.cnpool_iucs;
|
||||
cnpool = g_hnbgw->sccp.cnpool_iucs;
|
||||
hnbgw_cnpool_apply_cfg(cnpool);
|
||||
hnbgw_cnpool_cnlinks_start_or_restart(cnpool);
|
||||
|
||||
cnpool = &g_hnbgw->sccp.cnpool_iups;
|
||||
cnpool = g_hnbgw->sccp.cnpool_iups;
|
||||
hnbgw_cnpool_apply_cfg(cnpool);
|
||||
hnbgw_cnpool_cnlinks_start_or_restart(cnpool);
|
||||
|
||||
@@ -878,6 +877,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 = {
|
||||
@@ -998,8 +1027,14 @@ static int config_write_hnbgw(struct vty *vty)
|
||||
llist_for_each_entry(hnbp, &g_hnbgw->hnb_persistent_list, list)
|
||||
write_one_hnbp(vty, hnbp);
|
||||
|
||||
_config_write_cnpool(vty, &g_hnbgw->sccp.cnpool_iucs);
|
||||
_config_write_cnpool(vty, &g_hnbgw->sccp.cnpool_iups);
|
||||
_config_write_cnpool(vty, g_hnbgw->sccp.cnpool_iucs);
|
||||
_config_write_cnpool(vty, g_hnbgw->sccp.cnpool_iups);
|
||||
|
||||
if (g_hnbgw->config.nft_kpi.enable)
|
||||
vty_out(vty, " nft-kpi%s%s%s",
|
||||
g_hnbgw->config.nft_kpi.table_name ? " " : "",
|
||||
g_hnbgw->config.nft_kpi.table_name ? : "",
|
||||
VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -1019,8 +1054,8 @@ static int config_write_hnbgw_iuh(struct vty *vty)
|
||||
if (port && port != IUH_DEFAULT_SCTP_PORT)
|
||||
vty_out(vty, " local-port %u%s", port, VTY_NEWLINE);
|
||||
|
||||
if (g_hnbgw->config.hnbap_allow_tmsi)
|
||||
vty_out(vty, " hnbap-allow-tmsi 1%s", VTY_NEWLINE);
|
||||
if (!g_hnbgw->config.hnbap_allow_tmsi)
|
||||
vty_out(vty, " hnbap-allow-tmsi 0%s", VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -1035,6 +1070,7 @@ static void _config_write_cnlink(struct vty *vty, struct hnbgw_cnpool *cnpool)
|
||||
|
||||
llist_for_each_entry(cnlink, &cnpool->cnlinks, entry) {
|
||||
vty_out(vty, "%s %d%s", cnpool->peer_name, cnlink->nr, VTY_NEWLINE);
|
||||
vty_out(vty, " name %s%s", cnlink->name, VTY_NEWLINE);
|
||||
if (cnlink->vty.remote_addr_name)
|
||||
vty_out(vty, " remote-addr %s%s", cnlink->vty.remote_addr_name, VTY_NEWLINE);
|
||||
cnlink_write_nri(vty, cnlink, false);
|
||||
@@ -1043,13 +1079,13 @@ static void _config_write_cnlink(struct vty *vty, struct hnbgw_cnpool *cnpool)
|
||||
|
||||
static int config_write_msc(struct vty *vty)
|
||||
{
|
||||
_config_write_cnlink(vty, &g_hnbgw->sccp.cnpool_iucs);
|
||||
_config_write_cnlink(vty, g_hnbgw->sccp.cnpool_iucs);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int config_write_sgsn(struct vty *vty)
|
||||
{
|
||||
_config_write_cnlink(vty, &g_hnbgw->sccp.cnpool_iups);
|
||||
_config_write_cnlink(vty, g_hnbgw->sccp.cnpool_iups);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1076,6 +1112,7 @@ static int config_write_hnbgw_pfcp(struct vty *vty)
|
||||
|
||||
static void install_cnlink_elements(int node)
|
||||
{
|
||||
install_element(node, &cfg_cnlink_name_cmd);
|
||||
install_element(node, &cfg_cnlink_remote_addr_cmd);
|
||||
install_element(node, &cfg_cnlink_nri_add_cmd);
|
||||
install_element(node, &cfg_cnlink_nri_del_cmd);
|
||||
@@ -1125,10 +1162,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);
|
||||
|
||||
112
src/osmo-hnbgw/kpi_dtap.c
Normal file
112
src/osmo-hnbgw/kpi_dtap.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/* 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/hnb_persistent.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,10 +24,18 @@
|
||||
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb_persistent.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/kpi.h>
|
||||
|
||||
const struct value_string hnbgw_rab_state_names[] = {
|
||||
{ RAB_STATE_INACTIVE, "INACTIVE" },
|
||||
{ RAB_STATE_ACT_REQ, "ACT_REQ" },
|
||||
{ RAB_STATE_ACTIVE, "ACTIVE" },
|
||||
{ RAB_STATE_REL_REQ, "REL_REQ" },
|
||||
{}
|
||||
};
|
||||
|
||||
/***********************************************************************
|
||||
* DOWNLINK messages
|
||||
@@ -36,14 +44,24 @@
|
||||
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++) {
|
||||
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) {
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT);
|
||||
} else {
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT_ABNORMAL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -88,15 +106,20 @@ static void kpi_ranap_process_dl_rab_ass_req(struct hnbgw_context_map *map, rana
|
||||
* new, it is a setup */
|
||||
switch (map->rab_state[rab_id]) {
|
||||
case RAB_STATE_ACTIVE:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_REQ : HNB_CTR_RANAP_CS_RAB_MOD_REQ);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_REQ :
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_REQ);
|
||||
break;
|
||||
case RAB_STATE_INACTIVE:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_REQ : HNB_CTR_RANAP_CS_RAB_ACT_REQ);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_REQ :
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_REQ);
|
||||
map->rab_state[rab_id] = RAB_STATE_ACT_REQ;
|
||||
break;
|
||||
default:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_REQ_UNEXP :
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_REQ_UNEXP);
|
||||
LOG_MAP(map, DRANAP, LOGL_NOTICE,
|
||||
"Unexpected RAB Activation/Modification Req for RAB in state %u\n", map->rab_state[rab_id]);
|
||||
"Unexpected RAB Activation/Modification Req for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -106,10 +129,6 @@ 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 = {};
|
||||
@@ -133,10 +152,21 @@ 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) {
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_REQ :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ);
|
||||
} else {
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ_ABNORMAL);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_REQ_UNEXP :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_REQ_UNEXP);
|
||||
LOG_MAP(map, DRANAP, LOGL_NOTICE,
|
||||
"Unexpected RAB Release Req in state %u\n", map->rab_state[rab_id]);
|
||||
"Unexpected RAB Release Req in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
break;
|
||||
}
|
||||
/* mark that RAB as release requested */
|
||||
@@ -147,8 +177,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 +205,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;
|
||||
}
|
||||
@@ -201,15 +253,20 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
|
||||
/* differentiate modify / activate */
|
||||
switch (map->rab_state[rab_id]) {
|
||||
case RAB_STATE_ACT_REQ:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_CNF : HNB_CTR_RANAP_CS_RAB_ACT_CNF);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_CNF :
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_CNF);
|
||||
map->rab_state[rab_id] = RAB_STATE_ACTIVE;
|
||||
break;
|
||||
case RAB_STATE_ACTIVE:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_CNF : HNB_CTR_RANAP_CS_RAB_MOD_CNF);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_CNF :
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_CNF);
|
||||
break;
|
||||
default:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_CNF_UNEXP :
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_CNF_UNEXP);
|
||||
LOG_MAP(map, DRANAP, LOGL_NOTICE,
|
||||
"Unexpected RAB Activation/Modification Conf for RAB in state %u\n", map->rab_state[rab_id]);
|
||||
"Unexpected RAB Activation/Modification Conf for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -220,8 +277,6 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
|
||||
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_RELEASEDLIST_PRESENT) {
|
||||
RANAP_RAB_ReleasedList_t *r_list = &ies->raB_ReleasedList;
|
||||
/* increment number of released RABs, we don't need to do that individually during iteration */
|
||||
HNBP_CTR_ADD(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_CNF : HNB_CTR_RANAP_CS_RAB_REL_CNF,
|
||||
r_list->raB_ReleasedList_ies.list.count);
|
||||
for (unsigned int i = 0; i < r_list->raB_ReleasedList_ies.list.count; i++) {
|
||||
RANAP_IE_t *released_list_ie = r_list->raB_ReleasedList_ies.list.array[i];
|
||||
RANAP_RAB_ReleasedItemIEs_t _rab_rel_item_ies = {};
|
||||
@@ -245,10 +300,15 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
|
||||
|
||||
switch (map->rab_state[rab_id]) {
|
||||
case RAB_STATE_REL_REQ:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_CNF :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_CNF);
|
||||
break;
|
||||
default:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_CNF_UNEXP :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_CNF_UNEXP);
|
||||
LOG_MAP(map, DRANAP, LOGL_NOTICE,
|
||||
"Unexpected RAB Release Conf for RAB in state %u\n", map->rab_state[rab_id]);
|
||||
"Unexpected RAB Release Conf for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
break;
|
||||
}
|
||||
/* mark that RAB as released */
|
||||
@@ -287,16 +347,21 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
|
||||
/* differentiate modify / activate */
|
||||
switch (map->rab_state[rab_id]) {
|
||||
case RAB_STATE_ACT_REQ:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_FAIL : HNB_CTR_RANAP_CS_RAB_ACT_FAIL);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_FAIL :
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_FAIL);
|
||||
map->rab_state[rab_id] = RAB_STATE_INACTIVE;
|
||||
break;
|
||||
case RAB_STATE_ACTIVE:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_FAIL : HNB_CTR_RANAP_CS_RAB_MOD_FAIL);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_FAIL :
|
||||
HNB_CTR_RANAP_CS_RAB_MOD_FAIL);
|
||||
// FIXME: does it remain active after modification failure?
|
||||
break;
|
||||
default:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_FAIL_UNEXP :
|
||||
HNB_CTR_RANAP_CS_RAB_ACT_FAIL_UNEXP);
|
||||
LOG_MAP(map, DRANAP, LOGL_NOTICE,
|
||||
"Unexpected RAB Activation/Modification Failed for RAB in state %u\n", map->rab_state[rab_id]);
|
||||
"Unexpected RAB Activation/Modification Failed for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -307,8 +372,6 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
|
||||
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_RELEASEFAILEDLIST_PRESENT) {
|
||||
RANAP_RAB_ReleaseFailedList_t *rf_list = &ies->raB_ReleaseFailedList;
|
||||
/* increment number of released RABs, we don't need to do that individually during iteration */
|
||||
HNBP_CTR_ADD(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_FAIL : HNB_CTR_RANAP_CS_RAB_REL_FAIL,
|
||||
rf_list->raB_FailedList_ies.list.count);
|
||||
for (unsigned int i = 0; i < rf_list->raB_FailedList_ies.list.count; i++) {
|
||||
RANAP_IE_t *failed_list_ie = rf_list->raB_FailedList_ies.list.array[i];
|
||||
RANAP_RAB_FailedItemIEs_t _rab_failed_item_ies = {};
|
||||
@@ -333,16 +396,21 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
|
||||
/* differentiate modify / activate */
|
||||
switch (map->rab_state[rab_id]) {
|
||||
case RAB_STATE_ACT_REQ:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_ACT_FAIL : HNB_CTR_RANAP_CS_RAB_ACT_FAIL);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_FAIL :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_FAIL);
|
||||
map->rab_state[rab_id] = RAB_STATE_INACTIVE;
|
||||
break;
|
||||
case RAB_STATE_ACTIVE:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_MOD_FAIL : HNB_CTR_RANAP_CS_RAB_MOD_FAIL);
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_FAIL :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_FAIL);
|
||||
// FIXME: does it remain active after modification failure?
|
||||
break;
|
||||
default:
|
||||
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_FAIL_UNEXP :
|
||||
HNB_CTR_RANAP_CS_RAB_REL_FAIL_UNEXP);
|
||||
LOG_MAP(map, DRANAP, LOGL_NOTICE,
|
||||
"Unexpected RAB Release Failed for RAB in state %u\n", map->rab_state[rab_id]);
|
||||
"Unexpected RAB Release Failed for RAB in state %s\n",
|
||||
hnbgw_rab_state_name(map->rab_state[rab_id]));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -352,8 +420,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 +453,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;
|
||||
@@ -846,9 +846,9 @@ int handle_cs_rab_ass_req(struct hnbgw_context_map *map, struct msgb *ranap_msg,
|
||||
* into account under the assumption that voice calls typically require a single RAB only. Nevertheless, we
|
||||
* will block all incoming RAB assignments that try to assign more (or less) than one RAB. */
|
||||
if (ranap_rab_ass_req_ies_get_count(&message->msg.raB_AssignmentRequestIEs) != 1) {
|
||||
LOGP(DMGW, LOGL_ERROR,
|
||||
"%s() rua_ctx_id=%d, RAB-AssignmentRequest with more than one RAB assignment -- abort!\n",
|
||||
__func__, map->rua_ctx_id);
|
||||
LOG_MAP(map, DMGW, LOGL_ERROR,
|
||||
"%s() RAB-AssignmentRequest with more than one RAB assignment -- abort!\n",
|
||||
__func__);
|
||||
tx_release_req(map);
|
||||
return -1;
|
||||
}
|
||||
@@ -886,9 +886,9 @@ int mgw_fsm_handle_cs_rab_ass_resp(struct hnbgw_context_map *map, struct msgb *r
|
||||
/* NOTE: This situation is a corner-case. We may end up here when the co-located MGW caused a problem
|
||||
* on the way between RANAP RAB Assignment Request and RANAP RAB Assignment Response. */
|
||||
|
||||
LOGP(DMGW, LOGL_ERROR,
|
||||
"%s() rua_ctx_id=%d, no MGW fsm -- sending Iu-Release-Request!\n",
|
||||
__func__, map->rua_ctx_id);
|
||||
LOG_MAP(map, DMGW, LOGL_ERROR,
|
||||
"%s() no MGW fsm -- sending Iu-Release-Request!\n",
|
||||
__func__);
|
||||
|
||||
/* Send a release request, to make sure that the MSC is aware of the problem. */
|
||||
tx_release_req(map);
|
||||
|
||||
1046
src/osmo-hnbgw/nft_kpi.c
Normal file
1046
src/osmo-hnbgw/nft_kpi.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -45,6 +45,7 @@
|
||||
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_cn.h>
|
||||
#include <osmocom/hnbgw/hnbgw_pfcp.h>
|
||||
@@ -222,6 +223,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,8 +331,22 @@ int main(int argc, char **argv)
|
||||
hnbgw_pfcp_init();
|
||||
#endif
|
||||
|
||||
hnbgw_cnpool_start(&g_hnbgw->sccp.cnpool_iucs);
|
||||
hnbgw_cnpool_start(&g_hnbgw->sccp.cnpool_iups);
|
||||
/* 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);
|
||||
|
||||
if (hnbgw_cmdline_config.daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
@@ -345,6 +361,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)
|
||||
@@ -352,5 +370,8 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
/* not reached */
|
||||
#if ENABLE_PFCP
|
||||
hnbgw_pfcp_release();
|
||||
#endif
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ static const struct value_string ps_rab_ass_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(PS_RAB_ASS_EV_LOCAL_F_TEIDS_RX),
|
||||
OSMO_VALUE_STRING(PS_RAB_ASS_EV_RAB_ASS_RESP),
|
||||
OSMO_VALUE_STRING(PS_RAB_ASS_EV_RAB_ESTABLISHED),
|
||||
OSMO_VALUE_STRING(PS_RAB_ASS_EV_RAB_RELEASED),
|
||||
OSMO_VALUE_STRING(PS_RAB_ASS_EV_RAB_FAIL),
|
||||
{}
|
||||
};
|
||||
@@ -81,10 +82,10 @@ enum ps_rab_ass_state {
|
||||
* This structure manages the RAB Assignment procedures, and the currently set up RABs:
|
||||
*
|
||||
* - hnbgw_context_map
|
||||
* - .ps_rab_ass: list of PS RAB Assignment procedures
|
||||
* - .ps_rab_ass_list: list of PS RAB Assignment procedures
|
||||
* - ps_rab_ass_fsm: one RANAP PS RAB Assignment procedure
|
||||
* - ...
|
||||
* - .ps_rabs: list of individual PS RABs
|
||||
* - .ps_rab_list: list of individual PS RABs
|
||||
* - ps_rab_fsm: one GTP mapping with PFCP session to the UPF, for a single RAB
|
||||
* - ...
|
||||
*
|
||||
@@ -111,6 +112,9 @@ struct ps_rab_ass {
|
||||
* ps_rab_fsms, without iterating the RAB Assignment message every time (minor optimisation). */
|
||||
int rabs_count;
|
||||
int rabs_done_count;
|
||||
|
||||
unsigned int rabs_rel_count;
|
||||
unsigned int rabs_rel_done_count;
|
||||
};
|
||||
|
||||
struct osmo_tdef_state_timeout ps_rab_ass_fsm_timeouts[32] = {
|
||||
@@ -118,7 +122,7 @@ struct osmo_tdef_state_timeout ps_rab_ass_fsm_timeouts[32] = {
|
||||
/* PS_RAB_ASS_ST_WAIT_RABS_ESTABLISHED is terminated by PFCP timeouts via ps_rab_fsm */
|
||||
};
|
||||
|
||||
#define ps_rab_ass_fsm_state_chg(state) \
|
||||
#define ps_rab_ass_fsm_state_chg(fi, state) \
|
||||
osmo_tdef_fsm_inst_state_chg(fi, state, ps_rab_ass_fsm_timeouts, hnbgw_T_defs, -1)
|
||||
|
||||
static struct osmo_fsm ps_rab_ass_fsm;
|
||||
@@ -140,7 +144,7 @@ static struct ps_rab_ass *ps_rab_ass_alloc(struct hnbgw_context_map *map)
|
||||
};
|
||||
fi->priv = rab_ass;
|
||||
|
||||
llist_add_tail(&rab_ass->entry, &map->ps_rab_ass);
|
||||
llist_add_tail(&rab_ass->entry, &map->ps_rab_ass_list);
|
||||
return rab_ass;
|
||||
}
|
||||
|
||||
@@ -219,12 +223,9 @@ int hnbgw_gtpmap_rx_rab_ass_req(struct hnbgw_context_map *map, struct msgb *rana
|
||||
{
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies = &message->msg.raB_AssignmentRequestIEs;
|
||||
int i;
|
||||
|
||||
struct ps_rab_ass *rab_ass;
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
rab_ass = ps_rab_ass_alloc(map);
|
||||
|
||||
talloc_steal(rab_ass, message);
|
||||
rab_ass->ranap_rab_ass_req_message = message;
|
||||
/* Now rab_ass owns message and will clean it up */
|
||||
@@ -234,33 +235,39 @@ int hnbgw_gtpmap_rx_rab_ass_req(struct hnbgw_context_map *map, struct msgb *rana
|
||||
goto no_rab;
|
||||
}
|
||||
|
||||
/* Make sure we indeed deal with a setup-or-modify list */
|
||||
if (!(ies->presenceMask & RAB_ASSIGNMENTREQUESTIES_RANAP_RAB_SETUPORMODIFYLIST_PRESENT)) {
|
||||
LOG_MAP(map, DLPFCP, LOGL_ERROR, "RANAP PS RAB AssignmentRequest lacks setup-or-modify list\n");
|
||||
goto no_rab;
|
||||
/* setup-or-modify list */
|
||||
if (ies->presenceMask & RAB_ASSIGNMENTREQUESTIES_RANAP_RAB_SETUPORMODIFYLIST_PRESENT) {
|
||||
/* Multiple RABs may be set up, assemble in list map->ps_rab_list. */
|
||||
for (i = 0; i < ies->raB_SetupOrModifyList.list.count; i++) {
|
||||
RANAP_ProtocolIE_ContainerPair_t *protocol_ie_container_pair;
|
||||
RANAP_ProtocolIE_FieldPair_t *protocol_ie_field_pair;
|
||||
|
||||
protocol_ie_container_pair = ies->raB_SetupOrModifyList.list.array[i];
|
||||
protocol_ie_field_pair = protocol_ie_container_pair->list.array[0];
|
||||
if (!protocol_ie_field_pair)
|
||||
goto no_rab;
|
||||
if (protocol_ie_field_pair->id != RANAP_ProtocolIE_ID_id_RAB_SetupOrModifyItem)
|
||||
goto no_rab;
|
||||
|
||||
if (ps_rab_setup_core_remote(rab_ass, protocol_ie_field_pair))
|
||||
goto no_rab;
|
||||
}
|
||||
}
|
||||
|
||||
/* Multiple RABs may be set up, assemble in list rab_ass->ps_rabs. */
|
||||
for (i = 0; i < ies->raB_SetupOrModifyList.list.count; i++) {
|
||||
RANAP_ProtocolIE_ContainerPair_t *protocol_ie_container_pair;
|
||||
RANAP_ProtocolIE_FieldPair_t *protocol_ie_field_pair;
|
||||
/* Note: ReleaseList is handled at RAB Assign Response time. */
|
||||
|
||||
protocol_ie_container_pair = ies->raB_SetupOrModifyList.list.array[i];
|
||||
protocol_ie_field_pair = protocol_ie_container_pair->list.array[0];
|
||||
if (!protocol_ie_field_pair)
|
||||
goto no_rab;
|
||||
if (protocol_ie_field_pair->id != RANAP_ProtocolIE_ID_id_RAB_SetupOrModifyItem)
|
||||
goto no_rab;
|
||||
|
||||
if (ps_rab_setup_core_remote(rab_ass, protocol_ie_field_pair))
|
||||
goto no_rab;
|
||||
if (rab_ass->rabs_count > 0) {
|
||||
/* Got all RABs' state and their Core side GTP info in map->ps_rab_list.
|
||||
* For each, a ps_rab_fsm has been started and each will call back with
|
||||
* PS_RAB_ASS_EV_LOCAL_F_TEIDS_RX or PS_RAB_ASS_EV_RAB_FAIL. */
|
||||
return ps_rab_ass_fsm_state_chg(rab_ass->fi, PS_RAB_ASS_ST_WAIT_LOCAL_F_TEIDS);
|
||||
}
|
||||
|
||||
/* Got all RABs' state and their Core side GTP info in map->ps_rabs. For each, a ps_rab_fsm has been started and
|
||||
* each will call back with PS_RAB_ASS_EV_LOCAL_F_TEIDS_RX or PS_RAB_ASS_EV_RAB_FAIL. */
|
||||
fi = rab_ass->fi;
|
||||
return ps_rab_ass_fsm_state_chg(PS_RAB_ASS_ST_WAIT_LOCAL_F_TEIDS);
|
||||
|
||||
/* No Setup-or-modify, only Release RABs. Forward the message as is,
|
||||
* RABs will be released at RAB ASS Response time. */
|
||||
map_rua_dispatch(map, MAP_RUA_EV_TX_DIRECT_TRANSFER, ranap_msg);
|
||||
osmo_fsm_inst_term(rab_ass->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
return 0;
|
||||
no_rab:
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return -1;
|
||||
@@ -294,7 +301,7 @@ static void ps_rab_ass_fsm_wait_local_f_teids(struct osmo_fsm_inst *fi, uint32_t
|
||||
/* See whether all information is in so that we can forward the modified RAB Assignment Request to RUA. */
|
||||
static void ps_rab_ass_req_check_local_f_teids(struct ps_rab_ass *rab_ass)
|
||||
{
|
||||
struct ps_rab *rab;
|
||||
struct ps_rab *rab = NULL;
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies = &rab_ass->ranap_rab_ass_req_message->msg.raB_AssignmentRequestIEs;
|
||||
int i;
|
||||
struct msgb *msg;
|
||||
@@ -346,6 +353,12 @@ continue_cleanloop:
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyItemFirst, &first);
|
||||
}
|
||||
|
||||
if (!rab) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Lookup PS RAB Assignment Request failed\n");
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send the modified RAB Assignment Request to the hNodeB, wait for the RAB Assignment Response */
|
||||
msg = ranap_rab_ass_req_encode(ies);
|
||||
if (!msg) {
|
||||
@@ -407,70 +420,127 @@ int hnbgw_gtpmap_rx_rab_ass_resp(struct hnbgw_context_map *map, struct msgb *ran
|
||||
* each other, we are able to handle mismatching RAB IDs in request and response messages.
|
||||
*/
|
||||
|
||||
RANAP_RAB_AssignmentResponseIEs_t *ies = &message->msg.raB_AssignmentResponseIEs;
|
||||
int rc;
|
||||
int i;
|
||||
struct ps_rab_ass *rab_ass;
|
||||
struct osmo_fsm_inst *fi;
|
||||
RANAP_RAB_AssignmentResponseIEs_t *ies;
|
||||
|
||||
/* Make sure we indeed deal with a setup-or-modify list */
|
||||
ies = &message->msg.raB_AssignmentResponseIEs;
|
||||
if (!(ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_SETUPORMODIFIEDLIST_PRESENT)) {
|
||||
LOG_MAP(map, DRUA, LOGL_ERROR, "RANAP PS RAB AssignmentResponse lacks setup-or-modify list\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rab_ass = ps_rab_ass_alloc(map);
|
||||
|
||||
talloc_steal(rab_ass, message);
|
||||
rab_ass->ranap_rab_ass_resp_message = message;
|
||||
|
||||
talloc_steal(rab_ass, ranap_msg);
|
||||
rab_ass->ranap_rab_ass_resp_msgb = ranap_msg;
|
||||
/* Now rab_ass owns message and will clean it up */
|
||||
|
||||
if (!osmo_pfcp_cp_peer_is_associated(g_hnbgw->pfcp.cp_peer)) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "PFCP is not associated, cannot set up GTP mapping\n");
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return -1;
|
||||
goto no_rab;
|
||||
}
|
||||
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_NOTICE, "PS RAB-AssignmentResponse received, updating RABs\n");
|
||||
/* setup-or-modify list */
|
||||
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_SETUPORMODIFIEDLIST_PRESENT) {
|
||||
|
||||
/* Multiple RABs may be set up, bump matching FSMs in list rab_ass->ps_rabs. */
|
||||
for (i = 0; i < ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.count; i++) {
|
||||
RANAP_IE_t *list_ie;
|
||||
RANAP_RAB_SetupOrModifiedItemIEs_t item_ies;
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_INFO, "PS RAB-AssignmentResponse received, updating RABs\n");
|
||||
|
||||
list_ie = ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[i];
|
||||
if (!list_ie)
|
||||
continue;
|
||||
/* Multiple RABs may be set up, bump matching FSMs in list map->ps_rab_list. */
|
||||
for (i = 0; i < ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.count; i++) {
|
||||
RANAP_IE_t *list_ie;
|
||||
RANAP_RAB_SetupOrModifiedItemIEs_t item_ies;
|
||||
|
||||
rc = ranap_decode_rab_setupormodifieditemies_fromlist(&item_ies,
|
||||
&list_ie->value);
|
||||
if (rc < 0) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Failed to decode PS RAB-AssignmentResponse"
|
||||
" SetupOrModifiedItemIEs with list index %d\n", i);
|
||||
goto continue_cleanloop;
|
||||
}
|
||||
list_ie = ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[i];
|
||||
if (!list_ie)
|
||||
continue;
|
||||
|
||||
if (ps_rab_setup_access_remote(rab_ass, &item_ies.raB_SetupOrModifiedItem))
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Failed to apply PS RAB-AssignmentResponse"
|
||||
" SetupOrModifiedItemIEs with list index %d\n", i);
|
||||
rab_ass->rabs_count++;
|
||||
rc = ranap_decode_rab_setupormodifieditemies_fromlist(&item_ies,
|
||||
&list_ie->value);
|
||||
if (rc < 0) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Failed to decode PS RAB-AssignmentResponse"
|
||||
" SetupOrModifiedItemIEs with list index %d\n", i);
|
||||
goto continue_cleanloop;
|
||||
}
|
||||
|
||||
if (ps_rab_setup_access_remote(rab_ass, &item_ies.raB_SetupOrModifiedItem))
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Failed to apply PS RAB-AssignmentResponse"
|
||||
" SetupOrModifiedItemIEs with list index %d\n", i);
|
||||
rab_ass->rabs_count++;
|
||||
|
||||
continue_cleanloop:
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, &item_ies);
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, &item_ies);
|
||||
}
|
||||
}
|
||||
|
||||
/* Got all RABs' state and updated their Access side GTP info in map->ps_rabs. For each RAB ID, the matching
|
||||
* ps_rab_fsm has been instructed to tell the UPF about the Access Remote GTP F-TEID. Each will call back with
|
||||
* PS_RAB_ASS_EV_RAB_ESTABLISHED or PS_RAB_ASS_EV_RAB_FAIL. */
|
||||
fi = rab_ass->fi;
|
||||
return ps_rab_ass_fsm_state_chg(PS_RAB_ASS_ST_WAIT_RABS_ESTABLISHED);
|
||||
/* release list */
|
||||
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_RELEASEDLIST_PRESENT) {
|
||||
/* Multiple RABs may be set up, assemble in list map->ps_rab_list. */
|
||||
RANAP_RAB_ReleasedList_t *r_list = &ies->raB_ReleasedList;
|
||||
for (i = 0; i < r_list->raB_ReleasedList_ies.list.count; i++) {
|
||||
RANAP_IE_t *released_list_ie = r_list->raB_ReleasedList_ies.list.array[i];
|
||||
RANAP_RAB_ReleasedItemIEs_t _rab_rel_item_ies = {};
|
||||
RANAP_RAB_ReleasedItemIEs_t *rab_rel_item_ies = &_rab_rel_item_ies;
|
||||
RANAP_RAB_ReleasedItem_t *rab_rel_item;
|
||||
uint8_t rab_id;
|
||||
struct ps_rab *rab;
|
||||
|
||||
if (!released_list_ie)
|
||||
goto no_rab;
|
||||
if (released_list_ie->id != RANAP_ProtocolIE_ID_id_RAB_ReleasedItem)
|
||||
goto no_rab;
|
||||
|
||||
rc = ranap_decode_rab_releaseditemies_fromlist(rab_rel_item_ies, &released_list_ie->value);
|
||||
if (rc < 0) {
|
||||
LOG_MAP(map, DLPFCP, LOGL_NOTICE, "Rx RAB ASS REQ (REL): Failed decoding ReleaseItem %u\n", i);
|
||||
goto no_rab;
|
||||
}
|
||||
|
||||
rab_rel_item = &rab_rel_item_ies->raB_ReleasedItem;
|
||||
/* The RAB-ID is defined as a bitstring with a size of 8 (1 byte),
|
||||
* See also RANAP-IEs.asn, RAB-ID ::= BIT STRING (SIZE (8)) */
|
||||
rab_id = rab_rel_item->rAB_ID.buf[0];
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_ReleasedItem, rab_rel_item_ies);
|
||||
|
||||
rab = ps_rab_get(map, rab_id);
|
||||
if (rab) {
|
||||
rab_ass->rabs_rel_count++;
|
||||
ps_rab_release(rab, rab_ass->fi);
|
||||
} else {
|
||||
LOG_MAP(map, DLPFCP, LOGL_NOTICE, "Rx RAB ASS REQ (REL) for unknown rab_id %u\n", rab_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rab_ass->rabs_count > 0 || rab_ass->rabs_rel_count > 0) {
|
||||
/* Got all RABs' state and updated their Access side GTP info in map->ps_rab_list.
|
||||
* For each RAB ID, the matching ps_rab_fsm has been instructed to tell the UPF about
|
||||
* the Access Remote GTP F-TEID. Each will call back with PS_RAB_ASS_EV_RAB_ESTABLISHED
|
||||
* or PS_RAB_ASS_EV_RAB_FAIL. */
|
||||
return ps_rab_ass_fsm_state_chg(rab_ass->fi, PS_RAB_ASS_ST_WAIT_RABS_ESTABLISHED);
|
||||
}
|
||||
|
||||
/* No Setup-or-modify nor Release RABs. Forward the message as is. */
|
||||
rc = map_sccp_dispatch(map, MAP_SCCP_EV_TX_DATA_REQUEST, ranap_msg);
|
||||
if (rc < 0) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Sending RANAP PS RAB-AssignmentResponse failed\n");
|
||||
goto no_rab;
|
||||
}
|
||||
/* The request message has been forwarded. We are done. */
|
||||
osmo_fsm_inst_term(rab_ass->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
return 0;
|
||||
|
||||
no_rab:
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ps_rab_ass_resp_send_if_ready(struct ps_rab_ass *rab_ass);
|
||||
static void ps_rab_ass_resp_send_if_ready_quick(struct ps_rab_ass *rab_ass)
|
||||
{
|
||||
/* some RABs are still pending, postpone going through the message until all are done. */
|
||||
if (rab_ass->rabs_done_count < rab_ass->rabs_count)
|
||||
return;
|
||||
if (rab_ass->rabs_rel_done_count < rab_ass->rabs_rel_count)
|
||||
return;
|
||||
ps_rab_ass_resp_send_if_ready(rab_ass);
|
||||
}
|
||||
|
||||
static void ps_rab_ass_fsm_wait_rabs_established(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
@@ -479,12 +549,13 @@ static void ps_rab_ass_fsm_wait_rabs_established(struct osmo_fsm_inst *fi, uint3
|
||||
switch (event) {
|
||||
case PS_RAB_ASS_EV_RAB_ESTABLISHED:
|
||||
rab_ass->rabs_done_count++;
|
||||
if (rab_ass->rabs_done_count < rab_ass->rabs_count) {
|
||||
/* some RABs are still pending, postpone going through the message until all are done. */
|
||||
return;
|
||||
}
|
||||
/* All RABs have succeeded, ready to forward */
|
||||
ps_rab_ass_resp_send_if_ready(rab_ass);
|
||||
ps_rab_ass_resp_send_if_ready_quick(rab_ass);
|
||||
return;
|
||||
|
||||
case PS_RAB_ASS_EV_RAB_RELEASED:
|
||||
rab_ass->rabs_rel_done_count++;
|
||||
ps_rab_ass_resp_send_if_ready_quick(rab_ass);
|
||||
return;
|
||||
|
||||
case PS_RAB_ASS_EV_RAB_FAIL:
|
||||
@@ -595,14 +666,14 @@ continue_cleanloop:
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_NOTICE, "Sending RANAP PS RAB-AssignmentResponse with mapped GTP info\n");
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_INFO, "Sending RANAP PS RAB-AssignmentResponse with mapped GTP info\n");
|
||||
|
||||
rc = map_sccp_dispatch(rab_ass->map, MAP_SCCP_EV_TX_DATA_REQUEST, msg);
|
||||
if (rc < 0) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Sending RANAP PS RAB-AssignmentResponse failed\n");
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The request message has been forwarded. We are done. */
|
||||
osmo_fsm_inst_term(rab_ass->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
@@ -612,14 +683,14 @@ static void ps_rab_ass_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_
|
||||
struct ps_rab_ass *rab_ass = fi->priv;
|
||||
struct ps_rab *rab;
|
||||
|
||||
llist_for_each_entry(rab, &rab_ass->map->ps_rabs, entry) {
|
||||
llist_for_each_entry(rab, &rab_ass->map->ps_rab_list, entry) {
|
||||
if (rab->req_fi == fi)
|
||||
rab->req_fi = NULL;
|
||||
if (rab->resp_fi == fi)
|
||||
rab->resp_fi = NULL;
|
||||
}
|
||||
|
||||
/* remove from map->ps_rab_ass */
|
||||
/* remove from map->ps_rab_ass_list */
|
||||
llist_del(&rab_ass->entry);
|
||||
}
|
||||
|
||||
@@ -627,10 +698,10 @@ void hnbgw_gtpmap_release(struct hnbgw_context_map *map)
|
||||
{
|
||||
struct ps_rab_ass *rab_ass, *next;
|
||||
struct ps_rab *rab, *next2;
|
||||
llist_for_each_entry_safe(rab, next2, &map->ps_rabs, entry) {
|
||||
ps_rab_release(rab);
|
||||
llist_for_each_entry_safe(rab, next2, &map->ps_rab_list, entry) {
|
||||
ps_rab_release(rab, NULL);
|
||||
}
|
||||
llist_for_each_entry_safe(rab_ass, next, &map->ps_rab_ass, entry) {
|
||||
llist_for_each_entry_safe(rab_ass, next, &map->ps_rab_ass_list, entry) {
|
||||
osmo_fsm_inst_term(rab_ass->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
}
|
||||
@@ -658,6 +729,7 @@ static const struct osmo_fsm_state ps_rab_ass_fsm_states[] = {
|
||||
.action = ps_rab_ass_fsm_wait_rabs_established,
|
||||
.in_event_mask = 0
|
||||
| S(PS_RAB_ASS_EV_RAB_ESTABLISHED)
|
||||
| S(PS_RAB_ASS_EV_RAB_RELEASED)
|
||||
| S(PS_RAB_ASS_EV_RAB_FAIL)
|
||||
,
|
||||
},
|
||||
|
||||
@@ -107,7 +107,7 @@ static struct ps_rab *ps_rab_alloc(struct hnbgw_context_map *map, uint8_t rab_id
|
||||
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&rab->use_count, PS_RAB_USE_ACTIVE, 1) == 0);
|
||||
|
||||
llist_add_tail(&rab->entry, &map->ps_rabs);
|
||||
llist_add_tail(&rab->entry, &map->ps_rab_list);
|
||||
return rab;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ struct ps_rab *ps_rab_find_by_seid(uint64_t seid, bool is_cp_seid)
|
||||
struct hnbgw_context_map *map;
|
||||
llist_for_each_entry(map, &hnb->map_list, hnb_list) {
|
||||
struct ps_rab *rab;
|
||||
llist_for_each_entry(rab, &map->ps_rabs, entry) {
|
||||
llist_for_each_entry(rab, &map->ps_rab_list, entry) {
|
||||
uint64_t rab_seid = is_cp_seid ? rab->cp_seid : rab->up_f_seid.seid;
|
||||
if (rab_seid == seid)
|
||||
return rab;
|
||||
@@ -154,7 +154,7 @@ static struct osmo_pfcp_msg *ps_rab_new_pfcp_msg_req(struct ps_rab *rab, enum os
|
||||
struct ps_rab *ps_rab_get(struct hnbgw_context_map *map, uint8_t rab_id)
|
||||
{
|
||||
struct ps_rab *rab;
|
||||
llist_for_each_entry(rab, &map->ps_rabs, entry) {
|
||||
llist_for_each_entry(rab, &map->ps_rab_list, entry) {
|
||||
if (rab->rab_id != rab_id)
|
||||
continue;
|
||||
return rab;
|
||||
@@ -173,7 +173,7 @@ void ps_rab_failure(struct ps_rab *rab)
|
||||
osmo_fsm_inst_dispatch(rab->req_fi, PS_RAB_ASS_EV_RAB_FAIL, rab);
|
||||
if (rab->resp_fi)
|
||||
osmo_fsm_inst_dispatch(rab->resp_fi, PS_RAB_ASS_EV_RAB_FAIL, rab);
|
||||
ps_rab_release(rab);
|
||||
ps_rab_release(rab, NULL);
|
||||
}
|
||||
|
||||
struct ps_rab *ps_rab_start(struct hnbgw_context_map *map, uint8_t rab_id,
|
||||
@@ -320,7 +320,7 @@ static void ps_rab_fsm_wait_pfcp_est_resp_onenter(struct osmo_fsm_inst *fi, uint
|
||||
m->h.seid = 0;
|
||||
|
||||
/* Make a new CP-SEID, our local reference for the PFCP session. */
|
||||
rab->cp_seid = osmo_pfcp_next_seid(&g_hnbgw->pfcp.cp_peer->next_seid_state);
|
||||
rab->cp_seid = osmo_pfcp_cp_peer_next_seid(g_hnbgw->pfcp.cp_peer);
|
||||
cp_f_seid = (struct osmo_pfcp_ie_f_seid){
|
||||
.seid = rab->cp_seid,
|
||||
};
|
||||
@@ -672,6 +672,8 @@ static int ps_rab_fsm_use_cb(struct osmo_use_count_entry *e, int32_t old_use_cou
|
||||
static void ps_rab_fsm_wait_use_count_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct ps_rab *rab = fi->priv;
|
||||
if (rab->resp_fi)
|
||||
osmo_fsm_inst_dispatch(rab->resp_fi, PS_RAB_ASS_EV_RAB_RELEASED, rab);
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&rab->use_count, PS_RAB_USE_ACTIVE, -1) == 0);
|
||||
}
|
||||
|
||||
@@ -692,7 +694,7 @@ static void ps_rab_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event,
|
||||
|
||||
static void ps_rab_forget_map(struct ps_rab *rab)
|
||||
{
|
||||
/* remove from map->ps_rabs */
|
||||
/* remove from map->ps_rab_list */
|
||||
if (rab->map)
|
||||
llist_del(&rab->entry);
|
||||
rab->map = NULL;
|
||||
@@ -704,10 +706,12 @@ static void ps_rab_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_caus
|
||||
ps_rab_forget_map(rab);
|
||||
}
|
||||
|
||||
void ps_rab_release(struct ps_rab *rab)
|
||||
/* notify_fi can be NULL, in which case no event PS_RAB_ASS_EV_RAB_RELEASED event will be dispatched */
|
||||
void ps_rab_release(struct ps_rab *rab, struct osmo_fsm_inst *notify_fi)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = rab->fi;
|
||||
ps_rab_forget_map(rab);
|
||||
rab->resp_fi = notify_fi;
|
||||
switch (fi->state) {
|
||||
case PS_RAB_ST_RX_CORE_REMOTE_F_TEID:
|
||||
/* No session requested yet. Nothing to be deleted. */
|
||||
|
||||
@@ -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" },
|
||||
{ }
|
||||
};
|
||||
|
||||
107
src/osmo-hnbgw/umts_cell_id.c
Normal file
107
src/osmo-hnbgw/umts_cell_id.c
Normal file
@@ -0,0 +1,107 @@
|
||||
/* UMTS Cell ID */
|
||||
|
||||
/* (C) 2015,2024 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2016-2025 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/jhash.h>
|
||||
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
|
||||
#include <osmocom/hnbgw/umts_cell_id.h>
|
||||
|
||||
int umts_cell_id_to_str_buf(char *buf, size_t buflen, const struct umts_cell_id *ucid)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
OSMO_STRBUF_APPEND_NOLEN(sb, osmo_plmn_name_buf, &ucid->plmn);
|
||||
OSMO_STRBUF_PRINTF(sb, "-L%u-R%u-S%u-C%u", ucid->lac, ucid->rac, ucid->sac, ucid->cid);
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
char *umts_cell_id_to_str_c(void *ctx, const struct umts_cell_id *ucid)
|
||||
{
|
||||
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", umts_cell_id_to_str_buf, ucid)
|
||||
}
|
||||
|
||||
const char *umts_cell_id_to_str(const struct umts_cell_id *ucid)
|
||||
{
|
||||
return umts_cell_id_to_str_c(OTC_SELECT, ucid);
|
||||
}
|
||||
|
||||
/* Useful to index a hash table by struct umts_cell_id. */
|
||||
uint32_t umts_cell_id_hash(const struct umts_cell_id *ucid)
|
||||
{
|
||||
return osmo_jhash(ucid, sizeof(*ucid), 0x423423);
|
||||
}
|
||||
|
||||
/* parse a string representation of an umts_cell_id into its decoded representation */
|
||||
int umts_cell_id_from_str(struct umts_cell_id *ucid, const char *instr)
|
||||
{
|
||||
int rc;
|
||||
char buf[4];
|
||||
const char *pos = instr;
|
||||
const char *end;
|
||||
|
||||
/* We want to use struct umts_cell_id as hashtable key. If it ever happens to contain any padding bytes, make
|
||||
* sure everything is deterministically zero. */
|
||||
memset(ucid, 0, sizeof(*ucid));
|
||||
|
||||
/* read MCC */
|
||||
end = strchr(pos, '-');
|
||||
if (!end || end <= pos || (end - pos) >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
osmo_strlcpy(buf, pos, end - pos + 1);
|
||||
if (osmo_mcc_from_str(buf, &ucid->plmn.mcc))
|
||||
return -EINVAL;
|
||||
pos = end + 1;
|
||||
|
||||
/* read MNC -- here the number of leading zeros matters. */
|
||||
end = strchr(pos, '-');
|
||||
if (!end || end == pos || (end - pos) >= sizeof(buf))
|
||||
return -EINVAL;
|
||||
osmo_strlcpy(buf, pos, end - pos + 1);
|
||||
if (osmo_mnc_from_str(buf, &ucid->plmn.mnc, &ucid->plmn.mnc_3_digits))
|
||||
return -EINVAL;
|
||||
pos = end + 1;
|
||||
|
||||
/* parse the rest, where leading zeros do not matter */
|
||||
rc = sscanf(pos, "L%" SCNu16 "-R%" SCNu8 "-S%" SCNu16 "-C%" SCNu32 "",
|
||||
&ucid->lac, &ucid->rac, &ucid->sac, &ucid->cid);
|
||||
if (rc < 0)
|
||||
return -errno;
|
||||
|
||||
if (rc != 4)
|
||||
return -EINVAL;
|
||||
|
||||
if (ucid->lac == 0 || ucid->lac == 0xffff)
|
||||
return -EINVAL;
|
||||
|
||||
/* CellIdentity in the ASN.1 syntax is a bit-string of 28 bits length */
|
||||
if (ucid->cid >= (1 << 28))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -134,8 +134,23 @@ OsmoHNBGW(config)# sgsn ?
|
||||
OsmoHNBGW(config)# ### The config file has no 'msc' or 'sgsn', so defaults have been set up
|
||||
OsmoHNBGW(config)# show running-config
|
||||
...
|
||||
cs7 instance 0
|
||||
point-code 0.23.5
|
||||
...
|
||||
sccp-address addr-dyn-msc-default
|
||||
routing-indicator PC
|
||||
point-code 0.23.1
|
||||
subsystem-number 142
|
||||
sccp-address addr-dyn-sgsn-default
|
||||
routing-indicator PC
|
||||
point-code 0.23.4
|
||||
...
|
||||
msc 0
|
||||
name msc-0
|
||||
remote-addr addr-dyn-msc-default
|
||||
sgsn 0
|
||||
name sgsn-0
|
||||
remote-addr addr-dyn-sgsn-default
|
||||
...
|
||||
|
||||
OsmoHNBGW(config)# msc 1
|
||||
@@ -224,9 +239,15 @@ OsmoHNBGW(config)# ### Just by entering the nodes above, 'msc 1' and 'sgsn 1' no
|
||||
OsmoHNBGW(config)# show running-config
|
||||
...
|
||||
msc 0
|
||||
name msc-0
|
||||
remote-addr addr-dyn-msc-default
|
||||
msc 1
|
||||
name msc-1
|
||||
sgsn 0
|
||||
name sgsn-0
|
||||
remote-addr addr-dyn-sgsn-default
|
||||
sgsn 1
|
||||
name sgsn-1
|
||||
...
|
||||
|
||||
OsmoHNBGW(config)# ### Add {msc,sgsn}x{2,3}
|
||||
@@ -252,18 +273,28 @@ OsmoHNBGW(config-sgsn)# exit
|
||||
OsmoHNBGW(config)# show running-config
|
||||
...
|
||||
msc 0
|
||||
name msc-0
|
||||
remote-addr addr-dyn-msc-default
|
||||
msc 1
|
||||
name msc-1
|
||||
msc 2
|
||||
name msc-2
|
||||
remote-addr addr-msc2
|
||||
no allow-attach
|
||||
msc 3
|
||||
name msc-3
|
||||
remote-addr addr-msc3
|
||||
allow-emergency
|
||||
sgsn 0
|
||||
name sgsn-0
|
||||
remote-addr addr-dyn-sgsn-default
|
||||
sgsn 1
|
||||
name sgsn-1
|
||||
sgsn 2
|
||||
name sgsn-2
|
||||
remote-addr addr-sgsn2
|
||||
sgsn 3
|
||||
name sgsn-3
|
||||
remote-addr addr-sgsn3
|
||||
...
|
||||
|
||||
@@ -282,16 +313,26 @@ OsmoHNBGW(config-sgsn)# exit
|
||||
OsmoHNBGW(config)# show running-config
|
||||
...
|
||||
msc 0
|
||||
name msc-0
|
||||
remote-addr addr-dyn-msc-default
|
||||
msc 1
|
||||
name msc-1
|
||||
msc 2
|
||||
name msc-2
|
||||
remote-addr addr-msc2
|
||||
msc 3
|
||||
name msc-3
|
||||
remote-addr addr-msc4
|
||||
sgsn 0
|
||||
name sgsn-0
|
||||
remote-addr addr-dyn-sgsn-default
|
||||
sgsn 1
|
||||
name sgsn-1
|
||||
sgsn 2
|
||||
name sgsn-2
|
||||
remote-addr addr-sgsn4
|
||||
sgsn 3
|
||||
name sgsn-3
|
||||
remote-addr addr-sgsn3
|
||||
...
|
||||
|
||||
@@ -321,18 +362,26 @@ OsmoHNBGW(config-hnbgw)# show running-config
|
||||
hnbgw
|
||||
...
|
||||
msc 0
|
||||
name msc-0
|
||||
remote-addr addr-msc0
|
||||
msc 1
|
||||
name msc-1
|
||||
msc 2
|
||||
name msc-2
|
||||
remote-addr addr-msc2
|
||||
msc 3
|
||||
name msc-3
|
||||
remote-addr addr-msc4
|
||||
sgsn 0
|
||||
name sgsn-0
|
||||
remote-addr addr-sgsn0
|
||||
sgsn 1
|
||||
name sgsn-1
|
||||
sgsn 2
|
||||
name sgsn-2
|
||||
remote-addr addr-sgsn4
|
||||
sgsn 3
|
||||
name sgsn-3
|
||||
remote-addr addr-sgsn3
|
||||
...
|
||||
|
||||
@@ -348,3 +397,36 @@ hnbgw
|
||||
...
|
||||
msc 0
|
||||
...
|
||||
|
||||
OsmoHNBGW(config-hnbgw)# exit
|
||||
OsmoHNBGW(config)# ### Apply all SCCP changes:
|
||||
OsmoHNBGW(config)# apply sccp
|
||||
OsmoHNBGW(config)# show running-config
|
||||
...
|
||||
hnbgw
|
||||
...
|
||||
msc 0
|
||||
name msc-0
|
||||
remote-addr addr-msc0
|
||||
msc 1
|
||||
name msc-1
|
||||
remote-addr addr-dyn-msc-default
|
||||
msc 2
|
||||
name msc-2
|
||||
remote-addr addr-msc2
|
||||
msc 3
|
||||
name msc-3
|
||||
remote-addr addr-msc4
|
||||
sgsn 0
|
||||
name sgsn-0
|
||||
remote-addr addr-sgsn0
|
||||
sgsn 1
|
||||
name sgsn-1
|
||||
remote-addr addr-dyn-sgsn-default
|
||||
sgsn 2
|
||||
name sgsn-2
|
||||
remote-addr addr-sgsn4
|
||||
sgsn 3
|
||||
name sgsn-3
|
||||
remote-addr addr-sgsn3
|
||||
...
|
||||
|
||||
@@ -32,7 +32,6 @@ cs7 instance 1
|
||||
hnbgw
|
||||
iuh
|
||||
local-ip 0.0.0.0
|
||||
hnbap-allow-tmsi 1
|
||||
mgw 0
|
||||
remote-ip 127.0.0.1
|
||||
local-port 2729
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -24,15 +24,18 @@
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/ranap/iu_helpers.h>
|
||||
#include <osmocom/hnbgw/ranap_rab_ass.h>
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/ranap_common_cn.h>
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnb.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/ranap_rab_ass.h>
|
||||
|
||||
static void *msgb_ctx;
|
||||
extern void *talloc_asn1_ctx;
|
||||
|
||||
|
||||
@@ -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
|
||||
163
tests/umts_cell_id/umts_cell_id_test.c
Normal file
163
tests/umts_cell_id/umts_cell_id_test.c
Normal file
@@ -0,0 +1,163 @@
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
|
||||
#include <osmocom/hnbgw/umts_cell_id.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-R255-S65535-C268435455",
|
||||
.id = {
|
||||
.plmn = {
|
||||
.mcc = 999,
|
||||
.mnc = 999,
|
||||
.mnc_3_digits = true,
|
||||
},
|
||||
.lac = 65534,
|
||||
.rac = 255,
|
||||
.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-R256-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-R255-S65535-C268435455"
|
||||
-> umts_cell_id_from_str(): ok
|
||||
-> umts_cell_id_to_str_buf(): ok
|
||||
-> "999-999-L65534-R255-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