mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw.git
synced 2025-11-03 05:23:23 +00:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3c7f750a2 | ||
|
|
d41112fbcc | ||
|
|
87ecf69b55 | ||
|
|
07d01d50a5 | ||
|
|
a08b8a595a | ||
|
|
28619961a9 | ||
|
|
9bc7649b95 | ||
|
|
a1c8653bf9 | ||
|
|
76c4203552 | ||
|
|
a7fcbe100c | ||
|
|
61021881ac | ||
|
|
0589c3ecf1 | ||
|
|
0e03070789 | ||
|
|
e62af4d46a | ||
|
|
bef2c345df | ||
|
|
b9be0ea93e | ||
|
|
bbad8dec36 | ||
|
|
c923d19b7b | ||
|
|
5f19597b02 | ||
|
|
1906a30ca9 | ||
|
|
12bc4afab3 | ||
|
|
25cd41f3b5 | ||
|
|
55239c2cca | ||
|
|
3bf5395102 | ||
|
|
1de2091515 | ||
|
|
d046306b63 | ||
|
|
6bcd615d10 | ||
|
|
f9825cbd4a | ||
|
|
930ed702b6 | ||
|
|
96c712570a | ||
|
|
fe7c34737d | ||
|
|
d3382ae952 | ||
|
|
c971c657c5 | ||
|
|
eadf523393 | ||
|
|
419e832473 | ||
|
|
d28771a1b5 | ||
|
|
0c5878fa9d | ||
|
|
9ea431123d | ||
|
|
b08b19c990 | ||
|
|
791babf40e | ||
|
|
d129e0c86e | ||
|
|
2dfeb1e218 | ||
|
|
0a5e2b3643 | ||
|
|
9f654da0aa | ||
|
|
f7df74fc48 | ||
|
|
a0d528ef31 | ||
|
|
d8de11b430 | ||
|
|
598ebb6943 | ||
|
|
1ce5148996 | ||
|
|
941008e785 | ||
|
|
ca2c5b9067 | ||
|
|
c5b7106f8d | ||
|
|
e6201765cf | ||
|
|
44f5a336a8 | ||
|
|
1496498713 | ||
|
|
b7ff03e5be | ||
|
|
223aeda282 | ||
|
|
a82c8d2425 | ||
|
|
05aaccc42d | ||
|
|
7eb89ec9fe | ||
|
|
44dfe698fa | ||
|
|
7893028ef6 | ||
|
|
62fb1dea61 | ||
|
|
304f7646c9 | ||
|
|
87e03208af | ||
|
|
de8b170d1a | ||
|
|
1d1839a34b | ||
|
|
8c7aae87b0 | ||
|
|
ff2fbdf998 | ||
|
|
2c91bd66a1 | ||
|
|
afbcae6366 | ||
|
|
da9d08c94e | ||
|
|
0ca9567fb2 | ||
|
|
be9ed71631 | ||
|
|
d1f4b9b9a1 | ||
|
|
b5508f98ef | ||
|
|
9e46544486 | ||
|
|
7dd3a61b57 | ||
|
|
81f1751896 | ||
|
|
e7c66defc2 | ||
|
|
f5742a3bed | ||
|
|
efe4850e75 | ||
|
|
0c465b0f68 | ||
|
|
7daa502a2d | ||
|
|
dddafa60ea | ||
|
|
60008b1457 | ||
|
|
dd29038a8f | ||
|
|
6d8e37f610 |
@@ -19,9 +19,9 @@ GIT Repository
|
||||
|
||||
You can clone from the official osmo-hnbgw.git repository using
|
||||
|
||||
git clone git://git.osmocom.org/osmo-hnbgw.git
|
||||
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw
|
||||
|
||||
There is a cgit interface at https://git.osmocom.org/osmo-hnbgw/
|
||||
There is a web interface at https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
45
configure.ac
45
configure.ac
@@ -36,14 +36,7 @@ if test "x$PKG_CONFIG_INSTALLED" = "xno"; then
|
||||
fi
|
||||
PKG_PROG_PKG_CONFIG([0.20])
|
||||
|
||||
dnl check for AX_CHECK_COMPILE_FLAG
|
||||
m4_ifdef([AX_CHECK_COMPILE_FLAG], [], [
|
||||
AC_MSG_ERROR([Please install autoconf-archive; re-run 'autoreconf -fi' for it to take effect.])
|
||||
])
|
||||
|
||||
dnl checks for libraries
|
||||
AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
|
||||
AC_SUBST(LIBRARY_DL)
|
||||
old_LIBS=$LIBS
|
||||
AC_SEARCH_LIBS([sctp_recvmsg], [sctp], [
|
||||
AC_DEFINE(HAVE_LIBSCTP, 1, [Define 1 to enable SCTP support])
|
||||
@@ -56,16 +49,26 @@ AC_SEARCH_LIBS([sctp_recvmsg], [sctp], [
|
||||
LIBS=$old_LIBS
|
||||
|
||||
PKG_CHECK_MODULES(LIBASN1C, libasn1c >= 0.9.30)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 1.1.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.3.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.7.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 1.4.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.4.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 1.4.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.11.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.2.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)
|
||||
|
||||
dnl checks for header files
|
||||
AC_HEADER_STDC
|
||||
@@ -113,13 +116,6 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])],
|
||||
CFLAGS="$saved_CFLAGS"
|
||||
AC_SUBST(SYMBOL_VISIBILITY)
|
||||
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=implicit], [CFLAGS="$CFLAGS -Werror=implicit"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=maybe-uninitialized], [CFLAGS="$CFLAGS -Werror=maybe-uninitialized"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=memset-transposed-args], [CFLAGS="$CFLAGS -Werror=memset-transposed-args"])
|
||||
AX_CHECK_COMPILE_FLAG([-Wnull-dereference], [CFLAGS="$CFLAGS -Wnull-dereference"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=sizeof-array-argument], [CFLAGS="$CFLAGS -Werror=sizeof-array-argument"])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=sizeof-pointer-memaccess], [CFLAGS="$CFLAGS -Werror=sizeof-pointer-memaccess"])
|
||||
|
||||
# Coverage build taken from WebKit's configure.in
|
||||
AC_MSG_CHECKING([whether to enable code coverage support])
|
||||
AC_ARG_ENABLE(coverage,
|
||||
@@ -154,7 +150,7 @@ if test "x$enable_ext_tests" = "xyes" ; then
|
||||
fi
|
||||
AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
|
||||
if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
|
||||
AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
|
||||
AC_MSG_ERROR([Please install https://gitea.osmocom.org/cellular-infrastructure/osmo-python-tests to run the VTY/CTRL tests.])
|
||||
fi
|
||||
fi
|
||||
AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
|
||||
@@ -233,6 +229,7 @@ AC_OUTPUT(
|
||||
src/osmo-hnbgw/Makefile
|
||||
tests/Makefile
|
||||
tests/atlocal
|
||||
tests/ranap_rab_ass/Makefile
|
||||
doc/Makefile
|
||||
doc/examples/Makefile
|
||||
doc/manuals/Makefile
|
||||
|
||||
@@ -33,8 +33,10 @@ osmo-build-dep.sh libosmocore "" --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-pfcp
|
||||
osmo-build-dep.sh libasn1c
|
||||
osmo-build-dep.sh osmo-iuh
|
||||
osmo-build-dep.sh osmo-mgw
|
||||
|
||||
# Additional configure options and depends
|
||||
CONFIG=""
|
||||
|
||||
@@ -24,7 +24,6 @@ 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: autoconf-archive
|
||||
BuildRequires: automake >= 1.9
|
||||
BuildRequires: libtool >= 2
|
||||
BuildRequires: lksctp-tools-devel
|
||||
@@ -33,18 +32,20 @@ BuildRequires: pkgconfig >= 0.20
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: pkgconfig(libcrypto) >= 0.9.5
|
||||
BuildRequires: pkgconfig(libosmo-netif) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmo-sigtran) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmoabis) >= 1.2.0
|
||||
BuildRequires: pkgconfig(libosmotrau) >= 1.2.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmogb) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.6.0
|
||||
BuildRequires: pkgconfig(libosmo-hnbap) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmo-ranap) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmo-rua) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmo-mgcp-client) >= 1.11.0
|
||||
BuildRequires: pkgconfig(libosmo-netif) >= 1.3.0
|
||||
BuildRequires: pkgconfig(libosmo-sigtran) >= 1.7.0
|
||||
BuildRequires: pkgconfig(libosmoabis) >= 1.4.0
|
||||
BuildRequires: pkgconfig(libosmotrau) >= 1.4.0
|
||||
BuildRequires: pkgconfig(libosmocore) >= 1.8.0
|
||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.8.0
|
||||
BuildRequires: pkgconfig(libosmogb) >= 1.8.0
|
||||
BuildRequires: pkgconfig(libosmogsm) >= 1.8.0
|
||||
BuildRequires: pkgconfig(libosmovty) >= 1.8.0
|
||||
BuildRequires: pkgconfig(libosmo-hnbap) >= 1.4.0
|
||||
BuildRequires: pkgconfig(libosmo-ranap) >= 1.4.0
|
||||
BuildRequires: pkgconfig(libosmo-rua) >= 1.4.0
|
||||
BuildRequires: pkgconfig(libosmo-pfcp) >= 0.2.0
|
||||
BuildRequires: pkgconfig(talloc)
|
||||
BuildRequires: pkgconfig(libasn1c) >= 0.9.30
|
||||
%{?systemd_requires}
|
||||
@@ -60,7 +61,8 @@ echo "%{version}" >.tarball-version
|
||||
autoreconf -fi
|
||||
%configure \
|
||||
--docdir=%{_docdir}/%{name} \
|
||||
--with-systemdsystemunitdir=%{_unitdir}
|
||||
--with-systemdsystemunitdir=%{_unitdir} \
|
||||
--enable-pfcp
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
@@ -90,6 +92,7 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
%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-pfcp.cfg
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-hnbgw.cfg
|
||||
%{_unitdir}/%{name}.service
|
||||
|
||||
@@ -4,6 +4,8 @@ Description=Osmocom Home Nodeb Gateway (OsmoHNBGW)
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
StateDirectory=osmocom
|
||||
WorkingDirectory=%S/osmocom
|
||||
ExecStart=/usr/bin/osmo-hnbgw -c /etc/osmocom/osmo-hnbgw.cfg
|
||||
RestartSec=2
|
||||
|
||||
|
||||
124
debian/changelog
vendored
124
debian/changelog
vendored
@@ -1,3 +1,127 @@
|
||||
osmo-hnbgw (1.4.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* hnbgw_cn.c: Guard against null ss7 ptr during init
|
||||
* cosmetic: Fix typo in log and whitespace
|
||||
* hnbgw: Log new SCTP HNB connections
|
||||
* hnb_context_release(): Make sure assigned conn is freed
|
||||
* Improve logging around hnb_context and sctp conn lifecycle
|
||||
* Change log level about conn becoming closed to NOTICE
|
||||
* hnbgw: Unregister HNB if SCTP link is restarted
|
||||
* hnbgw: Fix recent regression not closing conn upon rx of SCTP_SHUTDOWN_EVENT
|
||||
* hnbap: Accept duplicated HNB Register Request on same conn
|
||||
* hnbap: Improve logging around HNBAP HNB Register Request
|
||||
* Workaround bug where old hnb_context from same remote addr+port is kept
|
||||
* Fix handling of sctp SCTP_SHUTDOWN_EVENT notification
|
||||
* Close conn when receiving SCTP_ASSOC_CHANGE notification
|
||||
* hnb_read_cb: use local var to reduce get_ofd() calls
|
||||
* hnb_read_cb(): -EBADF must be returned if conn is freed to avoid use-after-free
|
||||
* Clear SCTP tx queue upon SCTP RESTART notification
|
||||
* Makefile.am: Drop duplicated LIBOSMOMGCPCLIENT_LIBS
|
||||
* Introduce support for libosmo-mgcp-client MGW pooling
|
||||
* doc: Include mgwpool.adoc from osmo-gsm-manuals
|
||||
* vty: Fix timers not printed when dumping running-config
|
||||
* hnbgw: Avoid allocating SCCP conn id >0x00fffffe
|
||||
* context_map: Lower loglevel to INFO when deallocating context IDs
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* mgw_fsm: move MGCP timeout to mgw_fsm_T_defs
|
||||
* fix test_ranap_rab_ass_resp_decode_encode
|
||||
* ranap_rab_ass_req_encode(): return msgb
|
||||
* add ps_rab_ass FSM to map GTP via UPF
|
||||
* reduce code dup in handle_cn_data_ind()
|
||||
* build: add --enable-pfcp, make PFCP dep optional
|
||||
* optimize: decode PS msgs only when PFCP is enabled
|
||||
* ps_rab_fsm: check use cb success
|
||||
* add example osmo-hnbgw-pfcp.cfg
|
||||
* manual: add missing bit on the MGCP port
|
||||
* manual: update overview chart with PFCP
|
||||
* manual: update IuCS/IuPS protocol stack chart
|
||||
* manual: explain the PFCP port
|
||||
* example cfg: tweak logging
|
||||
* debian,RPM: package with PFCP support
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* tests/ranap_rab_ass: fix potential NULL pointer dereferences
|
||||
* configure.ac: do not require unused dlopen
|
||||
|
||||
[ Oliver Smith ]
|
||||
* rpm spec: add osmo-hnbgw-pfcp.cfg
|
||||
|
||||
[ Daniel Willmann ]
|
||||
* Install show talloc-context VTY commands
|
||||
* hnbgw_hnbap: Fix memory leaks in HNBAP handling
|
||||
|
||||
[ Harald Welte ]
|
||||
* packate the new osmo-hnbgw-pfcp.cfg example config file
|
||||
* cosmetic: Fix typos
|
||||
* Abort if processing SCTP connection without HNB context
|
||||
* hnbgw_rx_hnb_deregister: Don't call hnb_context_release()
|
||||
* Don't process RUA messages if HNB is not registered
|
||||
* Don't permit anything but HNB (de)registration until HNB is registered
|
||||
|
||||
[ Neels Janosch Hofmeyr ]
|
||||
* fix regression: in RUA, do PFCP only when enabled
|
||||
* do not depend on libosmo-gtlv
|
||||
* drop bogus error log 'no MGW fsm'
|
||||
* fix segfault on MGCP timeout
|
||||
* fix msgb leak for RANAP RAB Ass. Req.
|
||||
* fix possible leak of ue_context on UE REGISTER error
|
||||
* fix SCCP conn leak on non-graceful HNB shutdown
|
||||
* coverity: hnbgw_rua.c: remove redundant check
|
||||
|
||||
[ Max ]
|
||||
* Set working directory in systemd service file
|
||||
* ctrl: take both address and port from vty config
|
||||
|
||||
[ arehbein ]
|
||||
* osmo-hnbgw: Transition to use of 'telnet_init_default'
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 18:05:46 +0100
|
||||
|
||||
osmo-hnbgw (1.3.0) unstable; urgency=medium
|
||||
|
||||
[ Philipp Maier ]
|
||||
* hnbgw_cn.c: fix sourcecode formatting
|
||||
* hbgw_hnbap: use osmo_plmn_from_bcd instead of gsm48_mcc_mnc_from_bcd
|
||||
* ranap_rab_ass: add decoder and rewrite functions for RAB-AssignmentRequest/Response
|
||||
* ranap_rab_ass: ensure specific rab_id
|
||||
* ranap_rab_ass: add function to check if RAB is in FailureList
|
||||
* ranap_rab_ass: add function to check if RAB is in ReleaseList
|
||||
* ranap_rab_ass_test: cosmetic: correct test function names
|
||||
* mgw_fsm: add MGW support to osmo-hnbgw
|
||||
* overview.adoc: update network diagram
|
||||
* running.adoc: explain MGW configuration
|
||||
* osmo-hnbgw.cfg: use local port 2729 as default for MGCP client
|
||||
* mgw_fsm: release call when FSM is not created
|
||||
* ranap_rab_ass: check for more than one RAB assignment req
|
||||
|
||||
[ Neels Hofmeyr ]
|
||||
* use osmo_select_main_ctx(), tweak log in handle_cn_conn_conf()
|
||||
* allow calling rua_to_scu() without data
|
||||
* tweak comments in rua_to_scu()
|
||||
* add option to send SCCP CR without payload
|
||||
* fix segfault in error handling for mgw_fi == NULL
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* mgw_fsm: Mark structs as static const
|
||||
* mgw_fsm: Improve logging
|
||||
* cosmetic: mgw_fsm: Fix typo in log
|
||||
* mgw_fsm: Change macro to not use local variables implicitly
|
||||
* mgw_fsm: Fix error path accessing uninitialized fsm ptr
|
||||
* mgw_fsm: Simplify cleanup paths
|
||||
|
||||
[ Harald Welte ]
|
||||
* update URLs (git -> https; gitea)
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 29 Jun 2022 12:42:35 +0200
|
||||
|
||||
osmo-hnbgw (1.2.1) unstable; urgency=medium
|
||||
|
||||
* Do not turn some compiler warnings into errors by default
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 11 Jan 2022 18:55:50 +0100
|
||||
|
||||
osmo-hnbgw (1.2.0) unstable; urgency=medium
|
||||
|
||||
* Initial structure + import code from osmo-iuh.git
|
||||
|
||||
23
debian/control
vendored
23
debian/control
vendored
@@ -6,7 +6,6 @@ Build-Depends: debhelper (>=9),
|
||||
dh-autoreconf,
|
||||
autotools-dev,
|
||||
autoconf,
|
||||
autoconf-archive,
|
||||
automake,
|
||||
libtool,
|
||||
pkg-config,
|
||||
@@ -14,17 +13,19 @@ Build-Depends: debhelper (>=9),
|
||||
libtalloc-dev,
|
||||
libasn1c-dev (>= 0.9.30),
|
||||
libsctp-dev,
|
||||
libosmocore-dev (>= 1.6.0),
|
||||
libosmo-sigtran-dev (>= 1.5.0),
|
||||
libosmo-abis-dev (>= 1.2.0),
|
||||
libosmo-netif-dev (>= 1.1.0),
|
||||
libosmo-hnbap-dev (>= 1.1.0),
|
||||
libosmo-ranap-dev (>= 1.1.0),
|
||||
libosmo-rua-dev (>= 1.1.0),
|
||||
osmo-gsm-manuals-dev (>= 1.2.0)
|
||||
libosmocore-dev (>= 1.8.0),
|
||||
libosmo-sigtran-dev (>= 1.7.0),
|
||||
libosmo-abis-dev (>= 1.4.0),
|
||||
libosmo-netif-dev (>= 1.3.0),
|
||||
libosmo-mgcp-client-dev (>= 1.11.0),
|
||||
libosmo-hnbap-dev (>= 1.4.0),
|
||||
libosmo-ranap-dev (>= 1.4.0),
|
||||
libosmo-rua-dev (>= 1.4.0),
|
||||
libosmo-pfcp-dev (>= 0.2.0),
|
||||
osmo-gsm-manuals-dev (>= 1.4.0)
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: git://git.osmocom.org/osmo-hnbgw.git
|
||||
Vcs-Browser: https://git.osmocom.org/osmo-hnbgw/
|
||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw
|
||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw
|
||||
Homepage: https://projects.osmocom.org/projects/osmo-hnbgw
|
||||
|
||||
Package: osmo-hnbgw
|
||||
|
||||
2
debian/copyright
vendored
2
debian/copyright
vendored
@@ -1,6 +1,6 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: osmo-hnbgw
|
||||
Source: git://git.osmocom.org/osmo-hnbgw
|
||||
Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw
|
||||
|
||||
Files: *
|
||||
Copyright: 2021 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
|
||||
1
debian/osmo-hnbgw.install
vendored
1
debian/osmo-hnbgw.install
vendored
@@ -2,3 +2,4 @@ etc/osmocom/osmo-hnbgw.cfg
|
||||
lib/systemd/system/osmo-hnbgw.service
|
||||
usr/bin/osmo-hnbgw
|
||||
usr/share/doc/osmo-hnbgw/examples/osmo-hnbgw/osmo-hnbgw.cfg usr/share/doc/osmo-hnbgw/examples
|
||||
usr/share/doc/osmo-hnbgw/examples/osmo-hnbgw/osmo-hnbgw-pfcp.cfg usr/share/doc/osmo-hnbgw/examples
|
||||
|
||||
1
debian/rules
vendored
1
debian/rules
vendored
@@ -46,6 +46,7 @@
|
||||
|
||||
# debmake generated override targets
|
||||
CONFIGURE_FLAGS += --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||
CONFIGURE_FLAGS += --enable-pfcp
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- $(CONFIGURE_FLAGS)
|
||||
#
|
||||
|
||||
21
doc/examples/osmo-hnbgw/osmo-hnbgw-pfcp.cfg
Normal file
21
doc/examples/osmo-hnbgw/osmo-hnbgw-pfcp.cfg
Normal file
@@ -0,0 +1,21 @@
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print level 1
|
||||
logging print category 1
|
||||
logging print category-hex 0
|
||||
logging print file basename last
|
||||
logging print extended-timestamp 1
|
||||
logging level set-all notice
|
||||
hnbgw
|
||||
iuh
|
||||
local-ip 0.0.0.0
|
||||
hnbap-allow-tmsi 1
|
||||
mgw 0
|
||||
remote-ip 127.0.0.1
|
||||
local-port 2729
|
||||
remote-port 2427
|
||||
reset-endpoint rtpbridge/*
|
||||
pfcp
|
||||
remote-addr 127.0.0.2
|
||||
local-addr 127.0.0.1
|
||||
@@ -1,25 +1,19 @@
|
||||
!
|
||||
! OsmoHNBGW (0) configuration saved from vty
|
||||
!!
|
||||
!
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print level 1
|
||||
logging print category 1
|
||||
logging timestamp 1
|
||||
logging print category-hex 0
|
||||
logging print file basename last
|
||||
logging print extended-timestamp 1
|
||||
logging level all debug
|
||||
logging level lglobal notice
|
||||
logging level llapd notice
|
||||
logging level linp notice
|
||||
logging level lmux notice
|
||||
logging level lmi notice
|
||||
logging level lmib notice
|
||||
logging level lsms notice
|
||||
logging level lctrl notice
|
||||
logging level lgtp notice
|
||||
logging level lstats notice
|
||||
logging level set-all notice
|
||||
hnbgw
|
||||
iuh
|
||||
local-ip 0.0.0.0
|
||||
hnbap-allow-tmsi 1
|
||||
mgw 0
|
||||
remote-ip 127.0.0.1
|
||||
local-port 2729
|
||||
remote-port 2427
|
||||
reset-endpoint rtpbridge/*
|
||||
|
||||
|
||||
@@ -9,22 +9,62 @@ OsmoHNBGW implements the Home NodeB Gateway function in the 3G network architect
|
||||
as a gateway between the classic 3G core network (CN) domain with its IuCS and IuPS interface
|
||||
and the femtocell based RAN.
|
||||
|
||||
A typical 3G network consisting of Osmocom components will look as illustrated in the following
|
||||
A typical 3G network consisting of Osmocom components is illustrated in the following
|
||||
diagram:
|
||||
|
||||
[[fig-3g]]
|
||||
.Typical 3G network architecture used with OsmoHNBGW
|
||||
[graphviz]
|
||||
----
|
||||
+------------+ +--------+ +----------+ +---------+
|
||||
UE <-->| hNodeB |<--Iuh---->| HNB-GW |<--IuCS-->| OsmoMSC |<--GSUP-->| OsmoHLR |
|
||||
UE <-->| femto cell | ...-->| | ...-->| | | |
|
||||
| | | | +----------+ +---------|
|
||||
+------------+<--GTP-U | |
|
||||
\ | | +------+ +------+
|
||||
| | |<--IuPS-->| SGSN |<--GTP-C-->| GGSN |
|
||||
| +--------+ ...-->| | GTP-U-->| |
|
||||
| +------+ / +------+
|
||||
\_______________________________/
|
||||
digraph G {
|
||||
rankdir = LR;
|
||||
|
||||
UE [label="UE\n(3G phone)"]
|
||||
PBX [label="PBX\nAsterisk, FreeSwitch,\nKamailio, Yate, ..."]
|
||||
|
||||
subgraph cluster_msc_mgw {
|
||||
style=dotted
|
||||
MSC
|
||||
MGW1 [label="MGW"]
|
||||
MSC -> MGW1 [label="MGCP",constraint=false]
|
||||
}
|
||||
|
||||
subgraph cluster_hnbgw_mgw_upf {
|
||||
style=dotted
|
||||
MGW3 [label="MGW"]
|
||||
UPF
|
||||
HNBGW [label=HNBGW,style=bold]
|
||||
HNBGW -> MGW3 [label="MGCP",constraint=false]
|
||||
HNBGW -> UPF [label="PFCP",constraint=false]
|
||||
}
|
||||
|
||||
hNodeB [shape="box",label="hNodeB\n(3G femto cell)"]
|
||||
|
||||
MSC -> HLR [label="\nGSUP",style=bold]
|
||||
SGSN -> HLR [label="GSUP",style="dashed,bold"]
|
||||
UE -> hNodeB [label="Uu",style=bold]
|
||||
UE -> hNodeB [style="dashed,bold"]
|
||||
hNodeB -> HNBGW [label="Iuh",style="bold"]
|
||||
STP2 [label="STP\n(SCCP/M3UA)"]
|
||||
HNBGW -> STP2 -> SGSN [label="IuPS",style="dashed,bold"]
|
||||
HNBGW -> STP2 -> MSC [label="IuCS",style="bold"]
|
||||
SGSN -> GGSN [label="GTP-C",style="dashed,bold"]
|
||||
hNodeB -> UPF -> GGSN [label="GTP-U(3G)",style="dashed"]
|
||||
GGSN -> internet [label="tun",style="dashed"]
|
||||
|
||||
hNodeB -> MGW3 [label="IuUP/RTP",constraint=false]
|
||||
MGW3 -> MGW1 [label="IuUP/RTP"]
|
||||
|
||||
MSC -> SIPConnector [label="MNCC socket",style=bold]
|
||||
|
||||
SIPConnector -> PBX [label="SIP",style=bold]
|
||||
MGW1 -> PBX [label="RTP"]
|
||||
|
||||
A, B, C, D [style="invisible"]
|
||||
A -> B [label="data (PS)",style="dashed"]
|
||||
C -> D [label="voice/SMS/USSD (CS)"]
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
The HNB-GW performs a translation interface between the IuCS/IuPS interfaces on the one hand
|
||||
@@ -32,17 +72,18 @@ side, and the Iuh interface on the or ther hand:
|
||||
|
||||
----
|
||||
Iuh IuCS/IuPS
|
||||
|
||||
NAS +----+----+ +----+----+
|
||||
Non-Access Stratum | CC | MM | | CC | MM |
|
||||
- - - - - - - - - - - +----+----+-------+ +----+----+
|
||||
| RANAP | | H | RANAP |
|
||||
Access Stratum +---------+ HNBAP | N +---------+ - - SCCP USER SAP
|
||||
| RUA | | B | SUA | \
|
||||
+---------+-------+ - +---------+ |
|
||||
| SCTP | G | SCTP | } SIGTRAN
|
||||
+-----------------+ W +---------+ |
|
||||
| IP | | IP | /
|
||||
+----+----+
|
||||
| CC | MM |
|
||||
NAS +----+----+ . . +----+----+
|
||||
Non-Access Stratum | CC | MM | . | RANAP |
|
||||
- - - - - - - - - - - +----+----+-------+ +---------+
|
||||
| RANAP | | H | SCCP |
|
||||
Access Stratum +---------+ HNBAP | N +---------+
|
||||
| RUA | | B | M3UA |
|
||||
+---------+-------+ - +---------+
|
||||
| SCTP | G | SCTP |
|
||||
+-----------------+ W +---------+
|
||||
| IP | | IP |
|
||||
+-----------------+ +---------+
|
||||
----
|
||||
|
||||
|
||||
@@ -69,6 +69,8 @@ specific interface, and will hence not encounter conflicts for multiple instance
|
||||
running on the same interface:
|
||||
|
||||
- The SCCP/M3UA links are established by OsmoHNBGW contacting an STP.
|
||||
- The MGCP link is established by OsmoHNBGW contacting an MGW.
|
||||
- The PFCP link is established by OsmoHNBGW contacting a UPF.
|
||||
|
||||
To run multiple OsmoHNBGW instances on the same SCCP routing, each HNBGW has to
|
||||
configure a distinct point-code, see <<configure_iucs_iups>>.
|
||||
@@ -117,3 +119,75 @@ hnbgw
|
||||
local-ip 10.9.8.7
|
||||
local-port 29169
|
||||
----
|
||||
|
||||
==== Configure co-located media gateway
|
||||
|
||||
OsmoHNBGW requires a co-located OsmoMGW instance. The purpose of the co-located
|
||||
media gateway is to relay the RTP traffic between hNodeB and the core network.
|
||||
|
||||
For security reasons the RAN network is kept separate and isolated from the
|
||||
core network. Both networks will usually have no transparent routing in between
|
||||
them. The co-located media gateway provides an interface between hNodeB and core
|
||||
network across this boundary.
|
||||
|
||||
The configuration is done under the hnbgw node along with `iucs` and `iups`.
|
||||
|
||||
An example configuration for OsmoHNBGW's MGCP client:
|
||||
|
||||
----
|
||||
hnbgw
|
||||
mgw 0
|
||||
remote-ip 127.0.0.1
|
||||
remote-port 2427
|
||||
reset-endpoint rtpbridge/* <1>
|
||||
----
|
||||
<1> The 'reset-endpoint' setting instructs the OsmoMGW to send a wildcarded
|
||||
DLCX to the media gateway. This helps to clear lingering calls from the
|
||||
media gateway when the OsmoHNBGW is restarted.
|
||||
|
||||
OsmoHNBGW is also able to handle a pool of media gateways for load
|
||||
distribution. See also <<mgw_pooling>>.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Previous versions of OsmoHNBGW didn't have the 'mgw' VTY node and
|
||||
hence didn't support the MGW pooling feature. Therefore, historically the MGW
|
||||
related commands where placed under the `mgcp` VTY node. The MGW related commands
|
||||
under the `mgcp` VTY are still parsed and used but its use is deprecated and
|
||||
hence discouraged in favour of the new `mgw` node. Writing the config to a file
|
||||
from within OsmoHNBGW will automatically convert the config to use the new `mgw`
|
||||
node.
|
||||
====
|
||||
|
||||
==== Configure co-located User Plane Function
|
||||
|
||||
OsmoHNBGW optionally supports relaying the GTP user plane via a co-located UPF,
|
||||
which is controlled by the PFCP protocol.
|
||||
|
||||
PFCP support is optional at compile time, as well as run time. To use a co-located UPF,
|
||||
|
||||
* osmo-hnbgw needs to be compiled with 'configure --enable-pfcp',
|
||||
* and osmo-hnbgw.cfg needs to configure a 'pfcp' / 'remote-addr' and
|
||||
'local-addr'.
|
||||
|
||||
The following example configures OsmoHNBGW to associate via PFCP with a UPF
|
||||
listening on UDP 127.0.0.2:8805, ready to setup GTP tunnel relays.
|
||||
|
||||
----
|
||||
hnbgw
|
||||
pfcp
|
||||
remote-addr 127.0.0.2
|
||||
local-addr 127.0.0.1
|
||||
----
|
||||
|
||||
3GPP TS 29.244 4.2.2 specifies that PFCP Request messages shall be sent to UDP
|
||||
port 8805, i.e. the PFCP port is fixed as 8805 and currently not configurable in
|
||||
osmo-hnbgw.
|
||||
|
||||
Setting a 'local-addr' is required: the PFCP protocol features a Node ID, which
|
||||
uniquely identifies PFCP peers across different interfaces. According to the
|
||||
PFCP specification, the Node ID can be a fully-qualified domain name (FQDN) or
|
||||
an IP address. Currently, osmo-hnbgw has no support for using an FQDN as Node
|
||||
ID, and so far uses the 'local-addr' as local Node ID -- hence the 'local-addr'
|
||||
must not be "0.0.0.0", which is an unfortunate consequence. This is likely to
|
||||
improve in the future, see https://osmocom.org/issues/5647 .
|
||||
|
||||
@@ -24,6 +24,8 @@ include::./common/chapters/logging.adoc[]
|
||||
|
||||
include::./common/chapters/cs7-config.adoc[]
|
||||
|
||||
include::./common/chapters/mgwpool.adoc[]
|
||||
|
||||
// include::{srcdir}/chapters/net.adoc[]
|
||||
|
||||
// include::./common/chapters/control_if.adoc[]
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
noinst_HEADERS = \
|
||||
vty.h \
|
||||
context_map.h hnbgw.h hnbgw_cn.h \
|
||||
hnbgw_hnbap.h hnbgw_rua.h hnbgw_ranap.h
|
||||
hnbgw_hnbap.h hnbgw_rua.h hnbgw_ranap.h \
|
||||
ranap_rab_ass.h mgw_fsm.h tdefs.h \
|
||||
hnbgw_pfcp.h \
|
||||
ps_rab_ass_fsm.h \
|
||||
ps_rab_fsm.h \
|
||||
$(NULL)
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/rua/RUA_CN-DomainIndicator.h>
|
||||
|
||||
struct msgb;
|
||||
|
||||
#define LOG_MAP(HNB_CTX_MAP, SUBSYS, LEVEL, FMT, ARGS...) \
|
||||
LOGHNB((HNB_CTX_MAP) ? (HNB_CTX_MAP)->hnb_ctx : NULL, \
|
||||
SUBSYS, LEVEL, "RUA-%u %s: " FMT, \
|
||||
(HNB_CTX_MAP) ? (HNB_CTX_MAP)->rua_ctx_id : 0, \
|
||||
(HNB_CTX_MAP) ? ((HNB_CTX_MAP)->is_ps ? "PS" : "CS") : "NULL", \
|
||||
##ARGS) \
|
||||
|
||||
enum hnbgw_context_map_state {
|
||||
MAP_S_NULL,
|
||||
@@ -33,8 +43,32 @@ struct hnbgw_context_map {
|
||||
bool is_ps;
|
||||
/* SCCP User SAP connection ID */
|
||||
uint32_t scu_conn_id;
|
||||
/* Set to true on SCCP Conn Conf, set to false when an OSMO_SCU_PRIM_N_DISCONNECT has been sent for the SCCP
|
||||
* User SAP conn. Useful to avoid leaking SCCP connections: guarantee that an OSMO_SCU_PRIM_N_DISCONNECT gets
|
||||
* sent, even when RUA fails to gracefully disconnect. */
|
||||
bool scu_conn_active;
|
||||
/* Pending data to be sent: when we send an "empty" SCCP CR first, the initial RANAP message will be sent in a
|
||||
* separate DT once the CR is confirmed. This caches the initial RANAP message. */
|
||||
struct msgb *cached_msg;
|
||||
|
||||
enum hnbgw_context_map_state state;
|
||||
|
||||
/* FSM instance for the MGW */
|
||||
struct osmo_fsm_inst *mgw_fi;
|
||||
|
||||
/* FSMs handling RANAP RAB assignments for PS. list of struct ps_rab_ass. For PS RAB Assignment, each Request
|
||||
* and gets one ps_rab_ass FSM and each Response gets one ps_rab_ass FSM. The reason is that theoretically, each
|
||||
* such message can contain any number and any combination of RAB IDs, 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 hnbgw_context_map.ps_rabs, 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;
|
||||
|
||||
/* 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;
|
||||
};
|
||||
|
||||
|
||||
@@ -46,6 +80,8 @@ context_map_alloc_by_hnb(struct hnb_context *hnb, uint32_t rua_ctx_id,
|
||||
struct hnbgw_context_map *
|
||||
context_map_by_cn(struct hnbgw_cnlink *cn, uint32_t scu_conn_id);
|
||||
|
||||
int context_map_send_cached_msg(struct hnbgw_context_map *map);
|
||||
|
||||
void context_map_deactivate(struct hnbgw_context_map *map);
|
||||
|
||||
int context_map_init(struct hnb_gw *gw);
|
||||
|
||||
@@ -10,16 +10,19 @@
|
||||
#define DEBUG
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_pool.h>
|
||||
|
||||
enum {
|
||||
DMAIN,
|
||||
DHNBAP,
|
||||
DRUA,
|
||||
DRANAP,
|
||||
DMGW,
|
||||
};
|
||||
|
||||
#define LOGHNB(x, ss, lvl, fmt, args ...) \
|
||||
LOGP(ss, lvl, "%s " fmt, hnb_context_name(x), ## args)
|
||||
#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,
|
||||
@@ -83,6 +86,7 @@ struct hnbgw_cnlink {
|
||||
struct llist_head map_list;
|
||||
};
|
||||
|
||||
/* 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;
|
||||
@@ -100,8 +104,7 @@ struct hnb_context {
|
||||
/*! SCTP stream ID for RUA */
|
||||
uint16_t rua_stream;
|
||||
|
||||
/*! True if a HNB-REGISTER-REQ from this HNB has been accepted. Note that
|
||||
* this entire data structure is freed if the HNB sends HNB-DE-REGISTER-REQ. */
|
||||
/*! True if a HNB-REGISTER-REQ from this HNB has been accepted. */
|
||||
bool hnb_registered;
|
||||
|
||||
/* linked list of hnbgw_context_map */
|
||||
@@ -133,6 +136,14 @@ struct hnb_gw {
|
||||
bool hnbap_allow_tmsi;
|
||||
/*! print hnb-id (true) or MCC-MNC-LAC-RAC-SAC (false) in logs */
|
||||
bool log_prefix_hnb_id;
|
||||
unsigned int max_sccp_cr_payload_len;
|
||||
struct mgcp_client_conf *mgcp_client;
|
||||
struct {
|
||||
char *local_addr;
|
||||
uint16_t local_port;
|
||||
char *remote_addr;
|
||||
uint16_t remote_port;
|
||||
} pfcp;
|
||||
} config;
|
||||
/*! SCTP listen socket for incoming connections */
|
||||
struct osmo_stream_srv_link *iuh;
|
||||
@@ -151,6 +162,14 @@ struct hnb_gw {
|
||||
struct osmo_sccp_addr iucs_remote_addr;
|
||||
struct osmo_sccp_addr iups_remote_addr;
|
||||
} sccp;
|
||||
/* MGW pool, also includes the single MGCP client as fallback if no
|
||||
* pool is configured. */
|
||||
struct mgcp_client_pool *mgw_pool;
|
||||
|
||||
struct {
|
||||
struct osmo_pfcp_endpoint *ep;
|
||||
struct osmo_pfcp_cp_peer *cp_peer;
|
||||
} pfcp;
|
||||
};
|
||||
|
||||
extern void *talloc_asn1_ctx;
|
||||
@@ -172,3 +191,15 @@ void hnb_context_release(struct hnb_context *ctx);
|
||||
|
||||
void hnbgw_vty_init(struct hnb_gw *gw, void *tall_ctx);
|
||||
int hnbgw_vty_go_parent(struct vty *vty);
|
||||
|
||||
bool hnbgw_requires_empty_sccp_cr(struct hnb_gw *gw, unsigned int ranap_msg_len);
|
||||
|
||||
/* Return true when the user configured GTP mapping to be enabled, by configuring a PFCP link to a UPF.
|
||||
* Return false when the user configured to skip GTP mapping and RANAP PS RAB Requests/Responses should be passed thru
|
||||
* 1:1.
|
||||
* GTP mapping means that there are two GTP tunnels, one towards HNB and one towards CN, and we forward payloads between
|
||||
* the two tunnels, mapping the TEIDs and GTP addresses. */
|
||||
static inline bool hnb_gw_is_gtp_mapping_enabled(const struct hnb_gw *gw)
|
||||
{
|
||||
return gw->config.pfcp.remote_addr != NULL;
|
||||
}
|
||||
|
||||
5
include/osmocom/hnbgw/hnbgw_pfcp.h
Normal file
5
include/osmocom/hnbgw/hnbgw_pfcp.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
struct hnb_gw;
|
||||
|
||||
int hnbgw_pfcp_init(struct hnb_gw *hnb_gw);
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/rua/RUA_Cause.h>
|
||||
#include <osmocom/rua/RUA_CN-DomainIndicator.h>
|
||||
|
||||
int hnbgw_rua_rx(struct hnb_context *hnb, struct msgb *msg);
|
||||
int hnbgw_rua_init(void);
|
||||
@@ -11,3 +12,9 @@ int rua_tx_dt(struct hnb_context *hnb, int is_ps, uint32_t context_id,
|
||||
const uint8_t *data, unsigned int len);
|
||||
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);
|
||||
|
||||
int rua_to_scu(struct hnb_context *hnb,
|
||||
RUA_CN_DomainIndicator_t cN_DomainIndicator,
|
||||
enum osmo_scu_prim_type type,
|
||||
uint32_t context_id, uint32_t cause,
|
||||
const uint8_t *data, unsigned int len);
|
||||
|
||||
7
include/osmocom/hnbgw/mgw_fsm.h
Normal file
7
include/osmocom/hnbgw/mgw_fsm.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
|
||||
int handle_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message);
|
||||
int mgw_fsm_handle_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message);
|
||||
int mgw_fsm_release(struct hnbgw_context_map *map);
|
||||
14
include/osmocom/hnbgw/ps_rab_ass_fsm.h
Normal file
14
include/osmocom/hnbgw/ps_rab_ass_fsm.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
|
||||
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_FAIL,
|
||||
};
|
||||
|
||||
int hnbgw_gtpmap_rx_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message);
|
||||
int hnbgw_gtpmap_rx_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message);
|
||||
void hnbgw_gtpmap_release(struct hnbgw_context_map *map);
|
||||
102
include/osmocom/hnbgw/ps_rab_fsm.h
Normal file
102
include/osmocom/hnbgw/ps_rab_fsm.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/use_count.h>
|
||||
#include <osmocom/pfcp/pfcp_msg.h>
|
||||
|
||||
/* A GTP tunnel has two endpoints, each endpoint has an IP address and a Tunnel Endpoint ID. So two struct addr_teid
|
||||
* identify one GTP tunnel. For GTP mapping between HNB and CN, we have two tunnels, see also struct half_gtp_map. The
|
||||
* combination of IP address and TEID is also known as F-TEID (fully qualified TEID). */
|
||||
struct addr_teid {
|
||||
bool present;
|
||||
struct osmo_sockaddr addr;
|
||||
uint32_t teid;
|
||||
};
|
||||
|
||||
/* One half_gtp_map represents one GTP tunnel, either on the HNB side or on the CN side. Two struct half_gtp_map make up
|
||||
* a GTP mapping between HNB and CN. One half_gtp_map for the Access (HNB) side, one for the Core (CN) side. The PFCP
|
||||
* PDR (Packet Detection Rule) identifies packets coming in on the GTP tunnel the half_gtp_map represents, while the
|
||||
* PFCP FAR (Forwarding Action Rule) identifies the GTP destination, i.e. the other side's GTP tunnel. So a
|
||||
* half_gtp_map.far_id is closely tied to the other half_gtp_map, and makes little sense on its own.
|
||||
*
|
||||
* half_gtp_map | half_gtp_map
|
||||
* Access HNBGW+UPF Core
|
||||
* remote local | local remote
|
||||
* -->PDR-FAR-->|
|
||||
* |<--FAR-PDR<--
|
||||
*
|
||||
* See ps_rab.core, ps_rab.access.
|
||||
*/
|
||||
struct half_gtp_map {
|
||||
/* GTP endpoint, obtained from incoming RAB Assignment Request/Response.
|
||||
* This is the remote side as seen from the UPF's point of view.
|
||||
* For example, ps_rab.core.remote is the CN GTP that the RAB Assignment Request told us.
|
||||
* ps_rab.access.remote is the HNB GTP that RAB Assignment Response told us. */
|
||||
struct addr_teid remote;
|
||||
/* UPF GTP endpoint, obtained from PFCP Session Establishment Response. */
|
||||
struct addr_teid local;
|
||||
/* PFCP Packet Detection Rule id that detects GTP-U packets coming from Core/Access */
|
||||
uint16_t pdr_id;
|
||||
/* PFCP Forward Action Rule id that forwards GTP-U packets to Access/Core */
|
||||
uint32_t far_id;
|
||||
/* Whether the RANAP message this RAB's remote address was obtained from had the address encoded in x213_nsap */
|
||||
bool use_x213_nsap;
|
||||
};
|
||||
|
||||
/* A PS RAB's PFCP state. For the related RANAP state, see struct ps_rab_ass instead. */
|
||||
struct ps_rab {
|
||||
/* Instance of ps_rab_fsm. */
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
/* backpointer */
|
||||
struct hnb_gw *hnb_gw;
|
||||
|
||||
/* List entry and backpointer.
|
||||
* If map == NULL, do not call llist_del(&entry): the hnbgw_context_map may deallocate before the PFCP release
|
||||
* is complete, in which case it sets map = NULL. */
|
||||
struct llist_head entry;
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
/* RAB-ID used in RANAP RAB AssignmentRequest and Response messages */
|
||||
uint8_t rab_id;
|
||||
/* Backpointer to the ps_rab_ass_fsm for the RAB Assignment Request from Core that created this RAB.
|
||||
* There are two separate RAB Assignment FSMs responsible for this RAB, one for the Request message and one for
|
||||
* the Response message. Each RAB Assignment FSM may be responsible for N other RABs besides this one. */
|
||||
struct osmo_fsm_inst *req_fi;
|
||||
/* Backpointer to the ps_rab_ass_fsm for the RAB Assignment Response from Access that confirmed this RAB. */
|
||||
struct osmo_fsm_inst *resp_fi;
|
||||
|
||||
/* PFCP session controlling the GTP mapping for this RAB */
|
||||
uint64_t cp_seid;
|
||||
struct osmo_pfcp_ie_f_seid up_f_seid;
|
||||
bool release_requested;
|
||||
|
||||
/* 'local' and 'remote' refer to the GTP information from the UPF's point of view:
|
||||
* HNB UPF CN
|
||||
* access.remote <---> access.local | core.local <---> core.remote
|
||||
*/
|
||||
struct half_gtp_map core;
|
||||
struct half_gtp_map access;
|
||||
|
||||
struct osmo_use_count use_count;
|
||||
};
|
||||
|
||||
struct ps_rab *ps_rab_start(struct hnbgw_context_map *map, uint8_t rab_id,
|
||||
const struct addr_teid *core_f_teid, bool use_x213_nsap,
|
||||
struct osmo_fsm_inst *req_fi);
|
||||
|
||||
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);
|
||||
|
||||
struct ps_rab_rx_args {
|
||||
struct addr_teid f_teid;
|
||||
bool use_x213_nsap;
|
||||
struct osmo_fsm_inst *notify_fi;
|
||||
};
|
||||
int ps_rab_rx_access_remote_f_teid(struct hnbgw_context_map *map, uint8_t rab_id,
|
||||
const struct ps_rab_rx_args *args);
|
||||
|
||||
struct ps_rab *ps_rab_find_by_seid(struct hnb_gw *hnb_gw, uint64_t seid, bool is_cp_seid);
|
||||
void ps_rab_pfcp_set_msg_ctx(struct ps_rab *rab, struct osmo_pfcp_msg *m);
|
||||
20
include/osmocom/hnbgw/ranap_rab_ass.h
Normal file
20
include/osmocom/hnbgw/ranap_rab_ass.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
struct msgb *ranap_rab_ass_req_encode(RANAP_RAB_AssignmentRequestIEs_t *rab_assignment_request_ies);
|
||||
int ranap_rab_ass_resp_encode(uint8_t *data, unsigned int len,
|
||||
RANAP_RAB_AssignmentResponseIEs_t *rab_assignment_response_ies);
|
||||
|
||||
int ranap_rab_ass_req_ies_extract_inet_addr(struct osmo_sockaddr *addr, uint8_t *rab_id,
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies, unsigned int index);
|
||||
int ranap_rab_ass_resp_ies_extract_inet_addr(struct osmo_sockaddr *addr, RANAP_RAB_AssignmentResponseIEs_t *ies,
|
||||
uint8_t rab_id);
|
||||
|
||||
int ranap_rab_ass_req_ies_replace_inet_addr(RANAP_RAB_AssignmentRequestIEs_t *ies, struct osmo_sockaddr *addr,
|
||||
uint8_t rab_id);
|
||||
int ranap_rab_ass_resp_ies_replace_inet_addr(RANAP_RAB_AssignmentResponseIEs_t *ies, struct osmo_sockaddr *addr,
|
||||
uint8_t rab_id);
|
||||
|
||||
bool ranap_rab_ass_req_ies_check_release(RANAP_RAB_AssignmentRequestIEs_t *ies, uint8_t rab_id);
|
||||
bool ranap_rab_ass_resp_ies_check_failure(RANAP_RAB_AssignmentResponseIEs_t *ies, uint8_t rab_id);
|
||||
|
||||
int ranap_rab_ass_req_ies_get_count(RANAP_RAB_AssignmentRequestIEs_t *ies);
|
||||
7
include/osmocom/hnbgw/tdefs.h
Normal file
7
include/osmocom/hnbgw/tdefs.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/tdef.h>
|
||||
|
||||
extern struct osmo_tdef mgw_fsm_T_defs[];
|
||||
extern struct osmo_tdef ps_T_defs[];
|
||||
extern struct osmo_tdef_group hnbgw_tdef_group[];
|
||||
@@ -7,5 +7,8 @@ enum osmo_iuh_vty_node {
|
||||
IUH_NODE,
|
||||
IUCS_NODE,
|
||||
IUPS_NODE,
|
||||
MGCP_NODE,
|
||||
MGW_NODE,
|
||||
PFCP_NODE,
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ AM_CFLAGS = \
|
||||
$(LIBOSMORUA_CFLAGS) \
|
||||
$(LIBOSMORANAP_CFLAGS) \
|
||||
$(LIBOSMOHNBAP_CFLAGS) \
|
||||
$(LIBOSMOMGCPCLIENT_CFLAGS) \
|
||||
$(NULL)
|
||||
|
||||
AM_LDFLAGS = \
|
||||
@@ -37,6 +38,9 @@ osmo_hnbgw_SOURCES = \
|
||||
hnbgw_vty.c \
|
||||
context_map.c \
|
||||
hnbgw_cn.c \
|
||||
ranap_rab_ass.c \
|
||||
mgw_fsm.c \
|
||||
tdefs.c \
|
||||
$(NULL)
|
||||
|
||||
osmo_hnbgw_LDADD = \
|
||||
@@ -52,4 +56,21 @@ osmo_hnbgw_LDADD = \
|
||||
$(LIBOSMORANAP_LIBS) \
|
||||
$(LIBOSMOHNBAP_LIBS) \
|
||||
$(LIBSCTP_LIBS) \
|
||||
$(LIBOSMOMGCPCLIENT_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
if ENABLE_PFCP
|
||||
AM_CFLAGS += \
|
||||
$(LIBOSMOGTLV_CFLAGS) \
|
||||
$(LIBOSMOPFCP_CFLAGS) \
|
||||
$(NULL)
|
||||
osmo_hnbgw_LDADD += \
|
||||
$(LIBOSMOGTLV_LIBS) \
|
||||
$(LIBOSMOPFCP_LIBS) \
|
||||
$(NULL)
|
||||
osmo_hnbgw_SOURCES += \
|
||||
hnbgw_pfcp.c \
|
||||
ps_rab_ass_fsm.c \
|
||||
ps_rab_fsm.c \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
@@ -19,13 +19,20 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* an expired mapping is destroyed after 1..2 * EXPIRY_TIMER_SECS */
|
||||
#define EXPIRY_TIMER_SECS 23
|
||||
|
||||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
#include <osmocom/hnbgw/ps_rab_ass_fsm.h>
|
||||
|
||||
const struct value_string hnbgw_context_map_state_names[] = {
|
||||
{MAP_S_NULL , "not-initialized"},
|
||||
@@ -53,8 +60,21 @@ static int alloc_cn_conn_id(struct hnbgw_cnlink *cn, uint32_t *id_out)
|
||||
uint32_t i;
|
||||
uint32_t id;
|
||||
|
||||
for (i = 0; i < 0xffffffff; i++) {
|
||||
/* SUA: RFC3868 sec 3.10.4:
|
||||
* The source reference number is a 4 octet long integer.
|
||||
* This is allocated by the source SUA instance.
|
||||
* M3UA/SCCP: ITU-T Q.713 sec 3.3:
|
||||
* The "source local reference" parameter field is a three-octet field containing a
|
||||
* reference number which is generated and used by the local node to identify the
|
||||
* connection section after the connection section is set up.
|
||||
* The coding "all ones" is reserved for future use.
|
||||
* Hence, let's simply use 24 bit ids to fit all link types (excluding 0x00ffffff).
|
||||
*/
|
||||
|
||||
for (i = 0; i < 0x00ffffff; i++) {
|
||||
id = cn->next_conn_id++;
|
||||
if (cn->next_conn_id == 0x00ffffff)
|
||||
cn->next_conn_id = 0;
|
||||
if (!cn_id_in_use(cn, id)) {
|
||||
*id_out = id;
|
||||
return 1;
|
||||
@@ -100,6 +120,8 @@ context_map_alloc_by_hnb(struct hnb_context *hnb, uint32_t rua_ctx_id,
|
||||
map->rua_ctx_id = rua_ctx_id;
|
||||
map->is_ps = is_ps;
|
||||
map->scu_conn_id = new_scu_conn_id;
|
||||
INIT_LLIST_HEAD(&map->ps_rab_ass);
|
||||
INIT_LLIST_HEAD(&map->ps_rabs);
|
||||
|
||||
/* put it into both lists */
|
||||
llist_add_tail(&map->hnb_list, &hnb->map_list);
|
||||
@@ -129,14 +151,47 @@ context_map_by_cn(struct hnbgw_cnlink *cn, uint32_t scu_conn_id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int context_map_send_cached_msg(struct hnbgw_context_map *map)
|
||||
{
|
||||
int rc;
|
||||
if (!map || !map->cached_msg)
|
||||
return 0;
|
||||
rc = rua_to_scu(map->hnb_ctx,
|
||||
map->is_ps ? RUA_CN_DomainIndicator_ps_domain : RUA_CN_DomainIndicator_cs_domain,
|
||||
OSMO_SCU_PRIM_N_DATA, map->rua_ctx_id, 0,
|
||||
msgb_data(map->cached_msg), msgb_length(map->cached_msg));
|
||||
msgb_free(map->cached_msg);
|
||||
map->cached_msg = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
void context_map_deactivate(struct hnbgw_context_map *map)
|
||||
{
|
||||
LOG_MAP(map, DMAIN, LOGL_INFO, "Deactivating\n");
|
||||
|
||||
/* set the state to reserved. We still show up in the list and
|
||||
* avoid re-allocation of the context-id until we are cleaned up
|
||||
* by the context_map garbage collector timer */
|
||||
|
||||
if (map->state != MAP_S_RESERVED2)
|
||||
map->state = MAP_S_RESERVED1;
|
||||
|
||||
/* Is SCCP still active and needs to be disconnected ungracefully? */
|
||||
if (map->scu_conn_active) {
|
||||
osmo_sccp_tx_disconn(map->hnb_ctx->gw->sccp.cnlink->sccp_user, map->scu_conn_id, NULL, 0);
|
||||
map->scu_conn_active = false;
|
||||
}
|
||||
|
||||
/* a possibly still existing MGW FSM must be terminated when the context
|
||||
* map is deactivated. (this is a cornercase) */
|
||||
if (map->mgw_fi) {
|
||||
mgw_fsm_release(map);
|
||||
OSMO_ASSERT(map->mgw_fi == NULL);
|
||||
}
|
||||
|
||||
#if ENABLE_PFCP
|
||||
hnbgw_gtpmap_release(map);
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct osmo_timer_list context_map_tmr;
|
||||
@@ -158,6 +213,7 @@ static void context_map_tmr_cb(void *data)
|
||||
case MAP_S_RESERVED2:
|
||||
/* second time we see this reserved
|
||||
* entry: remove it */
|
||||
LOG_MAP(map, DMAIN, LOGL_INFO, "Deallocating\n");
|
||||
map->state = MAP_S_NULL;
|
||||
llist_del(&map->cn_list);
|
||||
llist_del(&map->hnb_list);
|
||||
|
||||
@@ -49,9 +49,12 @@
|
||||
#include <osmocom/ctrl/ports.h>
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
@@ -59,10 +62,15 @@
|
||||
#include <osmocom/sigtran/protocol/m3ua.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
#if ENABLE_PFCP
|
||||
#include <osmocom/pfcp/pfcp_proto.h>
|
||||
#endif
|
||||
|
||||
#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/hnbgw_pfcp.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
|
||||
static const char * const osmo_hnbgw_copyright =
|
||||
@@ -86,12 +94,24 @@ static struct hnb_gw *hnb_gw_create(void *ctx)
|
||||
gw->config.iuh_local_port = IUH_DEFAULT_SCTP_PORT;
|
||||
gw->config.log_prefix_hnb_id = true;
|
||||
|
||||
/* No limit by default, always include the initial RANAP message in the SCCP CR towards the CN.
|
||||
* 999999 is the maximum value in hnbgw_vty.c */
|
||||
gw->config.max_sccp_cr_payload_len = 999999;
|
||||
|
||||
gw->next_ue_ctx_id = 23;
|
||||
INIT_LLIST_HEAD(&gw->hnb_list);
|
||||
INIT_LLIST_HEAD(&gw->ue_list);
|
||||
|
||||
context_map_init(gw);
|
||||
|
||||
gw->mgw_pool = mgcp_client_pool_alloc(gw);
|
||||
gw->config.mgcp_client = talloc_zero(tall_hnb_ctx, struct mgcp_client_conf);
|
||||
mgcp_client_conf_init(gw->config.mgcp_client);
|
||||
|
||||
#if ENABLE_PFCP
|
||||
gw->config.pfcp.remote_port = OSMO_PFCP_PORT;
|
||||
#endif
|
||||
|
||||
return gw;
|
||||
}
|
||||
|
||||
@@ -220,30 +240,71 @@ void ue_context_free(struct ue_context *ue)
|
||||
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 varoius downstream processing functions */
|
||||
* benefit of various downstream processing functions */
|
||||
msg->dst = hnb;
|
||||
|
||||
rc = osmo_stream_srv_recv(conn, msg);
|
||||
if (rc == -EAGAIN) {
|
||||
/* Notification received */
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
/* 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;
|
||||
/* 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()\n");
|
||||
/* FIXME: clean up after disappeared HNB */
|
||||
hnb_context_release(hnb);
|
||||
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) {
|
||||
hnb_context_release(hnb);
|
||||
rc = -1;
|
||||
|
||||
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);
|
||||
@@ -255,6 +316,10 @@ static int hnb_read_cb(struct osmo_stream_srv *conn)
|
||||
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;
|
||||
@@ -275,6 +340,21 @@ out:
|
||||
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;
|
||||
}
|
||||
|
||||
struct hnb_context *hnb_context_alloc(struct hnb_gw *gw, struct osmo_stream_srv_link *link, int new_fd)
|
||||
{
|
||||
struct hnb_context *ctx;
|
||||
@@ -285,7 +365,7 @@ struct hnb_context *hnb_context_alloc(struct hnb_gw *gw, struct osmo_stream_srv_
|
||||
INIT_LLIST_HEAD(&ctx->map_list);
|
||||
|
||||
ctx->gw = gw;
|
||||
ctx->conn = osmo_stream_srv_create(tall_hnb_ctx, link, new_fd, hnb_read_cb, NULL, ctx);
|
||||
ctx->conn = osmo_stream_srv_create(tall_hnb_ctx, 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);
|
||||
@@ -319,6 +399,8 @@ void hnb_context_release(struct hnb_context *ctx)
|
||||
{
|
||||
struct hnbgw_context_map *map, *map2;
|
||||
|
||||
LOGHNB(ctx, DMAIN, LOGL_INFO, "Releasing HNB context\n");
|
||||
|
||||
/* remove from the list of HNB contexts */
|
||||
llist_del(&ctx->list);
|
||||
|
||||
@@ -333,17 +415,31 @@ void hnb_context_release(struct hnb_context *ctx)
|
||||
}
|
||||
ue_context_free_by_hnb(ctx->gw, ctx);
|
||||
|
||||
osmo_stream_srv_destroy(ctx->conn);
|
||||
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 */
|
||||
|
||||
talloc_free(ctx);
|
||||
}
|
||||
|
||||
bool hnbgw_requires_empty_sccp_cr(struct hnb_gw *gw, unsigned int ranap_msg_len)
|
||||
{
|
||||
return ranap_msg_len > gw->config.max_sccp_cr_payload_len;
|
||||
}
|
||||
|
||||
/*! call-back when the listen FD has something to read */
|
||||
static int accept_cb(struct osmo_stream_srv_link *srv, int fd)
|
||||
{
|
||||
struct hnb_gw *gw = osmo_stream_srv_link_get_data(srv);
|
||||
struct hnb_context *ctx;
|
||||
|
||||
LOGP(DMAIN, LOGL_INFO, "New HNB SCTP connection %s\n",
|
||||
osmo_sock_get_name2(fd));
|
||||
|
||||
ctx = hnb_context_alloc(gw, srv, fd);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
@@ -372,6 +468,11 @@ static const struct log_info_cat log_cat[] = {
|
||||
.color = "",
|
||||
.description = "RAN Application Part",
|
||||
},
|
||||
[DMGW] = {
|
||||
.name = "DMGW", .loglevel = LOGL_NOTICE, .enabled = 1,
|
||||
.color = "\033[1;33m",
|
||||
.description = "Media Gateway",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct log_info hnbgw_log_info = {
|
||||
@@ -573,6 +674,41 @@ static int hnb_ctrl_node_lookup(void *data, vector vline, int *node_type, void *
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int hnbgw_mgw_setup(void)
|
||||
{
|
||||
struct mgcp_client *mgcp_client_single;
|
||||
unsigned int pool_members_initalized;
|
||||
|
||||
/* Initialize MGW pool. This initalizes and connects all MGCP clients that are currently configured in
|
||||
* the pool. Adding additional MGCP clients to the pool is possible but the user has to configure and
|
||||
* (re)connect them manually from the VTY. */
|
||||
pool_members_initalized = mgcp_client_pool_connect(g_hnb_gw->mgw_pool);
|
||||
if (pool_members_initalized) {
|
||||
LOGP(DMGW, LOGL_NOTICE,
|
||||
"MGW pool with %u pool members configured, (ignoring MGW configuration in VTY node 'mgcp').\n",
|
||||
pool_members_initalized);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize and connect a single MGCP client. This MGCP client will appear as the one and only pool
|
||||
* member if there is no MGW pool configured. */
|
||||
LOGP(DMGW, LOGL_NOTICE, "No MGW pool configured, using MGW configuration in VTY node 'mgcp'\n");
|
||||
mgcp_client_single = mgcp_client_init(tall_hnb_ctx, g_hnb_gw->config.mgcp_client);
|
||||
if (!mgcp_client_single) {
|
||||
LOGP(DMGW, LOGL_ERROR, "MGW (single) client initalization failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (mgcp_client_connect(mgcp_client_single)) {
|
||||
LOGP(DMGW, LOGL_ERROR, "MGW (single) connect failed at (%s:%u)\n",
|
||||
g_hnb_gw->config.mgcp_client->remote_addr,
|
||||
g_hnb_gw->config.mgcp_client->remote_port);
|
||||
return -EINVAL;
|
||||
}
|
||||
mgcp_client_pool_register_single(g_hnb_gw->mgw_pool, mgcp_client_single);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct osmo_stream_srv_link *srv;
|
||||
@@ -595,6 +731,7 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
vty_info.tall_ctx = tall_hnb_ctx;
|
||||
vty_info.copyright = osmo_hnbgw_copyright;
|
||||
vty_init(&vty_info);
|
||||
|
||||
@@ -603,6 +740,7 @@ int main(int argc, char **argv)
|
||||
hnbgw_vty_init(g_hnb_gw, tall_hnb_ctx);
|
||||
ctrl_vty_init(tall_hnb_ctx);
|
||||
logging_vty_add_cmds();
|
||||
osmo_talloc_vty_add_cmds();
|
||||
|
||||
/* Handle options after vty_init(), for --version */
|
||||
handle_options(argc, argv);
|
||||
@@ -629,14 +767,14 @@ int main(int argc, char **argv)
|
||||
log_set_log_level(osmo_stderr_target,
|
||||
hnbgw_cmdline_config.log_level);
|
||||
|
||||
rc = telnet_init_dynif(tall_hnb_ctx, g_hnb_gw, vty_get_bind_addr(), OSMO_VTY_PORT_HNBGW);
|
||||
rc = telnet_init_default(tall_hnb_ctx, g_hnb_gw, OSMO_VTY_PORT_HNBGW);
|
||||
if (rc < 0) {
|
||||
perror("Error binding VTY port");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
g_hnb_gw->ctrl = ctrl_interface_setup_dynip2(g_hnb_gw, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_HNBGW,
|
||||
hnb_ctrl_node_lookup, _LAST_CTRL_NODE_HNB);
|
||||
g_hnb_gw->ctrl = ctrl_interface_setup2(g_hnb_gw, OSMO_CTRL_PORT_HNBGW, hnb_ctrl_node_lookup,
|
||||
_LAST_CTRL_NODE_HNB);
|
||||
if (!g_hnb_gw->ctrl) {
|
||||
LOGP(DMAIN, LOGL_ERROR, "Failed to create CTRL interface on %s:%u\n",
|
||||
ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_HNBGW);
|
||||
@@ -681,6 +819,15 @@ int main(int argc, char **argv)
|
||||
}
|
||||
g_hnb_gw->iuh = srv;
|
||||
|
||||
/* Initialize and connect MGCP client. */
|
||||
if (hnbgw_mgw_setup() != 0)
|
||||
return -EINVAL;
|
||||
|
||||
#if ENABLE_PFCP
|
||||
/* If UPF is configured, set up PFCP socket and send Association Setup Request to UPF */
|
||||
hnbgw_pfcp_init(g_hnb_gw);
|
||||
#endif
|
||||
|
||||
if (hnbgw_cmdline_config.daemonize) {
|
||||
rc = osmo_daemonize();
|
||||
if (rc < 0) {
|
||||
@@ -690,7 +837,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
while (1) {
|
||||
rc = osmo_select_main(0);
|
||||
rc = osmo_select_main_ctx(0);
|
||||
if (rc < 0)
|
||||
exit(3);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
|
||||
@@ -29,11 +31,21 @@
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#if ENABLE_PFCP
|
||||
#include <osmocom/pfcp/pfcp_cp_peer.h>
|
||||
#endif
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/ranap/ranap_msg_factory.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
#include <osmocom/hnbgw/ps_rab_ass_fsm.h>
|
||||
#include <osmocom/ranap/RANAP_ProcedureCode.h>
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
#include <osmocom/ranap/RANAP_RANAP-PDU.h>
|
||||
|
||||
/***********************************************************************
|
||||
* Outbound RANAP RESET to CN
|
||||
@@ -266,7 +278,7 @@ static int handle_cn_ranap(struct hnbgw_cnlink *cnlink, const struct osmo_scu_un
|
||||
int rc;
|
||||
|
||||
memset(pdu, 0, sizeof(*pdu));
|
||||
dec_ret = aper_decode(NULL,&asn_DEF_RANAP_RANAP_PDU, (void **) &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");
|
||||
@@ -324,18 +336,29 @@ static int handle_cn_conn_conf(struct hnbgw_cnlink *cnlink,
|
||||
const struct osmo_scu_connect_param *param,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
/* we don't actually need to do anything, as RUA towards the HNB
|
||||
* doesn't seem to know any confirmations to its CONNECT
|
||||
* operation */
|
||||
struct osmo_ss7_instance *ss7 = osmo_sccp_get_ss7(cnlink->gw->sccp.client);
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() conn_id=%d\n",
|
||||
param->conn_id);
|
||||
LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() called_addr=%s\n",
|
||||
inet_ntoa(param->called_addr.ip.v4));
|
||||
LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() calling_addr=%s\n",
|
||||
inet_ntoa(param->calling_addr.ip.v4));
|
||||
LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() responding_addr=%s\n",
|
||||
inet_ntoa(param->responding_addr.ip.v4));
|
||||
LOGP(DMAIN, LOGL_DEBUG, "handle_cn_conn_conf() conn_id=%d, addrs: called=%s calling=%s responding=%s\n",
|
||||
param->conn_id,
|
||||
osmo_sccp_addr_to_str_c(OTC_SELECT, ss7, ¶m->called_addr),
|
||||
osmo_sccp_addr_to_str_c(OTC_SELECT, ss7, ¶m->calling_addr),
|
||||
osmo_sccp_addr_to_str_c(OTC_SELECT, ss7, ¶m->responding_addr));
|
||||
|
||||
/* Nothing needs to happen for RUA, RUA towards the HNB doesn't seem to know any confirmations to its CONNECT
|
||||
* operation. */
|
||||
|
||||
map = context_map_by_cn(cnlink, param->conn_id);
|
||||
if (!map)
|
||||
return 0;
|
||||
|
||||
/* SCCP connection is confirmed. Mark conn as active, i.e. requires a DISCONNECT to clean up the SCCP
|
||||
* connection. */
|
||||
map->scu_conn_active = true;
|
||||
|
||||
/* If our initial SCCP CR was sent without data payload, then the initial RANAP message is cached and waiting to
|
||||
* be sent as soon as the SCCP connection is confirmed. See if that is the case, send cached data. */
|
||||
context_map_send_cached_msg(map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -345,10 +368,13 @@ static int handle_cn_data_ind(struct hnbgw_cnlink *cnlink,
|
||||
struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnbgw_context_map *map;
|
||||
ranap_message *message;
|
||||
int rc;
|
||||
|
||||
/* connection-oriented data is always passed transparently
|
||||
* towards the specific HNB, via a RUA connection identified by
|
||||
* conn_id */
|
||||
/* Usually connection-oriented data is always passed transparently towards the specific HNB, via a RUA
|
||||
* connection identified by conn_id. An exception is made for RANAP RAB AssignmentRequest and
|
||||
* RANAP RAB AssignmentResponse, since those messages contain transport layer information (RTP stream IP/Port),
|
||||
* which is rewritten by the FSM that controls the co-located media gateway. */
|
||||
|
||||
map = context_map_by_cn(cnlink, param->conn_id);
|
||||
if (!map) {
|
||||
@@ -356,6 +382,63 @@ static int handle_cn_data_ind(struct hnbgw_cnlink *cnlink,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Intercept RAB Assignment Request, to map RTP and GTP between access and core */
|
||||
if (!map->is_ps) {
|
||||
/* Circuit-Switched. Set up mapping of RTP ports via MGW */
|
||||
message = talloc_zero(map, ranap_message);
|
||||
rc = ranap_ran_rx_co_decode(map, message, msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
|
||||
if (rc == 0) {
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* mgw_fsm_alloc_and_handle_rab_ass_req() takes ownership of (ranap) message */
|
||||
return handle_rab_ass_req(map, oph, 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 the rua_tx_dt() call below. */
|
||||
mgw_fsm_release(map);
|
||||
break;
|
||||
}
|
||||
ranap_ran_rx_co_free(message);
|
||||
}
|
||||
|
||||
talloc_free(message);
|
||||
#if ENABLE_PFCP
|
||||
} else {
|
||||
struct hnb_gw *hnb_gw = cnlink->gw;
|
||||
/* Packet-Switched. Set up mapping of GTP ports via UPF */
|
||||
message = talloc_zero(map, ranap_message);
|
||||
rc = ranap_ran_rx_co_decode(map, message, msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
|
||||
if (rc == 0) {
|
||||
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(hnb_gw)) {
|
||||
LOGP(DMAIN, LOGL_DEBUG,
|
||||
"RAB Assignment: setting up GTP tunnel mapping via UPF %s\n",
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, &hnb_gw->pfcp.cp_peer->remote_addr));
|
||||
return hnbgw_gtpmap_rx_rab_ass_req(map, oph, 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 the rua_tx_dt() call below. */
|
||||
hnbgw_gtpmap_release(map);
|
||||
break;
|
||||
}
|
||||
ranap_ran_rx_co_free(message);
|
||||
}
|
||||
|
||||
talloc_free(message);
|
||||
#endif
|
||||
}
|
||||
|
||||
return rua_tx_dt(map->hnb_ctx, map->is_ps, map->rua_ctx_id,
|
||||
msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
}
|
||||
@@ -510,6 +593,8 @@ int hnbgw_cnlink_init(struct hnb_gw *gw, const char *stp_host, uint16_t stp_port
|
||||
LOGP(DRANAP, LOGL_NOTICE, "No cs7 instance configured for IuCS nor IuPS,"
|
||||
" creating default instance\n");
|
||||
ss7 = osmo_ss7_instance_find_or_create(gw, 0);
|
||||
if (!ss7)
|
||||
return -1;
|
||||
ss7->cfg.primary_pc = (23 << 3) + 5;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ static int hnbgw_tx_hnb_register_rej(struct hnb_context *ctx)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* generate a successfull outcome PDU */
|
||||
/* generate a unsuccessful outcome PDU */
|
||||
msg = hnbap_generate_unsuccessful_outcome(HNBAP_ProcedureCode_id_HNBRegister,
|
||||
HNBAP_Criticality_reject,
|
||||
&asn_DEF_HNBAP_HNBRegisterReject,
|
||||
@@ -111,7 +111,7 @@ static int hnbgw_tx_hnb_register_acc(struct hnb_context *ctx)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* generate a successfull outcome PDU */
|
||||
/* generate a successful outcome PDU */
|
||||
msg = hnbap_generate_successful_outcome(HNBAP_ProcedureCode_id_HNBRegister,
|
||||
HNBAP_Criticality_reject,
|
||||
&asn_DEF_HNBAP_HNBRegisterAccept,
|
||||
@@ -284,6 +284,7 @@ static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Ident
|
||||
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));
|
||||
@@ -331,14 +332,19 @@ static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Ident
|
||||
|
||||
ue = ue_context_by_tmsi(hnb->gw, tmsi);
|
||||
if (!ue)
|
||||
ue = ue_context_alloc(hnb, NULL, tmsi);
|
||||
ue = ue_allocated = ue_context_alloc(hnb, NULL, tmsi);
|
||||
|
||||
asn1_u24_to_bitstring(&accept.context_ID, &ctx_id, ue->context_id);
|
||||
|
||||
memset(&accept_out, 0, sizeof(accept_out));
|
||||
rc = hnbap_encode_ueregisteraccepties(&accept_out, &accept);
|
||||
if (rc < 0)
|
||||
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);
|
||||
return rc;
|
||||
}
|
||||
|
||||
msg = hnbap_generate_successful_outcome(HNBAP_ProcedureCode_id_UERegister,
|
||||
HNBAP_Criticality_reject,
|
||||
@@ -388,21 +394,26 @@ static int hnbgw_rx_hnb_deregister(struct hnb_context *ctx, ANY_t *in)
|
||||
LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "HNB-DE-REGISTER cause=%s\n", hnbap_cause_str(&ies.cause));
|
||||
|
||||
hnbap_free_hnbde_registeries(&ies);
|
||||
hnb_context_release(ctx);
|
||||
ctx->hnb_registered = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
{
|
||||
struct hnb_context *hnb;
|
||||
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 name[OSMO_SOCK_NAME_MAXLEN];
|
||||
|
||||
osmo_sock_get_name_buf(name, sizeof(name), ofd->fd);
|
||||
|
||||
rc = hnbap_decode_hnbregisterrequesties(&ies, in);
|
||||
if (rc < 0) {
|
||||
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Failure to decode HNB-REGISTER-REQ from %s: rc=%d\n",
|
||||
ctx->identity_info, rc);
|
||||
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Failure to decode HNB-REGISTER-REQ %s from %s: rc=%d\n",
|
||||
ctx->identity_info, name, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -413,23 +424,52 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
ctx->id.sac = asn1str_to_u16(&ies.sac);
|
||||
ctx->id.rac = asn1str_to_u8(&ies.rac);
|
||||
ctx->id.cid = asn1bitstr_to_u28(&ies.cellIdentity);
|
||||
gsm48_mcc_mnc_from_bcd(ies.plmNidentity.buf, &ctx->id.mcc, &ctx->id.mnc);
|
||||
osmo_plmn_from_bcd(ies.plmNidentity.buf, &plmn);
|
||||
ctx->id.mcc = plmn.mcc;
|
||||
ctx->id.mnc = plmn.mnc;
|
||||
|
||||
llist_for_each_entry(hnb, &ctx->gw->hnb_list, list) {
|
||||
llist_for_each_entry_safe(hnb, tmp, &ctx->gw->hnb_list, list) {
|
||||
if (hnb->hnb_registered && ctx != hnb && memcmp(&ctx->id, &hnb->id, sizeof(ctx->id)) == 0) {
|
||||
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(ctx->conn);
|
||||
char *name = osmo_sock_get_name(ctx, ofd->fd);
|
||||
/* If it's coming from the same remote IP addr+port, then it must be our internal
|
||||
* 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) {
|
||||
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "BUG! Found old registered HNB with same remote address, releasing it\n");
|
||||
hnb_context_release(hnb);
|
||||
continue;
|
||||
} /* else: addresses are different, we continue below */
|
||||
|
||||
/* If new conn registering same HNB is from anoter remote addr+port, let's reject it to avoid
|
||||
* misconfigurations or someone trying to impersonate an already working HNB: */
|
||||
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "rejecting HNB-REGISTER-REQ with duplicate cell identity "
|
||||
"MCC=%u,MNC=%u,LAC=%u,RAC=%u,SAC=%u,CID=%u from %s\n",
|
||||
ctx->id.mcc, ctx->id.mnc, ctx->id.lac, ctx->id.rac, ctx->id.sac, ctx->id.cid, name);
|
||||
talloc_free(name);
|
||||
hnbap_free_hnbregisterrequesties(&ies);
|
||||
return hnbgw_tx_hnb_register_rej(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
ctx->hnb_registered = true;
|
||||
LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "HNB-REGISTER-REQ %s MCC=%u,MNC=%u,LAC=%u,RAC=%u,SAC=%u,CID=%u from %s%s\n",
|
||||
ctx->identity_info, ctx->id.mcc, ctx->id.mnc, ctx->id.lac, ctx->id.rac, ctx->id.sac, ctx->id.cid,
|
||||
name, ctx->hnb_registered ? " (duplicated)" : "");
|
||||
|
||||
LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "HNB-REGISTER-REQ from %s\n", ctx->identity_info);
|
||||
ctx->hnb_registered = true;
|
||||
|
||||
/* Send HNBRegisterAccept */
|
||||
rc = hnbgw_tx_hnb_register_acc(ctx);
|
||||
@@ -441,6 +481,7 @@ static int hnbgw_rx_ue_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
{
|
||||
HNBAP_UERegisterRequestIEs_t ies;
|
||||
struct ue_context *ue;
|
||||
struct ue_context *ue_allocated = NULL;
|
||||
char imsi[16];
|
||||
int rc;
|
||||
|
||||
@@ -482,11 +523,18 @@ static int hnbgw_rx_ue_register_req(struct hnb_context *ctx, ANY_t *in)
|
||||
|
||||
ue = ue_context_by_imsi(ctx->gw, imsi);
|
||||
if (!ue)
|
||||
ue = ue_context_alloc(ctx, imsi, 0);
|
||||
ue = ue_allocated = ue_context_alloc(ctx, imsi, 0);
|
||||
|
||||
hnbap_free_ueregisterrequesties(&ies);
|
||||
/* Send UERegisterAccept */
|
||||
return hnbgw_tx_ue_register_acc(ue);
|
||||
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);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnbgw_rx_ue_deregister(struct hnb_context *ctx, ANY_t *in)
|
||||
@@ -531,32 +579,47 @@ static int hnbgw_rx_initiating_msg(struct hnb_context *hnb, HNBAP_InitiatingMess
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (imsg->procedureCode) {
|
||||
case HNBAP_ProcedureCode_id_HNBRegister: /* 8.2 */
|
||||
rc = hnbgw_rx_hnb_register_req(hnb, &imsg->value);
|
||||
break;
|
||||
case HNBAP_ProcedureCode_id_HNBDe_Register: /* 8.3 */
|
||||
rc = hnbgw_rx_hnb_deregister(hnb, &imsg->value);
|
||||
break;
|
||||
case HNBAP_ProcedureCode_id_UERegister: /* 8.4 */
|
||||
rc = hnbgw_rx_ue_register_req(hnb, &imsg->value);
|
||||
break;
|
||||
case HNBAP_ProcedureCode_id_UEDe_Register: /* 8.5 */
|
||||
rc = hnbgw_rx_ue_deregister(hnb, &imsg->value);
|
||||
break;
|
||||
case HNBAP_ProcedureCode_id_ErrorIndication: /* 8.6 */
|
||||
rc = hnbgw_rx_err_ind(hnb, &imsg->value);
|
||||
break;
|
||||
case HNBAP_ProcedureCode_id_TNLUpdate: /* 8.9 */
|
||||
case HNBAP_ProcedureCode_id_HNBConfigTransfer: /* 8.10 */
|
||||
case HNBAP_ProcedureCode_id_RelocationComplete: /* 8.11 */
|
||||
case HNBAP_ProcedureCode_id_U_RNTIQuery: /* 8.12 */
|
||||
case HNBAP_ProcedureCode_id_privateMessage:
|
||||
LOGHNB(hnb, DHNBAP, LOGL_NOTICE, "Unimplemented HNBAP Procedure %ld\n", imsg->procedureCode);
|
||||
break;
|
||||
default:
|
||||
LOGHNB(hnb, DHNBAP, LOGL_NOTICE, "Unknown HNBAP Procedure %ld\n", imsg->procedureCode);
|
||||
break;
|
||||
if (!hnb->hnb_registered) {
|
||||
switch (imsg->procedureCode) {
|
||||
case HNBAP_ProcedureCode_id_HNBRegister: /* 8.2 */
|
||||
rc = hnbgw_rx_hnb_register_req(hnb, &imsg->value);
|
||||
break;
|
||||
case HNBAP_ProcedureCode_id_HNBDe_Register: /* 8.3 */
|
||||
rc = hnbgw_rx_hnb_deregister(hnb, &imsg->value);
|
||||
break;
|
||||
default:
|
||||
LOGHNB(hnb, DHNBAP, LOGL_NOTICE, "HNBAP Procedure %ld not permitted for de-registered HNB\n",
|
||||
imsg->procedureCode);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (imsg->procedureCode) {
|
||||
case HNBAP_ProcedureCode_id_HNBRegister: /* 8.2.4: Abnormal Condition, Accept. */
|
||||
rc = hnbgw_rx_hnb_register_req(hnb, &imsg->value);
|
||||
break;
|
||||
case HNBAP_ProcedureCode_id_HNBDe_Register: /* 8.3 */
|
||||
rc = hnbgw_rx_hnb_deregister(hnb, &imsg->value);
|
||||
break;
|
||||
case HNBAP_ProcedureCode_id_UERegister: /* 8.4 */
|
||||
rc = hnbgw_rx_ue_register_req(hnb, &imsg->value);
|
||||
break;
|
||||
case HNBAP_ProcedureCode_id_UEDe_Register: /* 8.5 */
|
||||
rc = hnbgw_rx_ue_deregister(hnb, &imsg->value);
|
||||
break;
|
||||
case HNBAP_ProcedureCode_id_ErrorIndication: /* 8.6 */
|
||||
rc = hnbgw_rx_err_ind(hnb, &imsg->value);
|
||||
break;
|
||||
case HNBAP_ProcedureCode_id_TNLUpdate: /* 8.9 */
|
||||
case HNBAP_ProcedureCode_id_HNBConfigTransfer: /* 8.10 */
|
||||
case HNBAP_ProcedureCode_id_RelocationComplete: /* 8.11 */
|
||||
case HNBAP_ProcedureCode_id_U_RNTIQuery: /* 8.12 */
|
||||
case HNBAP_ProcedureCode_id_privateMessage:
|
||||
LOGHNB(hnb, DHNBAP, LOGL_NOTICE, "Unimplemented HNBAP Procedure %ld\n", imsg->procedureCode);
|
||||
break;
|
||||
default:
|
||||
LOGHNB(hnb, DHNBAP, LOGL_NOTICE, "Unknown HNBAP Procedure %ld\n", imsg->procedureCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
||||
148
src/osmo-hnbgw/hnbgw_pfcp.c
Normal file
148
src/osmo-hnbgw/hnbgw_pfcp.c
Normal file
@@ -0,0 +1,148 @@
|
||||
/* PFCP link to UPF for osmo-hnbgw */
|
||||
/* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* 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/sockaddr_str.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>
|
||||
|
||||
static void pfcp_set_msg_ctx(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m, struct osmo_pfcp_msg *req)
|
||||
{
|
||||
struct hnb_gw *hnb_gw = osmo_pfcp_endpoint_get_priv(ep);
|
||||
|
||||
if (!m->ctx.peer_fi)
|
||||
osmo_pfcp_cp_peer_set_msg_ctx(hnb_gw->pfcp.cp_peer, m);
|
||||
|
||||
/* If this is a response to an earlier request, just take the msg context from the request message.
|
||||
* In osmo-hnbgw, a session_fi always points at a ps_rab FSM. */
|
||||
if (!m->ctx.session_fi && req && req->ctx.session_fi)
|
||||
ps_rab_pfcp_set_msg_ctx(req->ctx.session_fi->priv, m);
|
||||
|
||||
/* Otherwise iterate all PS RABs in all hnb contexts matching on the SEID. This rarely happens at all: for tx,
|
||||
* ps_rab_new_pfcp_msg_tx() already sets the msg ctx, and for rx, we only expect to receive PFCP Responses,
|
||||
* which are handled above. The only time this will happen is when the UPF shuts down and sends a Deletion. */
|
||||
if (!m->ctx.session_fi && m->h.seid_present && m->h.seid != 0) {
|
||||
struct ps_rab *rab = ps_rab_find_by_seid(hnb_gw, m->h.seid, m->rx);
|
||||
if (rab)
|
||||
ps_rab_pfcp_set_msg_ctx(rab, m);
|
||||
}
|
||||
}
|
||||
|
||||
static void pfcp_rx_msg(struct osmo_pfcp_endpoint *ep, struct osmo_pfcp_msg *m, struct osmo_pfcp_msg *req)
|
||||
{
|
||||
switch (m->h.message_type) {
|
||||
|
||||
/* We only expect responses to requests. Those are handled by osmo_pfcp_msg.ctx.resp_cb. */
|
||||
|
||||
/* TODO: handle graceful shutdown from UPF (Session Modification? Deletion?) */
|
||||
|
||||
default:
|
||||
LOGP(DLPFCP, LOGL_ERROR, "rx unexpected PFCP message: %s\n",
|
||||
osmo_pfcp_message_type_str(m->h.message_type));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int hnbgw_pfcp_init(struct hnb_gw *hnb_gw)
|
||||
{
|
||||
struct osmo_pfcp_endpoint_cfg cfg;
|
||||
struct osmo_pfcp_endpoint *ep;
|
||||
struct osmo_sockaddr_str local_addr_str;
|
||||
struct osmo_sockaddr_str upf_addr_str;
|
||||
struct osmo_sockaddr upf_addr;
|
||||
|
||||
if (!hnb_gw_is_gtp_mapping_enabled(hnb_gw)) {
|
||||
LOGP(DLPFCP, LOGL_NOTICE, "No UPF configured, NOT setting up PFCP, NOT mapping GTP via UPF\n");
|
||||
return 0;
|
||||
}
|
||||
LOGP(DLPFCP, LOGL_DEBUG, "%p cfg: pfcp remote-addr %s\n", hnb_gw, hnb_gw->config.pfcp.remote_addr);
|
||||
|
||||
if (!hnb_gw->config.pfcp.local_addr) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Configuration error: missing local PFCP address, required for Node Id\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cfg = (struct osmo_pfcp_endpoint_cfg){
|
||||
.set_msg_ctx_cb = pfcp_set_msg_ctx,
|
||||
.rx_msg_cb = pfcp_rx_msg,
|
||||
.priv = hnb_gw,
|
||||
};
|
||||
|
||||
/* Set up PFCP endpoint's local node id from local IP address. Parse address string into local_addr_str... */
|
||||
if (osmo_sockaddr_str_from_str(&local_addr_str, hnb_gw->config.pfcp.local_addr, hnb_gw->config.pfcp.local_port)) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Error in PFCP local IP: %s\n",
|
||||
osmo_quote_str_c(OTC_SELECT, hnb_gw->config.pfcp.local_addr, -1));
|
||||
return -1;
|
||||
}
|
||||
/* ...and convert to osmo_sockaddr, write to ep->cfg */
|
||||
if (osmo_sockaddr_str_to_sockaddr(&local_addr_str, &cfg.local_addr.u.sas)) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Error in PFCP local IP: %s\n",
|
||||
osmo_quote_str_c(OTC_SELECT, hnb_gw->config.pfcp.local_addr, -1));
|
||||
return -1;
|
||||
}
|
||||
/* also store the local addr as local Node ID */
|
||||
if (osmo_pfcp_ie_node_id_from_osmo_sockaddr(&cfg.local_node_id, &cfg.local_addr)) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Error in PFCP local IP: %s\n",
|
||||
osmo_quote_str_c(OTC_SELECT, hnb_gw->config.pfcp.local_addr, -1));
|
||||
return -1;
|
||||
}
|
||||
|
||||
hnb_gw->pfcp.ep = ep = osmo_pfcp_endpoint_create(hnb_gw, &cfg);
|
||||
if (!ep) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Failed to allocate PFCP endpoint\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set up remote PFCP address to reach UPF at. First parse the string into upf_addr_str. */
|
||||
if (osmo_sockaddr_str_from_str(&upf_addr_str, hnb_gw->config.pfcp.remote_addr, hnb_gw->config.pfcp.remote_port)) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Error in PFCP remote IP: %s\n",
|
||||
osmo_quote_str_c(OTC_SELECT, hnb_gw->config.pfcp.remote_addr, -1));
|
||||
return -1;
|
||||
}
|
||||
/* then convert upf_addr_str to osmo_sockaddr */
|
||||
if (osmo_sockaddr_str_to_sockaddr(&upf_addr_str, &upf_addr.u.sas)) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Error in PFCP remote IP: %s\n",
|
||||
osmo_quote_str_c(OTC_SELECT, hnb_gw->config.pfcp.remote_addr, -1));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Start the socket */
|
||||
if (osmo_pfcp_endpoint_bind(ep)) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Cannot bind PFCP endpoint\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Associate with UPF */
|
||||
hnb_gw->pfcp.cp_peer = osmo_pfcp_cp_peer_alloc(hnb_gw, ep, &upf_addr);
|
||||
if (!hnb_gw->pfcp.cp_peer) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Cannot allocate PFCP CP Peer FSM\n");
|
||||
return -1;
|
||||
}
|
||||
if (osmo_pfcp_cp_peer_associate(hnb_gw->pfcp.cp_peer)) {
|
||||
LOGP(DLPFCP, LOGL_ERROR, "Cannot start PFCP CP Peer FSM\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
@@ -38,6 +39,11 @@
|
||||
#include <osmocom/rua/rua_ies_defs.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbap/HNBAP_CN-DomainIndicator.h>
|
||||
#include <osmocom/hnbgw/mgw_fsm.h>
|
||||
#include <osmocom/hnbgw/ps_rab_ass_fsm.h>
|
||||
#include <osmocom/ranap/RANAP_ProcedureCode.h>
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/ranap_common_cn.h>
|
||||
|
||||
static const char *cn_domain_indicator_to_str(RUA_CN_DomainIndicator_t cN_DomainIndicator)
|
||||
{
|
||||
@@ -173,11 +179,11 @@ int rua_tx_disc(struct hnb_context *hnb, int is_ps, uint32_t context_id,
|
||||
|
||||
|
||||
/* forward a RUA message to the SCCP User API to SCCP */
|
||||
static int rua_to_scu(struct hnb_context *hnb,
|
||||
RUA_CN_DomainIndicator_t cN_DomainIndicator,
|
||||
enum osmo_scu_prim_type type,
|
||||
uint32_t context_id, uint32_t cause,
|
||||
const uint8_t *data, unsigned int len)
|
||||
int rua_to_scu(struct hnb_context *hnb,
|
||||
RUA_CN_DomainIndicator_t cN_DomainIndicator,
|
||||
enum osmo_scu_prim_type type,
|
||||
uint32_t context_id, uint32_t cause,
|
||||
const uint8_t *data, unsigned int len)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct osmo_scu_prim *prim;
|
||||
@@ -186,6 +192,7 @@ static int rua_to_scu(struct hnb_context *hnb,
|
||||
struct osmo_sccp_addr *remote_addr;
|
||||
bool is_ps;
|
||||
bool release_context_map = false;
|
||||
ranap_message *message;
|
||||
int rc;
|
||||
|
||||
switch (cN_DomainIndicator) {
|
||||
@@ -220,9 +227,9 @@ static int rua_to_scu(struct hnb_context *hnb,
|
||||
default:
|
||||
map = context_map_alloc_by_hnb(hnb, context_id, is_ps, cn);
|
||||
OSMO_ASSERT(map);
|
||||
LOGHNB(hnb, DRUA, LOGL_DEBUG, "rua_to_scu() %s to %s, rua_ctx_id %u scu_conn_id %u\n",
|
||||
LOGHNB(hnb, DRUA, LOGL_DEBUG, "rua_to_scu() %s to %s, rua_ctx_id %u scu_conn_id %u data-len %u\n",
|
||||
cn_domain_indicator_to_str(cN_DomainIndicator), osmo_sccp_addr_dump(remote_addr),
|
||||
map->rua_ctx_id, map->scu_conn_id);
|
||||
map->rua_ctx_id, map->scu_conn_id, len);
|
||||
}
|
||||
|
||||
/* add primitive header */
|
||||
@@ -245,6 +252,8 @@ static int rua_to_scu(struct hnb_context *hnb,
|
||||
prim->u.disconnect.conn_id = map->scu_conn_id;
|
||||
prim->u.disconnect.cause = cause;
|
||||
release_context_map = true;
|
||||
/* Mark SCCP conn as gracefully disconnected */
|
||||
map->scu_conn_active = false;
|
||||
break;
|
||||
case OSMO_SCU_PRIM_N_UNITDATA:
|
||||
prim->u.unitdata.called_addr = *remote_addr;
|
||||
@@ -259,12 +268,50 @@ static int rua_to_scu(struct hnb_context *hnb,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* add optional data section, if needed */
|
||||
/* If there is RANAP data, include it in the msgb. Usually there is data, but this could also be an SCCP CR
|
||||
* a.k.a. OSMO_SCU_PRIM_N_CONNECT without RANAP payload. */
|
||||
if (data && len) {
|
||||
msg->l2h = msgb_put(msg, len);
|
||||
memcpy(msg->l2h, data, len);
|
||||
}
|
||||
|
||||
/* If there is data, see if it is a RAB Assignment message where we need to change the user plane information,
|
||||
* for RTP mapping via MGW (soon also GTP mapping via UPF). */
|
||||
if (data && len && map && !release_context_map) {
|
||||
if (!map->is_ps) {
|
||||
message = talloc_zero(map, ranap_message);
|
||||
rc = ranap_cn_rx_co_decode(map, message, msgb_l2(prim->oph.msg), msgb_l2len(prim->oph.msg));
|
||||
|
||||
if (rc == 0) {
|
||||
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_rab_ass_resp(map, &prim->oph, message);
|
||||
}
|
||||
ranap_cn_rx_co_free(message);
|
||||
}
|
||||
|
||||
talloc_free(message);
|
||||
#if ENABLE_PFCP
|
||||
} else if (hnb_gw_is_gtp_mapping_enabled(hnb->gw)) {
|
||||
/* map->is_ps == true and PFCP is enabled in osmo-hnbgw.cfg */
|
||||
message = talloc_zero(map, ranap_message);
|
||||
rc = ranap_cn_rx_co_decode(map, message, msgb_l2(prim->oph.msg), msgb_l2len(prim->oph.msg));
|
||||
|
||||
if (rc == 0) {
|
||||
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, &prim->oph, message);
|
||||
}
|
||||
ranap_cn_rx_co_free(message);
|
||||
}
|
||||
|
||||
talloc_free(message);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
rc = osmo_sccp_user_sap_down(cn->sccp_user, &prim->oph);
|
||||
|
||||
if (map && release_context_map)
|
||||
@@ -341,21 +388,69 @@ static int rua_rx_init_connect(struct msgb *msg, ANY_t *in)
|
||||
struct hnb_context *hnb = msg->dst;
|
||||
uint32_t context_id;
|
||||
int rc;
|
||||
const uint8_t *data;
|
||||
unsigned int data_len;
|
||||
|
||||
rc = rua_decode_connecties(&ies, in);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
context_id = asn1bitstr_to_u24(&ies.context_ID);
|
||||
data = ies.ranaP_Message.buf;
|
||||
data_len = ies.ranaP_Message.size;
|
||||
|
||||
LOGHNB(hnb, DRUA, LOGL_DEBUG, "RUA %s Connect.req(ctx=0x%x, %s)\n",
|
||||
cn_domain_indicator_to_str(ies.cN_DomainIndicator), context_id,
|
||||
ies.establishment_Cause == RUA_Establishment_Cause_emergency_call ? "emergency" : "normal");
|
||||
LOGHNB(hnb, DRUA, LOGL_DEBUG, "RUA %s Connect.req(ctx=0x%x, %s, RANAP.size=%u)\n",
|
||||
cn_domain_indicator_to_str(ies.cN_DomainIndicator), context_id,
|
||||
ies.establishment_Cause == RUA_Establishment_Cause_emergency_call ? "emergency" : "normal",
|
||||
data_len);
|
||||
|
||||
if (hnbgw_requires_empty_sccp_cr(hnb->gw, data_len)) {
|
||||
/* Do not include data in the SCCP CR, to avoid hitting a message size limit at the remote end that may
|
||||
* lead to rejection. */
|
||||
bool is_ps;
|
||||
struct osmo_sccp_addr *remote_addr;
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
switch (ies.cN_DomainIndicator) {
|
||||
case RUA_CN_DomainIndicator_cs_domain:
|
||||
remote_addr = &hnb->gw->sccp.iucs_remote_addr;
|
||||
is_ps = false;
|
||||
break;
|
||||
case RUA_CN_DomainIndicator_ps_domain:
|
||||
remote_addr = &hnb->gw->sccp.iups_remote_addr;
|
||||
is_ps = true;
|
||||
break;
|
||||
default:
|
||||
LOGHNB(hnb, DRUA, LOGL_ERROR, "Unsupported Domain %ld\n", ies.cN_DomainIndicator);
|
||||
rua_free_connecties(&ies);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!hnb->gw->sccp.cnlink) {
|
||||
LOGHNB(hnb, DRUA, LOGL_NOTICE, "CN=NULL, discarding message\n");
|
||||
rua_free_connecties(&ies);
|
||||
return 0;
|
||||
}
|
||||
|
||||
map = context_map_alloc_by_hnb(hnb, context_id, is_ps, hnb->gw->sccp.cnlink);
|
||||
OSMO_ASSERT(map);
|
||||
OSMO_ASSERT(map->is_ps == is_ps);
|
||||
LOGHNB(hnb, DRUA, LOGL_DEBUG, "rua_rx_init_connect() %s to %s, rua_ctx_id %u scu_conn_id %u;"
|
||||
" Sending SCCP CR without payload, caching %u octets\n",
|
||||
cn_domain_indicator_to_str(ies.cN_DomainIndicator), osmo_sccp_addr_dump(remote_addr),
|
||||
map->rua_ctx_id, map->scu_conn_id, data_len);
|
||||
|
||||
map->cached_msg = msgb_alloc_c(map, data_len, "map.cached_msg");
|
||||
OSMO_ASSERT(map->cached_msg);
|
||||
memcpy(msgb_put(map->cached_msg, data_len), data, data_len);
|
||||
|
||||
/* Data is cached for after CR is confirmed, send SCCP CR but omit payload. */
|
||||
data = NULL;
|
||||
data_len = 0;
|
||||
}
|
||||
|
||||
rc = rua_to_scu(hnb, ies.cN_DomainIndicator, OSMO_SCU_PRIM_N_CONNECT,
|
||||
context_id, 0, ies.ranaP_Message.buf,
|
||||
ies.ranaP_Message.size);
|
||||
|
||||
context_id, 0, data, data_len);
|
||||
rua_free_connecties(&ies);
|
||||
|
||||
return rc;
|
||||
|
||||
@@ -18,19 +18,25 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/vty/command.h>
|
||||
#include <osmocom/vty/tdef_vty.h>
|
||||
|
||||
#include <osmocom/hnbgw/vty.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
#include <osmocom/sigtran/protocol/sua.h>
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/mgcp_client/mgcp_client.h>
|
||||
|
||||
static void *tall_hnb_ctx = NULL;
|
||||
static struct hnb_gw *g_hnb_gw = NULL;
|
||||
|
||||
@@ -86,6 +92,19 @@ DEFUN(cfg_hnbgw_iups, cfg_hnbgw_iups_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static struct cmd_node mgcp_node = {
|
||||
MGCP_NODE,
|
||||
"%s(config-hnbgw-mgcp)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
DEFUN(cfg_hnbgw_mgcp, cfg_hnbgw_mgcp_cmd,
|
||||
"mgcp", "Configure MGCP client")
|
||||
{
|
||||
vty->node = MGCP_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int hnbgw_vty_go_parent(struct vty *vty)
|
||||
{
|
||||
switch (vty->node) {
|
||||
@@ -95,6 +114,10 @@ int hnbgw_vty_go_parent(struct vty *vty)
|
||||
vty->node = HNBGW_NODE;
|
||||
vty->index = NULL;
|
||||
break;
|
||||
case MGCP_NODE:
|
||||
vty->node = HNBGW_NODE;
|
||||
vty->index = NULL;
|
||||
break;
|
||||
case HNBGW_NODE:
|
||||
vty->node = CONFIG_NODE;
|
||||
vty->index = NULL;
|
||||
@@ -309,6 +332,18 @@ DEFUN(cfg_hnbgw_log_prefix, cfg_hnbgw_log_prefix_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_hnbgw_max_sccp_cr_payload_len, cfg_hnbgw_max_sccp_cr_payload_len_cmd,
|
||||
"sccp cr max-payload-len <0-999999>",
|
||||
"Configure SCCP behavior\n"
|
||||
"Configure SCCP Connection Request\n"
|
||||
"Set an upper bound for payload data length included directly in the CR. If an initial RUA message has a"
|
||||
" RANAP payload larger than this value (octets), send an SCCP CR without data, followed by an SCCP DT."
|
||||
" This may be necessary if the remote component has a size limit on valid SCCP CR messages.\n")
|
||||
{
|
||||
g_hnb_gw->config.max_sccp_cr_payload_len = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_hnbgw_iucs_remote_addr,
|
||||
cfg_hnbgw_iucs_remote_addr_cmd,
|
||||
"remote-addr NAME",
|
||||
@@ -329,11 +364,59 @@ DEFUN(cfg_hnbgw_iups_remote_addr,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#if ENABLE_PFCP
|
||||
|
||||
static struct cmd_node pfcp_node = {
|
||||
PFCP_NODE,
|
||||
"%s(config-hnbgw-pfcp)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
DEFUN(cfg_hnbgw_pfcp, cfg_hnbgw_pfcp_cmd,
|
||||
"pfcp", "Configure PFCP for GTP tunnel mapping")
|
||||
{
|
||||
vty->node = PFCP_NODE;
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pfcp_remote_addr, cfg_pfcp_remote_addr_cmd,
|
||||
"remote-addr IP_ADDR",
|
||||
"Remote UPF's listen IP address; where to send PFCP requests\n"
|
||||
"IP address\n")
|
||||
{
|
||||
osmo_talloc_replace_string(g_hnb_gw, &g_hnb_gw->config.pfcp.remote_addr, argv[0]);
|
||||
LOGP(DLPFCP, LOGL_NOTICE, "%p cfg: pfcp remote-addr %s\n", g_hnb_gw, g_hnb_gw->config.pfcp.remote_addr);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pfcp_local_addr, cfg_pfcp_local_addr_cmd,
|
||||
"local-addr IP_ADDR",
|
||||
"Local address for PFCP\n"
|
||||
"IP address\n")
|
||||
{
|
||||
osmo_talloc_replace_string(g_hnb_gw, &g_hnb_gw->config.pfcp.local_addr, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pfcp_local_port, cfg_pfcp_local_port_cmd,
|
||||
"local-port <1-65535>",
|
||||
"Local port for PFCP\n"
|
||||
"IP port\n")
|
||||
{
|
||||
g_hnb_gw->config.pfcp.local_port = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#endif /* ENABLE_PFCP */
|
||||
|
||||
static int config_write_hnbgw(struct vty *vty)
|
||||
{
|
||||
vty_out(vty, "hnbgw%s", VTY_NEWLINE);
|
||||
vty_out(vty, " log-prefix %s%s", g_hnb_gw->config.log_prefix_hnb_id ? "hnb-id" : "umts-cell-id",
|
||||
VTY_NEWLINE);
|
||||
if (g_hnb_gw->config.max_sccp_cr_payload_len != 999999)
|
||||
vty_out(vty, " sccp cr max-payload-len %u%s", g_hnb_gw->config.max_sccp_cr_payload_len, VTY_NEWLINE);
|
||||
osmo_tdef_vty_groups_write(vty, " ");
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -382,6 +465,19 @@ static int config_write_hnbgw_iups(struct vty *vty)
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#if ENABLE_PFCP
|
||||
static int config_write_hnbgw_pfcp(struct vty *vty)
|
||||
{
|
||||
vty_out(vty, " pfcp%s", VTY_NEWLINE);
|
||||
if (g_hnb_gw->config.pfcp.local_addr)
|
||||
vty_out(vty, " local-addr %s%s", g_hnb_gw->config.pfcp.local_addr, VTY_NEWLINE);
|
||||
if (g_hnb_gw->config.pfcp.remote_addr)
|
||||
vty_out(vty, " remote-addr %s%s", g_hnb_gw->config.pfcp.remote_addr, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
void hnbgw_vty_init(struct hnb_gw *gw, void *tall_ctx)
|
||||
{
|
||||
g_hnb_gw = gw;
|
||||
@@ -392,6 +488,7 @@ void hnbgw_vty_init(struct hnb_gw *gw, void *tall_ctx)
|
||||
|
||||
install_element(HNBGW_NODE, &cfg_hnbgw_rnc_id_cmd);
|
||||
install_element(HNBGW_NODE, &cfg_hnbgw_log_prefix_cmd);
|
||||
install_element(HNBGW_NODE, &cfg_hnbgw_max_sccp_cr_payload_len_cmd);
|
||||
|
||||
install_element(HNBGW_NODE, &cfg_hnbgw_iuh_cmd);
|
||||
install_node(&iuh_node, config_write_hnbgw_iuh);
|
||||
@@ -415,4 +512,21 @@ void hnbgw_vty_init(struct hnb_gw *gw, void *tall_ctx)
|
||||
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);
|
||||
/* Deprecated: Old MGCP config without pooling support in MSC node: */
|
||||
install_node(&mgcp_node, NULL);
|
||||
mgcp_client_vty_init(tall_hnb_ctx, MGCP_NODE, g_hnb_gw->config.mgcp_client);
|
||||
|
||||
mgcp_client_pool_vty_init(HNBGW_NODE, MGW_NODE, " ", g_hnb_gw->mgw_pool);
|
||||
|
||||
#if ENABLE_PFCP
|
||||
install_node(&pfcp_node, config_write_hnbgw_pfcp);
|
||||
install_element(HNBGW_NODE, &cfg_hnbgw_pfcp_cmd);
|
||||
install_element(PFCP_NODE, &cfg_pfcp_local_addr_cmd);
|
||||
install_element(PFCP_NODE, &cfg_pfcp_local_port_cmd);
|
||||
install_element(PFCP_NODE, &cfg_pfcp_remote_addr_cmd);
|
||||
#endif
|
||||
|
||||
osmo_tdef_vty_groups_init(HNBGW_NODE, hnbgw_tdef_group);
|
||||
}
|
||||
|
||||
792
src/osmo-hnbgw/mgw_fsm.c
Normal file
792
src/osmo-hnbgw/mgw_fsm.c
Normal file
@@ -0,0 +1,792 @@
|
||||
/* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/prim.h>
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/byteswap.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/ranap_common_cn.h>
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
#include <osmocom/ranap/ranap_msg_factory.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/ranap/iu_helpers.h>
|
||||
#include <asn1c/asn1helpers.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/hnbgw/context_map.h>
|
||||
#include <osmocom/hnbgw/ranap_rab_ass.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
|
||||
|
||||
/* NOTE: This implementation can only handle one RAB per hnbgw context. This simplification was made because usually
|
||||
* a voice call will require only one RAB at a time. An exception may be corner cases like video calls, which we
|
||||
* do not support at the moment. */
|
||||
|
||||
/* Send Iu Release Request, this is done in erroneous cases from which we cannot recover */
|
||||
static void tx_release_req(struct hnbgw_context_map *map)
|
||||
{
|
||||
struct hnb_context *hnb = map->hnb_ctx;
|
||||
struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink;
|
||||
struct msgb *msg;
|
||||
struct osmo_scu_prim *prim;
|
||||
static const struct RANAP_Cause cause = {
|
||||
.present = RANAP_Cause_PR_transmissionNetwork,
|
||||
.choice.transmissionNetwork =
|
||||
RANAP_CauseTransmissionNetwork_iu_transport_connection_failed_to_establish,
|
||||
};
|
||||
|
||||
msg = ranap_new_msg_iu_rel_req(&cause);
|
||||
msg->l2h = msg->data;
|
||||
|
||||
prim = (struct osmo_scu_prim *)msgb_push(msg, sizeof(*prim));
|
||||
prim->u.data.conn_id = map->scu_conn_id;
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, msg);
|
||||
osmo_sccp_user_sap_down(cn->sccp_user, &prim->oph);
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
extern int asn1_xer_print;
|
||||
|
||||
enum mgw_fsm_event {
|
||||
MGW_EV_MGCP_OK,
|
||||
MGW_EV_MGCP_FAIL,
|
||||
MGW_EV_MGCP_TERM,
|
||||
MGW_EV_RAB_ASS_RESP,
|
||||
MGW_EV_RELEASE,
|
||||
};
|
||||
|
||||
static const struct value_string mgw_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(MGW_EV_MGCP_OK),
|
||||
OSMO_VALUE_STRING(MGW_EV_MGCP_FAIL),
|
||||
OSMO_VALUE_STRING(MGW_EV_MGCP_TERM),
|
||||
OSMO_VALUE_STRING(MGW_EV_RAB_ASS_RESP),
|
||||
OSMO_VALUE_STRING(MGW_EV_RELEASE),
|
||||
{}
|
||||
};
|
||||
|
||||
enum mgw_fsm_state {
|
||||
MGW_ST_CRCX_HNB,
|
||||
MGW_ST_ASSIGN,
|
||||
MGW_ST_MDCX_HNB,
|
||||
MGW_ST_CRCX_MSC,
|
||||
MGW_ST_ESTABLISHED,
|
||||
MGW_ST_RELEASE,
|
||||
MGW_ST_FAILURE,
|
||||
};
|
||||
|
||||
struct mgw_fsm_priv {
|
||||
/* Backpointer to HNBGW context */
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
/* RAB-ID from RANAP RAB AssignmentRequest message */
|
||||
uint8_t rab_id;
|
||||
|
||||
/* Pointers to messages and prim header we take ownership of */
|
||||
ranap_message *ranap_rab_ass_req_message;
|
||||
ranap_message *ranap_rab_ass_resp_message;
|
||||
struct osmo_prim_hdr *ranap_rab_ass_resp_oph;
|
||||
|
||||
/* MGW context */
|
||||
struct mgcp_client *mgcpc;
|
||||
struct osmo_mgcpc_ep *mgcpc_ep;
|
||||
struct osmo_mgcpc_ep_ci *mgcpc_ep_ci_hnb;
|
||||
struct osmo_mgcpc_ep_ci *mgcpc_ep_ci_msc;
|
||||
char msc_rtp_addr[INET6_ADDRSTRLEN];
|
||||
uint16_t msc_rtp_port;
|
||||
};
|
||||
|
||||
struct osmo_tdef_state_timeout mgw_fsm_timeouts[32] = {
|
||||
[MGW_ST_CRCX_HNB] = {.T = -1001 },
|
||||
[MGW_ST_ASSIGN] = {.T = -1002 },
|
||||
[MGW_ST_MDCX_HNB] = {.T = -1003 },
|
||||
[MGW_ST_CRCX_MSC] = {.T = -1004 },
|
||||
};
|
||||
|
||||
#define mgw_fsm_state_chg(fi, state) \
|
||||
osmo_tdef_fsm_inst_state_chg(fi, state, mgw_fsm_timeouts, mgw_fsm_T_defs, -1)
|
||||
|
||||
static void mgw_fsm_crcx_hnb_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
struct hnbgw_context_map *map = mgw_fsm_priv->map;
|
||||
struct osmo_sockaddr addr;
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies;
|
||||
const char *epname;
|
||||
struct mgcp_conn_peer mgw_info;
|
||||
int rc;
|
||||
|
||||
LOGPFSML(fi, LOGL_DEBUG, "RAB-AssignmentRequest received, creating HNB side call-leg on MGW...\n");
|
||||
|
||||
/* Parse the RAB Assignment Request now */
|
||||
ies = &mgw_fsm_priv->ranap_rab_ass_req_message->msg.raB_AssignmentRequestIEs;
|
||||
rc = ranap_rab_ass_req_ies_extract_inet_addr(&addr, &mgw_fsm_priv->rab_id, ies, 0);
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Invalid RAB-AssignmentRequest -- abort\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = osmo_sockaddr_str_from_sockaddr(&addr_str, &addr.u.sas);
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"Invalid RTP IP-address or port in RAB-AssignmentRequest -- abort\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
osmo_strlcpy(mgw_fsm_priv->msc_rtp_addr, addr_str.ip, sizeof(mgw_fsm_priv->msc_rtp_addr));
|
||||
mgw_fsm_priv->msc_rtp_port = addr_str.port;
|
||||
|
||||
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,
|
||||
};
|
||||
mgw_info.codecs[0] = CODEC_IUFP;
|
||||
mgw_info.codecs_len = 1;
|
||||
|
||||
mgw_fsm_priv->mgcpc = mgcp_client_pool_get(map->hnb_ctx->gw->mgw_pool);
|
||||
if (!mgw_fsm_priv->mgcpc) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"cannot ensure MGW endpoint -- no MGW configured, check configuration!\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
epname = mgcp_client_rtpbridge_wildcard(mgw_fsm_priv->mgcpc);
|
||||
mgw_fsm_priv->mgcpc_ep =
|
||||
osmo_mgcpc_ep_alloc(fi, MGW_EV_MGCP_TERM, mgw_fsm_priv->mgcpc, mgw_fsm_T_defs, fi->id, "%s", epname);
|
||||
mgw_fsm_priv->mgcpc_ep_ci_hnb = osmo_mgcpc_ep_ci_add(mgw_fsm_priv->mgcpc_ep, "to-HNB");
|
||||
|
||||
osmo_mgcpc_ep_ci_request(mgw_fsm_priv->mgcpc_ep_ci_hnb, MGCP_VERB_CRCX, &mgw_info, fi, MGW_EV_MGCP_OK,
|
||||
MGW_EV_MGCP_FAIL, NULL);
|
||||
}
|
||||
|
||||
static void mgw_fsm_crcx_hnb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
const struct mgcp_conn_peer *mgw_info;
|
||||
struct osmo_sockaddr addr;
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies;
|
||||
int rc;
|
||||
|
||||
switch (event) {
|
||||
case MGW_EV_MGCP_OK:
|
||||
mgw_info = osmo_mgcpc_ep_ci_get_rtp_info(mgw_fsm_priv->mgcpc_ep_ci_hnb);
|
||||
if (!mgw_info) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Got no RTP info response from MGW\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strchr(mgw_info->addr, '.'))
|
||||
addr_str.af = AF_INET;
|
||||
else
|
||||
addr_str.af = AF_INET6;
|
||||
addr_str.port = mgw_info->port;
|
||||
osmo_strlcpy(addr_str.ip, mgw_info->addr, sizeof(addr_str.ip));
|
||||
rc = osmo_sockaddr_str_to_sockaddr(&addr_str, &addr.u.sas);
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"Failed to convert RTP IP-address (%s) and Port (%u) to its binary representation\n",
|
||||
mgw_info->addr, mgw_info->port);
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ies = &mgw_fsm_priv->ranap_rab_ass_req_message->msg.raB_AssignmentRequestIEs;
|
||||
rc = ranap_rab_ass_req_ies_replace_inet_addr(ies, &addr, mgw_fsm_priv->rab_id);
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"Failed to replace RTP IP-address (%s) and Port (%u) in RAB-AssignmentRequest\n",
|
||||
mgw_info->addr, mgw_info->port);
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
mgw_fsm_state_chg(fi, MGW_ST_ASSIGN);
|
||||
return;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void mgw_fsm_assign_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
struct hnbgw_context_map *map = mgw_fsm_priv->map;
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies;
|
||||
struct msgb *msg;
|
||||
|
||||
ies = &mgw_fsm_priv->ranap_rab_ass_req_message->msg.raB_AssignmentRequestIEs;
|
||||
msg = ranap_rab_ass_req_encode(ies);
|
||||
if (!msg) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "failed to re-encode RAB-AssignmentRequest message\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGPFSML(fi, LOGL_DEBUG, "forwarding modified RAB-AssignmentRequest to HNB\n");
|
||||
rua_tx_dt(map->hnb_ctx, map->is_ps, map->rua_ctx_id, msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
static void mgw_fsm_assign(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case MGW_EV_RAB_ASS_RESP:
|
||||
mgw_fsm_state_chg(fi, MGW_ST_MDCX_HNB);
|
||||
return;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void mgw_fsm_mdcx_hnb_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
struct hnbgw_context_map *map = mgw_fsm_priv->map;
|
||||
struct hnb_context *hnb = map->hnb_ctx;
|
||||
struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink;
|
||||
struct mgcp_conn_peer mgw_info;
|
||||
struct osmo_sockaddr addr;
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
RANAP_RAB_AssignmentResponseIEs_t *ies;
|
||||
int rc;
|
||||
bool rab_failed_at_hnb;
|
||||
|
||||
LOGPFSML(fi, LOGL_DEBUG, "RAB-AssignmentResponse received, completing HNB side call-leg on MGW...\n");
|
||||
|
||||
mgw_info = (struct mgcp_conn_peer) {
|
||||
.call_id = map->rua_ctx_id,
|
||||
.ptime = 20,
|
||||
.conn_mode = MGCP_CONN_RECV_SEND,
|
||||
};
|
||||
mgw_info.codecs[0] = CODEC_IUFP;
|
||||
mgw_info.codecs_len = 1;
|
||||
|
||||
ies = &mgw_fsm_priv->ranap_rab_ass_resp_message->msg.raB_AssignmentResponseIEs;
|
||||
rc = ranap_rab_ass_resp_ies_extract_inet_addr(&addr, ies, mgw_fsm_priv->rab_id);
|
||||
if (rc < 0) {
|
||||
rab_failed_at_hnb = ranap_rab_ass_resp_ies_check_failure(ies, mgw_fsm_priv->rab_id);
|
||||
if (rab_failed_at_hnb) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"The RAB-AssignmentResponse contains a RAB-FailedList, RAB-Assignment (%u) failed.\n",
|
||||
mgw_fsm_priv->rab_id);
|
||||
|
||||
/* Forward the RAB-AssignmentResponse transparently. This will ensure that the MSC is informed
|
||||
* about the problem. */
|
||||
LOGPFSML(fi, LOGL_DEBUG, "forwarding unmodified RAB-AssignmentResponse to MSC\n");
|
||||
rc = osmo_sccp_user_sap_down(cn->sccp_user, mgw_fsm_priv->ranap_rab_ass_resp_oph);
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_oph = NULL;
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_DEBUG, "failed to forward RAB-AssignmentResponse message\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
}
|
||||
|
||||
/* Even though this is a failure situation, we still release normally as the error is located
|
||||
* at the HNB. */
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The RAB-ID we are dealing with is not on an FailedList and we were unable to parse the response
|
||||
* normally. This is a situation we cannot recover from. */
|
||||
LOGPFSML(fi, LOGL_ERROR, "Failed to extract RTP IP-address and Port from RAB-AssignmentResponse\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = osmo_sockaddr_str_from_sockaddr(&addr_str, &addr.u.sas);
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Invalid RTP IP-address or Port in RAB-AssignmentResponse\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
osmo_strlcpy(mgw_info.addr, addr_str.ip, sizeof(mgw_info.addr));
|
||||
mgw_info.port = addr_str.port;
|
||||
|
||||
osmo_mgcpc_ep_ci_request(mgw_fsm_priv->mgcpc_ep_ci_hnb, MGCP_VERB_MDCX, &mgw_info, fi, MGW_EV_MGCP_OK,
|
||||
MGW_EV_MGCP_FAIL, NULL);
|
||||
}
|
||||
|
||||
static void mgw_fsm_mdcx_hnb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
const struct mgcp_conn_peer *mgw_info;
|
||||
|
||||
switch (event) {
|
||||
case MGW_EV_MGCP_OK:
|
||||
mgw_info = osmo_mgcpc_ep_ci_get_rtp_info(mgw_fsm_priv->mgcpc_ep_ci_hnb);
|
||||
if (!mgw_info) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Got no RTP info response from MGW\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
mgw_fsm_state_chg(fi, MGW_ST_CRCX_MSC);
|
||||
return;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void mgw_fsm_crcx_msc_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
struct hnbgw_context_map *map = mgw_fsm_priv->map;
|
||||
struct mgcp_conn_peer mgw_info;
|
||||
|
||||
LOGPFSML(fi, LOGL_DEBUG, "creating MSC side call-leg on MGW...\n");
|
||||
|
||||
mgw_info = (struct mgcp_conn_peer) {
|
||||
.call_id = (map->rua_ctx_id << 8) | mgw_fsm_priv->rab_id,
|
||||
.ptime = 20,
|
||||
.port = mgw_fsm_priv->msc_rtp_port,
|
||||
};
|
||||
|
||||
osmo_strlcpy(mgw_info.addr, mgw_fsm_priv->msc_rtp_addr, sizeof(mgw_info.addr));
|
||||
mgw_info.codecs[0] = CODEC_IUFP;
|
||||
mgw_info.codecs_len = 1;
|
||||
|
||||
mgw_fsm_priv->mgcpc_ep_ci_msc = osmo_mgcpc_ep_ci_add(mgw_fsm_priv->mgcpc_ep, "to-MSC");
|
||||
osmo_mgcpc_ep_ci_request(mgw_fsm_priv->mgcpc_ep_ci_msc, MGCP_VERB_CRCX, &mgw_info, fi, MGW_EV_MGCP_OK,
|
||||
MGW_EV_MGCP_FAIL, NULL);
|
||||
}
|
||||
|
||||
static void mgw_fsm_crcx_msc(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
const struct mgcp_conn_peer *mgw_info;
|
||||
struct osmo_sockaddr addr;
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
int rc;
|
||||
int msg_max_len;
|
||||
RANAP_RAB_AssignmentResponseIEs_t *ies;
|
||||
|
||||
switch (event) {
|
||||
case MGW_EV_MGCP_OK:
|
||||
ies = &mgw_fsm_priv->ranap_rab_ass_resp_message->msg.raB_AssignmentResponseIEs;
|
||||
|
||||
mgw_info = osmo_mgcpc_ep_ci_get_rtp_info(mgw_fsm_priv->mgcpc_ep_ci_msc);
|
||||
if (!mgw_info) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "Got no response from MGW\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Replace RTP IP-Address/Port in ranap message container */
|
||||
if (strchr(mgw_info->addr, '.'))
|
||||
addr_str.af = AF_INET;
|
||||
else
|
||||
addr_str.af = AF_INET6;
|
||||
addr_str.port = mgw_info->port;
|
||||
osmo_strlcpy(addr_str.ip, mgw_info->addr, sizeof(addr_str.ip));
|
||||
rc = osmo_sockaddr_str_to_sockaddr(&addr_str, &addr.u.sas);
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"Failed to convert RTP IP-address (%s) and Port (%u) to its binary representation\n",
|
||||
mgw_info->addr, mgw_info->port);
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = ranap_rab_ass_resp_ies_replace_inet_addr(ies, &addr, mgw_fsm_priv->rab_id);
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR,
|
||||
"Failed to replace RTP IP-address (%s) and Port (%u) in RAB-AssignmentResponse\n",
|
||||
mgw_info->addr, mgw_info->port);
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* When the modified ranap message container is re-encoded, the resulting message might be larger then
|
||||
* the original message. Ensure that there is enough room in l2h to grow. (The current implementation
|
||||
* should yield a message with the same size, but there is no guarantee for that) */
|
||||
msg_max_len =
|
||||
msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg) +
|
||||
msgb_tailroom(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg);
|
||||
rc = msgb_resize_area(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg,
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_oph->msg->l2h,
|
||||
msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg), msg_max_len);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
rc = ranap_rab_ass_resp_encode(msgb_l2(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg),
|
||||
msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg), ies);
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_ERROR, "failed to re-encode RAB-AssignmentResponse message\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Resize l2h back to the actual message length */
|
||||
rc = msgb_resize_area(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg,
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_oph->msg->l2h,
|
||||
msgb_l2len(mgw_fsm_priv->ranap_rab_ass_resp_oph->msg), rc);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
/* When the established state is entered, the modified RAB AssignmentResponse is forwarded to the MSC.
|
||||
* The call is then established any way may stay for an indefinate amount of time in this state until
|
||||
* there is an IU Release happening. */
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_ESTABLISHED, 0, 0);
|
||||
return;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void mgw_fsm_established_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
struct hnbgw_context_map *map = mgw_fsm_priv->map;
|
||||
struct osmo_prim_hdr *oph = mgw_fsm_priv->ranap_rab_ass_resp_oph;
|
||||
struct hnb_context *hnb = map->hnb_ctx;
|
||||
struct hnbgw_cnlink *cn = hnb->gw->sccp.cnlink;
|
||||
int rc;
|
||||
|
||||
LOGPFSML(fi, LOGL_DEBUG, "forwarding modified RAB-AssignmentResponse to MSC\n");
|
||||
rc = osmo_sccp_user_sap_down(cn->sccp_user, oph);
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_oph = NULL;
|
||||
if (rc < 0) {
|
||||
LOGPFSML(fi, LOGL_DEBUG, "failed to forward RAB-AssignmentResponse message\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
}
|
||||
|
||||
LOGPFSML(fi, LOGL_DEBUG, "HNB and MSC side call-legs completed!\n");
|
||||
}
|
||||
|
||||
static void mgw_fsm_release_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
|
||||
static void mgw_fsm_failure_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
tx_release_req(mgw_fsm_priv->map);
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
}
|
||||
|
||||
static void mgw_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
case MGW_EV_MGCP_TERM:
|
||||
/* Put MGCP client back into MGW pool */
|
||||
if (mgw_fsm_priv->mgcpc) {
|
||||
mgcp_client_pool_put(mgw_fsm_priv->mgcpc);
|
||||
mgw_fsm_priv->mgcpc = NULL;
|
||||
}
|
||||
mgw_fsm_priv->mgcpc_ep = NULL;
|
||||
LOGPFSML(fi, LOGL_ERROR, "Media gateway failed\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
case MGW_EV_MGCP_FAIL:
|
||||
LOGPFSML(fi, LOGL_ERROR, "Media gateway failed to switch RTP streams\n");
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_FAILURE, 0, 0);
|
||||
return;
|
||||
case MGW_EV_RELEASE:
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
|
||||
return;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int mgw_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mgw_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
struct osmo_scu_prim *scu_prim;
|
||||
struct msgb *scu_msg;
|
||||
|
||||
if (mgw_fsm_priv->ranap_rab_ass_req_message) {
|
||||
ranap_ran_rx_co_free(mgw_fsm_priv->ranap_rab_ass_req_message);
|
||||
talloc_free(mgw_fsm_priv->ranap_rab_ass_req_message);
|
||||
mgw_fsm_priv->ranap_rab_ass_req_message = NULL;
|
||||
}
|
||||
|
||||
if (mgw_fsm_priv->ranap_rab_ass_resp_message) {
|
||||
ranap_cn_rx_co_free(mgw_fsm_priv->ranap_rab_ass_resp_message);
|
||||
talloc_free(mgw_fsm_priv->ranap_rab_ass_resp_message);
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_message = NULL;
|
||||
}
|
||||
|
||||
if (mgw_fsm_priv->ranap_rab_ass_resp_oph) {
|
||||
scu_prim = (struct osmo_scu_prim *)mgw_fsm_priv->ranap_rab_ass_resp_oph;
|
||||
scu_msg = scu_prim->oph.msg;
|
||||
msgb_free(scu_msg);
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_oph = NULL;
|
||||
}
|
||||
|
||||
talloc_free(mgw_fsm_priv);
|
||||
}
|
||||
|
||||
static void mgw_fsm_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
struct hnbgw_context_map *map = mgw_fsm_priv->map;
|
||||
|
||||
if (mgw_fsm_priv->mgcpc_ep) {
|
||||
/* Put MGCP client back into MGW pool */
|
||||
struct mgcp_client *mgcp_client = osmo_mgcpc_ep_client(mgw_fsm_priv->mgcpc_ep);
|
||||
mgcp_client_pool_put(mgcp_client);
|
||||
|
||||
osmo_mgcpc_ep_clear(mgw_fsm_priv->mgcpc_ep);
|
||||
mgw_fsm_priv->mgcpc_ep = NULL;
|
||||
}
|
||||
|
||||
/* Remove FSM from the context map. This will make this FSM unreachable for events coming from outside */
|
||||
map->mgw_fi = NULL;
|
||||
}
|
||||
|
||||
static const struct osmo_fsm_state mgw_fsm_states[] = {
|
||||
[MGW_ST_CRCX_HNB] = {
|
||||
.name = "MGW_ST_CRCX_HNB",
|
||||
.onenter = mgw_fsm_crcx_hnb_onenter,
|
||||
.action = mgw_fsm_crcx_hnb,
|
||||
.in_event_mask =
|
||||
S(MGW_EV_MGCP_OK),
|
||||
.out_state_mask =
|
||||
S(MGW_ST_ASSIGN) |
|
||||
S(MGW_ST_FAILURE) |
|
||||
S(MGW_ST_RELEASE) |
|
||||
S(MGW_ST_CRCX_HNB),
|
||||
},
|
||||
[MGW_ST_ASSIGN] = {
|
||||
.name = "MGW_ST_ASSIGN",
|
||||
.onenter = mgw_fsm_assign_onenter,
|
||||
.action = mgw_fsm_assign,
|
||||
.in_event_mask = S(MGW_EV_RAB_ASS_RESP),
|
||||
.out_state_mask =
|
||||
S(MGW_ST_MDCX_HNB) |
|
||||
S(MGW_ST_FAILURE) |
|
||||
S(MGW_ST_RELEASE),
|
||||
},
|
||||
[MGW_ST_MDCX_HNB] = {
|
||||
.name = "MGW_ST_MDCX_HNB",
|
||||
.onenter = mgw_fsm_mdcx_hnb_onenter,
|
||||
.action = mgw_fsm_mdcx_hnb,
|
||||
.in_event_mask =
|
||||
S(MGW_EV_MGCP_OK),
|
||||
.out_state_mask =
|
||||
S(MGW_ST_CRCX_MSC) |
|
||||
S(MGW_ST_FAILURE) |
|
||||
S(MGW_ST_RELEASE),
|
||||
},
|
||||
[MGW_ST_CRCX_MSC] = {
|
||||
.name = "MGW_ST_CRCX_MSC",
|
||||
.onenter = mgw_fsm_crcx_msc_onenter,
|
||||
.action = mgw_fsm_crcx_msc,
|
||||
.in_event_mask =
|
||||
S(MGW_EV_MGCP_OK),
|
||||
.out_state_mask =
|
||||
S(MGW_ST_ESTABLISHED) |
|
||||
S(MGW_ST_FAILURE) |
|
||||
S(MGW_ST_RELEASE),
|
||||
},
|
||||
[MGW_ST_ESTABLISHED] = {
|
||||
.name = "MGW_ST_ESTABLISHED",
|
||||
.onenter = mgw_fsm_established_onenter,
|
||||
.in_event_mask = 0,
|
||||
.out_state_mask =
|
||||
S(MGW_ST_FAILURE) |
|
||||
S(MGW_ST_RELEASE),
|
||||
},
|
||||
[MGW_ST_RELEASE] = {
|
||||
.name = "MGW_ST_RELEASE",
|
||||
.onenter = mgw_fsm_release_onenter,
|
||||
.in_event_mask = 0,
|
||||
.out_state_mask = 0,
|
||||
},
|
||||
[MGW_ST_FAILURE] = {
|
||||
.name = "MGW_ST_FAILURE",
|
||||
.onenter = mgw_fsm_failure_onenter,
|
||||
.in_event_mask = 0,
|
||||
.out_state_mask = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm mgw_fsm = {
|
||||
.name = "mgw",
|
||||
.states = mgw_fsm_states,
|
||||
.num_states = ARRAY_SIZE(mgw_fsm_states),
|
||||
.log_subsys = DMGW,
|
||||
.event_names = mgw_fsm_event_names,
|
||||
.allstate_action = mgw_fsm_allstate_action,
|
||||
.allstate_event_mask = S(MGW_EV_MGCP_TERM) | S(MGW_EV_RELEASE) | S(MGW_EV_MGCP_FAIL),
|
||||
.timer_cb = mgw_fsm_timer_cb,
|
||||
.cleanup = mgw_fsm_cleanup,
|
||||
.pre_term = mgw_fsm_pre_term,
|
||||
};
|
||||
|
||||
/* The MSC may ask to release a specific RAB within a RAB-AssignmentRequest */
|
||||
static int handle_rab_release(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message)
|
||||
{
|
||||
bool rab_release_req;
|
||||
struct osmo_fsm_inst *fi = map->mgw_fi;
|
||||
struct mgw_fsm_priv *mgw_fsm_priv = fi->priv;
|
||||
int rc;
|
||||
|
||||
/* Check if the RAB that is handled by this FSM is addressed by the release request */
|
||||
rab_release_req = ranap_rab_ass_req_ies_check_release(&message->msg.raB_AssignmentRequestIEs,
|
||||
mgw_fsm_priv->rab_id);
|
||||
if (!rab_release_req)
|
||||
return -EINVAL;
|
||||
|
||||
LOGPFSML(map->mgw_fi, LOGL_NOTICE, "MSC asked to release RAB-ID %u\n", mgw_fsm_priv->rab_id);
|
||||
|
||||
/* Forward the unmodifed RAB-AssignmentRequest to HNB, so that the HNB is informed about the RAB release as
|
||||
* well */
|
||||
LOGPFSML(fi, LOGL_DEBUG, "forwarding unmodified RAB-AssignmentRequest to HNB\n");
|
||||
rc = rua_tx_dt(map->hnb_ctx, map->is_ps, map->rua_ctx_id, msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
|
||||
/* Release the FSM normally */
|
||||
osmo_fsm_inst_state_chg(fi, MGW_ST_RELEASE, 0, 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Allocate MGW FSM and handle RANAP RAB AssignmentRequest).
|
||||
* \ptmap[in] map hanbgw context map that is responsible for this call.
|
||||
* \ptmap[in] oph osmo prim header with RANAP RAB AssignmentResponse (function takes no ownership).
|
||||
* \ptmap[in] message ranap message container (function takes ownership).
|
||||
* \returns 0 on success; negative on error. */
|
||||
int handle_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message)
|
||||
{
|
||||
static bool initialized = false;
|
||||
struct mgw_fsm_priv *mgw_fsm_priv;
|
||||
char fsm_name[255];
|
||||
int rc;
|
||||
|
||||
/* Initialize FSM if not done yet */
|
||||
if (!initialized) {
|
||||
OSMO_ASSERT(osmo_fsm_register(&mgw_fsm) == 0);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
/* The RTP stream negotiation usually begins with a RAB-AssignmentRequest and ends with an IU-Release, however
|
||||
* it may also be thet the MSC decides to release the RAB with a dedicated RAB-AssignmentRequest that contains
|
||||
* a ReleaseList. In this case an FSM will already be present. */
|
||||
if (map->mgw_fi) {
|
||||
/* A RAB Release might be in progress, handle it */
|
||||
rc = handle_rab_release(map, oph, message);
|
||||
if (rc >= 0)
|
||||
return rc;
|
||||
|
||||
LOGPFSML(map->mgw_fi, LOGL_ERROR,
|
||||
"mgw_fsm_alloc_and_handle_rab_ass_req() unable to handle RAB-AssignmentRequest!\n");
|
||||
osmo_fsm_inst_state_chg(map->mgw_fi, MGW_ST_FAILURE, 0, 0);
|
||||
OSMO_ASSERT(map->mgw_fi == NULL);
|
||||
}
|
||||
|
||||
/* This FSM only supports RAB assignments with a single RAB assignment only. This limitation has been taken
|
||||
* 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,
|
||||
"mgw_fsm_alloc_and_handle_rab_ass_req() rua_ctx_id=%d, RAB-AssignmentRequest with more than one RAB assignment -- abort!\n",
|
||||
map->rua_ctx_id);
|
||||
tx_release_req(map);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mgw_fsm_priv = talloc_zero(map, struct mgw_fsm_priv);
|
||||
mgw_fsm_priv->map = map;
|
||||
mgw_fsm_priv->ranap_rab_ass_req_message = message;
|
||||
|
||||
/* Allocate FSM */
|
||||
snprintf(fsm_name, sizeof(fsm_name), "mgw-fsm-%u-%u", map->rua_ctx_id, mgw_fsm_priv->rab_id);
|
||||
map->mgw_fi = osmo_fsm_inst_alloc(&mgw_fsm, map, mgw_fsm_priv, LOGL_DEBUG, fsm_name);
|
||||
|
||||
/* Start the FSM */
|
||||
mgw_fsm_state_chg(map->mgw_fi, MGW_ST_CRCX_HNB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Handlie RANAP RAB AssignmentResponse (deliver message, complete RTP stream switching).
|
||||
* \ptmap[in] map hanbgw context map that is responsible for this call.
|
||||
* \ptmap[in] oph osmo prim header with RANAP RAB AssignmentResponse (function takes ownership).
|
||||
* \ptmap[in] message ranap message container with decoded ranap message (function takes ownership).
|
||||
* \returns 0 on success; negative on error. */
|
||||
int mgw_fsm_handle_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message)
|
||||
{
|
||||
struct mgw_fsm_priv *mgw_fsm_priv;
|
||||
struct osmo_scu_prim *prim;
|
||||
struct msgb *msg;
|
||||
|
||||
OSMO_ASSERT(oph);
|
||||
|
||||
if (!map->mgw_fi) {
|
||||
/* 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,
|
||||
"mgw_fsm_handle_rab_ass_resp() rua_ctx_id=%d, no MGW fsm -- sending Iu-Release-Request!\n",
|
||||
map->rua_ctx_id);
|
||||
|
||||
/* Cleanup ranap message */
|
||||
ranap_cn_rx_co_free(message);
|
||||
talloc_free(message);
|
||||
|
||||
/* Toss RAB-AssignmentResponse */
|
||||
prim = (struct osmo_scu_prim *)oph;
|
||||
msg = prim->oph.msg;
|
||||
msgb_free(msg);
|
||||
|
||||
/* Send a release request, to make sure that the MSC is aware of the problem. */
|
||||
tx_release_req(map);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mgw_fsm_priv = map->mgw_fi->priv;
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_oph = oph;
|
||||
mgw_fsm_priv->ranap_rab_ass_resp_message = message;
|
||||
osmo_fsm_inst_dispatch(map->mgw_fi, MGW_EV_RAB_ASS_RESP, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Release the FSM and clear its associated RTP streams.
|
||||
* \ptmap[in] map hanbgw context map that is responsible for this call.
|
||||
* \returns 0 on success; negative on error. */
|
||||
int mgw_fsm_release(struct hnbgw_context_map *map)
|
||||
{
|
||||
if (!map->mgw_fi)
|
||||
return -EINVAL;
|
||||
|
||||
osmo_fsm_inst_dispatch(map->mgw_fi, MGW_EV_RELEASE, NULL);
|
||||
return 0;
|
||||
}
|
||||
687
src/osmo-hnbgw/ps_rab_ass_fsm.c
Normal file
687
src/osmo-hnbgw/ps_rab_ass_fsm.c
Normal file
@@ -0,0 +1,687 @@
|
||||
/* Handle RANAP PS RAB Assignment */
|
||||
/* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <asn1c/asn1helpers.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/prim.h>
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/byteswap.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/ranap_common_cn.h>
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
#include <osmocom/ranap/ranap_msg_factory.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/ranap/iu_helpers.h>
|
||||
|
||||
#include <osmocom/pfcp/pfcp_msg.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/ranap_rab_ass.h>
|
||||
#include <osmocom/hnbgw/ps_rab_fsm.h>
|
||||
|
||||
#include <osmocom/hnbgw/hnbgw_rua.h>
|
||||
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
|
||||
#define PORT_GTP1_U 2152
|
||||
|
||||
#define LOG_PS_RAB_ASS(RAB_ASS, LOGL, FMT, ARGS...) \
|
||||
LOGPFSML((RAB_ASS) ? (RAB_ASS)->fi : NULL, LOGL, FMT, ##ARGS)
|
||||
|
||||
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_FAIL,
|
||||
};
|
||||
|
||||
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_FAIL),
|
||||
{}
|
||||
};
|
||||
|
||||
enum ps_rab_ass_state {
|
||||
PS_RAB_ASS_ST_RX_RAB_ASS_MSG,
|
||||
PS_RAB_ASS_ST_WAIT_LOCAL_F_TEIDS,
|
||||
PS_RAB_ASS_ST_RX_RAB_ASS_RESP,
|
||||
PS_RAB_ASS_ST_WAIT_RABS_ESTABLISHED,
|
||||
};
|
||||
|
||||
/* Represents one RANAP PS RAB Assignment Request and Response dialog.
|
||||
* There may be any number of PS RAB Assignment Requests, each with any number of RABs being established. We need to
|
||||
* manage these asynchronously and flexibly:
|
||||
* - RABs may be assigned in a group and released one by one, or vice versa;
|
||||
* - we can only forward a RAB Assignment Request / Response when all RABs appearing in it have been set up by the UPF.
|
||||
*
|
||||
* 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_fsm: one RANAP PS RAB Assignment procedure
|
||||
* - ...
|
||||
* - .ps_rabs: list of individual PS RABs
|
||||
* - ps_rab_fsm: one GTP mapping with PFCP session to the UPF, for a single RAB
|
||||
* - ...
|
||||
*
|
||||
* This ps_rab_ass_fsm lives from a received RAB Assignment Request up to the sent RAB Assignment Response; it
|
||||
* deallocates when all the RABs have been set up.
|
||||
*
|
||||
* The ps_rab_ass_fsm sets up ps_rab_fsm instances, which live longer: up until a RAB or conn release is performed.
|
||||
*/
|
||||
struct ps_rab_ass {
|
||||
struct llist_head entry;
|
||||
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
/* backpointer */
|
||||
struct hnbgw_context_map *map;
|
||||
|
||||
ranap_message *ranap_rab_ass_req_message;
|
||||
|
||||
ranap_message *ranap_rab_ass_resp_message;
|
||||
struct osmo_prim_hdr *ranap_rab_ass_resp_oph;
|
||||
|
||||
/* A RAB Assignment may contain more than one RAB. Each RAB sets up a distinct ps_rab_fsm (aka PFCP session) and
|
||||
* reports back about local F-TEIDs assigned by the UPF. This gives the nr of RAB events we expect from
|
||||
* ps_rab_fsms, without iterating the RAB Assignment message every time (minor optimisation). */
|
||||
int rabs_count;
|
||||
int rabs_done_count;
|
||||
};
|
||||
|
||||
struct osmo_tdef_state_timeout ps_rab_ass_fsm_timeouts[32] = {
|
||||
/* PS_RAB_ASS_ST_WAIT_LOCAL_F_TEIDS is terminated by PFCP timeouts via ps_rab_fsm */
|
||||
/* PS_RAB_ASS_ST_WAIT_RABS_ESTABLISHED is terminated by PFCP timeouts via ps_rab_fsm */
|
||||
};
|
||||
|
||||
#define ps_rab_ass_fsm_state_chg(state) \
|
||||
osmo_tdef_fsm_inst_state_chg(fi, state, ps_rab_ass_fsm_timeouts, ps_T_defs, -1)
|
||||
|
||||
static struct osmo_fsm ps_rab_ass_fsm;
|
||||
|
||||
static struct ps_rab_ass *ps_rab_ass_alloc(struct hnbgw_context_map *map)
|
||||
{
|
||||
struct ps_rab_ass *rab_ass;
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
fi = osmo_fsm_inst_alloc(&ps_rab_ass_fsm, map, map, LOGL_DEBUG, NULL);
|
||||
OSMO_ASSERT(fi);
|
||||
osmo_fsm_inst_update_id_f_sanitize(fi, '-', "%s-RUA-%u", hnb_context_name(map->hnb_ctx), map->rua_ctx_id);
|
||||
|
||||
rab_ass = talloc(fi, struct ps_rab_ass);
|
||||
OSMO_ASSERT(rab_ass);
|
||||
*rab_ass = (struct ps_rab_ass){
|
||||
.fi = fi,
|
||||
.map = map,
|
||||
};
|
||||
fi->priv = rab_ass;
|
||||
|
||||
llist_add_tail(&rab_ass->entry, &map->ps_rab_ass);
|
||||
return rab_ass;
|
||||
}
|
||||
|
||||
static void ps_rab_ass_failure(struct ps_rab_ass *rab_ass)
|
||||
{
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "PS RAB Assignment failed\n");
|
||||
|
||||
/* TODO: send unsuccessful RAB Assignment Response to Core? */
|
||||
/* TODO: remove RAB from Access? */
|
||||
|
||||
osmo_fsm_inst_term(rab_ass->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
|
||||
/* Add a single RAB from a RANAP PS RAB Assignment Request's list of RABs */
|
||||
static int ps_rab_setup_core_remote(struct ps_rab_ass *rab_ass, RANAP_ProtocolIE_FieldPair_t *protocol_ie_field_pair)
|
||||
{
|
||||
struct hnbgw_context_map *map = rab_ass->map;
|
||||
uint8_t rab_id;
|
||||
struct addr_teid f_teid = {};
|
||||
bool use_x213_nsap;
|
||||
struct ps_rab *rab;
|
||||
|
||||
RANAP_RAB_SetupOrModifyItemFirst_t first;
|
||||
RANAP_TransportLayerAddress_t *transp_layer_addr;
|
||||
RANAP_TransportLayerInformation_t *tli;
|
||||
int rc;
|
||||
|
||||
if (protocol_ie_field_pair->id != RANAP_ProtocolIE_ID_id_RAB_SetupOrModifyItem)
|
||||
return -1;
|
||||
|
||||
/* Extract information about the GTP Core side */
|
||||
rc = ranap_decode_rab_setupormodifyitemfirst(&first,
|
||||
&protocol_ie_field_pair->firstValue);
|
||||
if (rc < 0)
|
||||
goto error_exit;
|
||||
|
||||
rab_id = first.rAB_ID.buf[0];
|
||||
|
||||
/* Decode GTP endpoint IP-Address */
|
||||
tli = first.transportLayerInformation;
|
||||
transp_layer_addr = &tli->transportLayerAddress;
|
||||
rc = ranap_transp_layer_addr_decode2(&f_teid.addr, &use_x213_nsap, transp_layer_addr);
|
||||
if (rc < 0)
|
||||
goto error_exit;
|
||||
osmo_sockaddr_set_port(&f_teid.addr.u.sa, PORT_GTP1_U);
|
||||
|
||||
/* Decode the GTP remote TEID */
|
||||
if (tli->iuTransportAssociation.present != RANAP_IuTransportAssociation_PR_gTP_TEI) {
|
||||
rc = -1;
|
||||
goto error_exit;
|
||||
}
|
||||
f_teid.teid = osmo_load32be(tli->iuTransportAssociation.choice.gTP_TEI.buf);
|
||||
f_teid.present = true;
|
||||
|
||||
rab_ass->rabs_count++;
|
||||
rab = ps_rab_start(map, rab_id, &f_teid, use_x213_nsap, rab_ass->fi);
|
||||
if (!rab) {
|
||||
rc = -1;
|
||||
goto error_exit;
|
||||
}
|
||||
rc = 0;
|
||||
|
||||
error_exit:
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyItemFirst, &first);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int hnbgw_gtpmap_rx_rab_ass_req(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message)
|
||||
{
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies = &message->msg.raB_AssignmentRequestIEs;
|
||||
int i;
|
||||
|
||||
struct hnb_gw *hnb_gw = map->hnb_ctx->gw;
|
||||
struct ps_rab_ass *rab_ass;
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
rab_ass = ps_rab_ass_alloc(map);
|
||||
rab_ass->ranap_rab_ass_req_message = message;
|
||||
/* Now rab_ass owns message and will clean it up */
|
||||
|
||||
if (!osmo_pfcp_cp_peer_is_associated(hnb_gw->pfcp.cp_peer)) {
|
||||
LOG_MAP(map, DLPFCP, LOGL_ERROR, "PFCP is not associated, cannot set up GTP mapping\n");
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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_rab:
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ps_rab_ass_req_check_local_f_teids(struct ps_rab_ass *rab_ass);
|
||||
|
||||
static void ps_rab_ass_fsm_wait_local_f_teids(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct ps_rab_ass *rab_ass = fi->priv;
|
||||
|
||||
switch (event) {
|
||||
case PS_RAB_ASS_EV_LOCAL_F_TEIDS_RX:
|
||||
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;
|
||||
}
|
||||
ps_rab_ass_req_check_local_f_teids(rab_ass);
|
||||
return;
|
||||
|
||||
case PS_RAB_ASS_EV_RAB_FAIL:
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies = &rab_ass->ranap_rab_ass_req_message->msg.raB_AssignmentRequestIEs;
|
||||
int i;
|
||||
struct msgb *msg;
|
||||
|
||||
/* Go through all RABs in the RAB Assignment Request message and replace with the F-TEID that the UPF assigned,
|
||||
* verifying that we indeed have local F-TEIDs for all RABs contained in this message. */
|
||||
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;
|
||||
RANAP_RAB_SetupOrModifyItemFirst_t first;
|
||||
uint8_t rab_id;
|
||||
int rc;
|
||||
|
||||
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)
|
||||
continue;
|
||||
if (protocol_ie_field_pair->id != RANAP_ProtocolIE_ID_id_RAB_SetupOrModifyItem)
|
||||
continue;
|
||||
|
||||
/* Get to the information about the GTP Core side */
|
||||
rc = ranap_decode_rab_setupormodifyitemfirst(&first,
|
||||
&protocol_ie_field_pair->firstValue);
|
||||
if (rc < 0)
|
||||
goto continue_cleanloop;
|
||||
|
||||
rab_id = first.rAB_ID.buf[0];
|
||||
|
||||
/* Find struct ps_rab for this rab_id */
|
||||
rab = ps_rab_get(rab_ass->map, rab_id);
|
||||
if (!rab || !rab->access.local.present) {
|
||||
/* Not ready to send on the RAB Assignment Request to RUA, a local F-TEID is missing. */
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyItemFirst, &first);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Replace GTP endpoint */
|
||||
ASN_STRUCT_FREE(asn_DEF_RANAP_TransportLayerInformation, first.transportLayerInformation);
|
||||
first.transportLayerInformation = ranap_new_transp_info_gtp(&rab->access.local.addr,
|
||||
rab->access.local.teid,
|
||||
rab->core.use_x213_nsap);
|
||||
|
||||
/* Reencode to update transport-layer-information */
|
||||
rc = ANY_fromType_aper(&protocol_ie_field_pair->firstValue, &asn_DEF_RANAP_RAB_SetupOrModifyItemFirst,
|
||||
&first);
|
||||
if (rc < 0)
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Re-encoding RANAP PS RAB Assignment Request failed\n");
|
||||
continue_cleanloop:
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyItemFirst, &first);
|
||||
}
|
||||
|
||||
/* Send the modified RAB Assignment Request to the hNodeB, wait for the RAB Assignment Response */
|
||||
msg = ranap_rab_ass_req_encode(ies);
|
||||
if (!msg) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Re-encoding RANAP PS RAB Assignment Request failed\n");
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return;
|
||||
}
|
||||
rua_tx_dt(rab_ass->map->hnb_ctx, rab_ass->map->is_ps, rab_ass->map->rua_ctx_id, msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
/* The request message has been forwarded. The response will be handled by a new FSM instance.
|
||||
* We are done. */
|
||||
osmo_fsm_inst_term(rab_ass->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
|
||||
/* Add a single RAB from a RANAP/RUA RAB Assignment Response's list of RABs */
|
||||
static int ps_rab_setup_access_remote(struct ps_rab_ass *rab_ass,
|
||||
RANAP_RAB_SetupOrModifiedItem_t *rab_item)
|
||||
{
|
||||
struct hnbgw_context_map *map = rab_ass->map;
|
||||
uint8_t rab_id;
|
||||
int rc;
|
||||
struct ps_rab_rx_args args = {};
|
||||
|
||||
rab_id = rab_item->rAB_ID.buf[0];
|
||||
|
||||
rc = ranap_transp_layer_addr_decode2(&args.f_teid.addr, &args.use_x213_nsap, rab_item->transportLayerAddress);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Decode the GTP remote TEID */
|
||||
if (!rab_item->iuTransportAssociation
|
||||
|| rab_item->iuTransportAssociation->present != RANAP_IuTransportAssociation_PR_gTP_TEI)
|
||||
return -1;
|
||||
args.f_teid.teid = osmo_load32be(rab_item->iuTransportAssociation->choice.gTP_TEI.buf);
|
||||
args.f_teid.present = true;
|
||||
|
||||
args.notify_fi = rab_ass->fi;
|
||||
|
||||
return ps_rab_rx_access_remote_f_teid(map, rab_id, &args);
|
||||
}
|
||||
|
||||
int hnbgw_gtpmap_rx_rab_ass_resp(struct hnbgw_context_map *map, struct osmo_prim_hdr *oph, ranap_message *message)
|
||||
{
|
||||
/* hNodeB responds with its own F-TEIDs. Need to tell the UPF about those to complete the GTP mapping.
|
||||
* 1. here, extract the F-TEIDs (one per RAB),
|
||||
* trigger each ps_rab_fsm to do a PFCP Session Modification.
|
||||
* 2. after all ps_rab_fsms responded with success, insert our Core side local F-TEIDs and send on the RAB
|
||||
* Assignment Response to IuPS. (We already know the local F-TEIDs assigned by the UPF and could send on the
|
||||
* RAB Assignment Response immediately, but rather wait for the PFCP mod req to succeed first.)
|
||||
*
|
||||
* To wait for all the RABs in this response message to complete, create a *separate* rab_ass_fsm instance from
|
||||
* the one created for the earlier RAB Assignment Request message. The reason is that technically we cannot
|
||||
* assume that the request and the response have exactly matching RAB IDs contained in them.
|
||||
*
|
||||
* In the vast majority of practical cases, there will be only one RAB Assignment Request message pending, but
|
||||
* for interop, by treating each RAB on its own and by treating request and response message separately from
|
||||
* each other, we are able to handle mismatching RAB IDs in request and response messages.
|
||||
*/
|
||||
|
||||
int rc;
|
||||
int i;
|
||||
struct ps_rab_ass *rab_ass;
|
||||
struct osmo_fsm_inst *fi;
|
||||
RANAP_RAB_AssignmentResponseIEs_t *ies;
|
||||
struct hnb_gw *hnb_gw = map->hnb_ctx->gw;
|
||||
|
||||
/* 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);
|
||||
rab_ass->ranap_rab_ass_resp_message = message;
|
||||
rab_ass->ranap_rab_ass_resp_oph = oph;
|
||||
/* Now rab_ass owns message and will clean it up */
|
||||
|
||||
if (!osmo_pfcp_cp_peer_is_associated(hnb_gw->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;
|
||||
}
|
||||
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_NOTICE, "PS RAB-AssignmentResponse received, updating RABs\n");
|
||||
|
||||
/* 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;
|
||||
|
||||
list_ie = ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[i];
|
||||
if (!list_ie)
|
||||
continue;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
static void ps_rab_ass_resp_send_if_ready(struct ps_rab_ass *rab_ass);
|
||||
|
||||
static void ps_rab_ass_fsm_wait_rabs_established(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct ps_rab_ass *rab_ass = fi->priv;
|
||||
|
||||
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);
|
||||
return;
|
||||
|
||||
case PS_RAB_ASS_EV_RAB_FAIL:
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* See whether all RABs are done establishing, and replace GTP info in the RAB Assignment Response message, so that we
|
||||
* can forward the modified RAB Assignment Request to M3UA. */
|
||||
static void ps_rab_ass_resp_send_if_ready(struct ps_rab_ass *rab_ass)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
struct hnbgw_cnlink *cn = rab_ass->map->cn_link;
|
||||
RANAP_RAB_AssignmentResponseIEs_t *ies = &rab_ass->ranap_rab_ass_resp_message->msg.raB_AssignmentResponseIEs;
|
||||
|
||||
/* Go through all RABs in the RAB Assignment Response message and replace with the F-TEID that the UPF assigned,
|
||||
* verifying that instructing the UPF has succeeded. */
|
||||
for (i = 0; i < ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.count; i++) {
|
||||
RANAP_IE_t *list_ie;
|
||||
RANAP_RAB_SetupOrModifiedItemIEs_t item_ies;
|
||||
RANAP_RAB_SetupOrModifiedItem_t *rab_item;
|
||||
int rc;
|
||||
uint8_t rab_id;
|
||||
uint32_t teid_be;
|
||||
struct ps_rab *rab;
|
||||
|
||||
list_ie = ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[i];
|
||||
if (!list_ie)
|
||||
continue;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
rab_item = &item_ies.raB_SetupOrModifiedItem;
|
||||
rab_id = rab_item->rAB_ID.buf[0];
|
||||
|
||||
/* Find struct ps_rab for this rab_id */
|
||||
rab = ps_rab_get(rab_ass->map, rab_id);
|
||||
if (!ps_rab_is_established(rab)) {
|
||||
/* Not ready to send on the RAB Assignment Response to M3UA, still waiting for it to be
|
||||
* established */
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, &item_ies);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Replace GTP endpoint */
|
||||
if (ranap_new_transp_layer_addr(rab_item->transportLayerAddress, &rab->core.local.addr,
|
||||
rab->access.use_x213_nsap) < 0) {
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, &item_ies);
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Re-encoding RANAP PS RAB-AssignmentResponse failed\n");
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_DEBUG, "Re-encoding RANAP PS RAB-AssignmentResponse: RAB %u:"
|
||||
" RUA sent F-TEID %s-0x%x; replacing with %s-0x%x\n",
|
||||
rab_id,
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, &rab->access.remote.addr), rab->access.remote.teid,
|
||||
osmo_sockaddr_to_str_c(OTC_SELECT, &rab->core.local.addr), rab->core.local.teid);
|
||||
|
||||
teid_be = htonl(rab->core.local.teid);
|
||||
rab_item->iuTransportAssociation->present = RANAP_IuTransportAssociation_PR_gTP_TEI;
|
||||
OCTET_STRING_fromBuf(&rab_item->iuTransportAssociation->choice.gTP_TEI,
|
||||
(const char *)&teid_be, sizeof(teid_be));
|
||||
|
||||
/* Reencode this list item in the RANAP message */
|
||||
rc = ANY_fromType_aper(&list_ie->value, &asn_DEF_RANAP_RAB_SetupOrModifiedItem, rab_item);
|
||||
if (rc < 0) {
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, &item_ies);
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Re-encoding RANAP PS RAB-AssignmentResponse failed\n");
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return;
|
||||
}
|
||||
|
||||
continue_cleanloop:
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, &item_ies);
|
||||
}
|
||||
|
||||
/* Replaced all the GTP info, re-encode the message. Since we are replacing data 1:1, taking care to use the
|
||||
* same IP address encoding, the resulting message size must be identical to the original message size. */
|
||||
rc = ranap_rab_ass_resp_encode(msgb_l2(rab_ass->ranap_rab_ass_resp_oph->msg),
|
||||
msgb_l2len(rab_ass->ranap_rab_ass_resp_oph->msg), ies);
|
||||
if (rc < 0) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Re-encoding RANAP PS RAB-AssignmentResponse failed\n");
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_NOTICE, "Sending RANAP PS RAB-AssignmentResponse with mapped GTP info\n");
|
||||
rc = osmo_sccp_user_sap_down(cn->sccp_user, rab_ass->ranap_rab_ass_resp_oph);
|
||||
rab_ass->ranap_rab_ass_resp_oph = NULL;
|
||||
if (rc < 0) {
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Sending RANAP PS RAB-AssignmentResponse failed\n");
|
||||
ps_rab_ass_failure(rab_ass);
|
||||
}
|
||||
|
||||
/* The request message has been forwarded. We are done. */
|
||||
osmo_fsm_inst_term(rab_ass->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
|
||||
static void ps_rab_ass_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
struct ps_rab_ass *rab_ass = fi->priv;
|
||||
struct osmo_scu_prim *scu_prim;
|
||||
struct msgb *scu_msg;
|
||||
struct ps_rab *rab;
|
||||
|
||||
if (rab_ass->ranap_rab_ass_req_message) {
|
||||
ranap_ran_rx_co_free(rab_ass->ranap_rab_ass_req_message);
|
||||
talloc_free(rab_ass->ranap_rab_ass_req_message);
|
||||
rab_ass->ranap_rab_ass_req_message = NULL;
|
||||
}
|
||||
|
||||
if (rab_ass->ranap_rab_ass_resp_message) {
|
||||
ranap_cn_rx_co_free(rab_ass->ranap_rab_ass_resp_message);
|
||||
talloc_free(rab_ass->ranap_rab_ass_resp_message);
|
||||
rab_ass->ranap_rab_ass_resp_message = NULL;
|
||||
}
|
||||
|
||||
if (rab_ass->ranap_rab_ass_resp_oph) {
|
||||
scu_prim = (struct osmo_scu_prim *)rab_ass->ranap_rab_ass_resp_oph;
|
||||
scu_msg = scu_prim->oph.msg;
|
||||
msgb_free(scu_msg);
|
||||
rab_ass->ranap_rab_ass_resp_oph = NULL;
|
||||
}
|
||||
|
||||
llist_for_each_entry(rab, &rab_ass->map->ps_rabs, entry) {
|
||||
if (rab->req_fi == fi)
|
||||
rab->req_fi = NULL;
|
||||
if (rab->resp_fi == fi)
|
||||
rab->resp_fi = NULL;
|
||||
}
|
||||
|
||||
llist_del(&rab_ass->entry);
|
||||
}
|
||||
|
||||
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_ass, next, &map->ps_rab_ass, entry) {
|
||||
osmo_fsm_inst_term(rab_ass->fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
static const struct osmo_fsm_state ps_rab_ass_fsm_states[] = {
|
||||
[PS_RAB_ASS_ST_RX_RAB_ASS_MSG] = {
|
||||
.name = "RX_RAB_ASS_MSG",
|
||||
.out_state_mask = 0
|
||||
| S(PS_RAB_ASS_ST_WAIT_LOCAL_F_TEIDS)
|
||||
| S(PS_RAB_ASS_ST_WAIT_RABS_ESTABLISHED)
|
||||
,
|
||||
},
|
||||
[PS_RAB_ASS_ST_WAIT_LOCAL_F_TEIDS] = {
|
||||
.name = "WAIT_LOCAL_F_TEIDS",
|
||||
.action = ps_rab_ass_fsm_wait_local_f_teids,
|
||||
.in_event_mask = 0
|
||||
| S(PS_RAB_ASS_EV_LOCAL_F_TEIDS_RX)
|
||||
| S(PS_RAB_ASS_EV_RAB_FAIL)
|
||||
,
|
||||
},
|
||||
[PS_RAB_ASS_ST_WAIT_RABS_ESTABLISHED] = {
|
||||
.name = "WAIT_RABS_ESTABLISHED",
|
||||
.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_FAIL)
|
||||
,
|
||||
},
|
||||
};
|
||||
|
||||
int ps_rab_ass_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct ps_rab_ass *rab_ass = fi->priv;
|
||||
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Timeout of " OSMO_T_FMT "\n", OSMO_T_FMT_ARGS(fi->T));
|
||||
/* terminate */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct osmo_fsm ps_rab_ass_fsm = {
|
||||
.name = "ps_rab_ass",
|
||||
.states = ps_rab_ass_fsm_states,
|
||||
.num_states = ARRAY_SIZE(ps_rab_ass_fsm_states),
|
||||
.log_subsys = DRANAP,
|
||||
.event_names = ps_rab_ass_fsm_event_names,
|
||||
.cleanup = ps_rab_ass_fsm_cleanup,
|
||||
.timer_cb = ps_rab_ass_fsm_timer_cb,
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void ps_rab_ass_fsm_register(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&ps_rab_ass_fsm) == 0);
|
||||
}
|
||||
819
src/osmo-hnbgw/ps_rab_fsm.c
Normal file
819
src/osmo-hnbgw/ps_rab_fsm.c
Normal file
@@ -0,0 +1,819 @@
|
||||
/* Handle PFCP communication with the UPF for a single RAB. */
|
||||
/* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocom/core/tdef.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/tdefs.h>
|
||||
#include <osmocom/hnbgw/ps_rab_fsm.h>
|
||||
#include <osmocom/hnbgw/ps_rab_ass_fsm.h>
|
||||
|
||||
#define LOG_PS_RAB(RAB, LOGL, FMT, ARGS...) \
|
||||
LOGPFSML((RAB) ? (RAB)->fi : NULL, LOGL, FMT, ##ARGS)
|
||||
|
||||
enum ps_rab_state {
|
||||
PS_RAB_ST_RX_CORE_REMOTE_F_TEID,
|
||||
PS_RAB_ST_WAIT_PFCP_EST_RESP,
|
||||
PS_RAB_ST_WAIT_ACCESS_REMOTE_F_TEID,
|
||||
PS_RAB_ST_WAIT_PFCP_MOD_RESP,
|
||||
PS_RAB_ST_ESTABLISHED,
|
||||
PS_RAB_ST_WAIT_PFCP_DEL_RESP,
|
||||
PS_RAB_ST_WAIT_USE_COUNT,
|
||||
};
|
||||
|
||||
enum ps_rab_event {
|
||||
PS_RAB_EV_PFCP_EST_RESP,
|
||||
PS_RAB_EV_RX_ACCESS_REMOTE_F_TEID,
|
||||
PS_RAB_EV_PFCP_MOD_RESP,
|
||||
PS_RAB_EV_PFCP_DEL_RESP,
|
||||
PS_RAB_EV_USE_COUNT_ZERO,
|
||||
};
|
||||
|
||||
static const struct value_string ps_rab_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(PS_RAB_EV_PFCP_EST_RESP),
|
||||
OSMO_VALUE_STRING(PS_RAB_EV_RX_ACCESS_REMOTE_F_TEID),
|
||||
OSMO_VALUE_STRING(PS_RAB_EV_PFCP_MOD_RESP),
|
||||
OSMO_VALUE_STRING(PS_RAB_EV_PFCP_DEL_RESP),
|
||||
OSMO_VALUE_STRING(PS_RAB_EV_USE_COUNT_ZERO),
|
||||
{}
|
||||
};
|
||||
|
||||
struct osmo_tdef_state_timeout ps_rab_fsm_timeouts[32] = {
|
||||
/* PS_RAB_ST_WAIT_PFCP_EST_RESP is terminated by PFCP timeouts via resp_cb() */
|
||||
/* PS_RAB_ST_WAIT_ACCESS_REMOTE_F_TEID is terminated by ps_rab_ass_fsm */
|
||||
/* PS_RAB_ST_WAIT_PFCP_MOD_RESP is terminated by PFCP timeouts via resp_cb() */
|
||||
/* PS_RAB_ST_WAIT_PFCP_DEL_RESP is terminated by PFCP timeouts via resp_cb() */
|
||||
};
|
||||
|
||||
enum pdr_far_id {
|
||||
ID_CORE_TO_ACCESS = 1,
|
||||
ID_ACCESS_TO_CORE = 2,
|
||||
};
|
||||
|
||||
|
||||
#define ps_rab_fsm_state_chg(state) \
|
||||
osmo_tdef_fsm_inst_state_chg(fi, state, ps_rab_fsm_timeouts, ps_T_defs, -1)
|
||||
|
||||
#define PS_RAB_USE_ACTIVE "active"
|
||||
|
||||
static struct osmo_fsm ps_rab_fsm;
|
||||
|
||||
static int ps_rab_fsm_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line);
|
||||
|
||||
static struct ps_rab *ps_rab_alloc(struct hnbgw_context_map *map, uint8_t rab_id)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct ps_rab *rab;
|
||||
|
||||
/* Allocate with the global hnb_gw, so that we can gracefully handle PFCP release even if a hnb_ctx gets
|
||||
* deallocated. */
|
||||
fi = osmo_fsm_inst_alloc(&ps_rab_fsm, map->hnb_ctx->gw, NULL, LOGL_DEBUG, NULL);
|
||||
OSMO_ASSERT(fi);
|
||||
osmo_fsm_inst_update_id_f_sanitize(fi, '-', "%s-RUA-%u-RAB-%u", hnb_context_name(map->hnb_ctx), map->rua_ctx_id,
|
||||
rab_id);
|
||||
|
||||
rab = talloc(fi, struct ps_rab);
|
||||
OSMO_ASSERT(rab);
|
||||
*rab = (struct ps_rab){
|
||||
.fi = fi,
|
||||
.hnb_gw = map->hnb_ctx->gw,
|
||||
.map = map,
|
||||
.rab_id = rab_id,
|
||||
.use_count = {
|
||||
.talloc_object = rab,
|
||||
.use_cb = ps_rab_fsm_use_cb,
|
||||
},
|
||||
};
|
||||
fi->priv = rab;
|
||||
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&rab->use_count, PS_RAB_USE_ACTIVE, 1) == 0);
|
||||
|
||||
llist_add_tail(&rab->entry, &map->ps_rabs);
|
||||
return rab;
|
||||
}
|
||||
|
||||
/* Iterate all ps_rab instances of all context maps and return the one matching the given SEID.
|
||||
* If is_cp_seid == true, match seid with rab->cp_seid (e.g. for received PFCP messages).
|
||||
* Otherwise match seid with rab->up_f_seid.seid (e.g. for sent PFCP messages). */
|
||||
struct ps_rab *ps_rab_find_by_seid(struct hnb_gw *hnb_gw, uint64_t seid, bool is_cp_seid)
|
||||
{
|
||||
struct hnb_context *hnb;
|
||||
llist_for_each_entry(hnb, &hnb_gw->hnb_list, list) {
|
||||
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) {
|
||||
uint64_t rab_seid = is_cp_seid ? rab->cp_seid : rab->up_f_seid.seid;
|
||||
if (rab_seid == seid)
|
||||
return rab;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ps_rab_pfcp_set_msg_ctx(struct ps_rab *rab, struct osmo_pfcp_msg *m)
|
||||
{
|
||||
if (m->ctx.session_fi)
|
||||
return;
|
||||
m->ctx.session_fi = rab->fi;
|
||||
m->ctx.session_use_count = &rab->use_count;
|
||||
m->ctx.session_use_token = "PFCP_MSG";
|
||||
OSMO_ASSERT(osmo_use_count_get_put(m->ctx.session_use_count, m->ctx.session_use_token, 1) == 0);
|
||||
}
|
||||
|
||||
static struct osmo_pfcp_msg *ps_rab_new_pfcp_msg_req(struct ps_rab *rab, enum osmo_pfcp_message_type msg_type)
|
||||
{
|
||||
struct hnb_gw *hnb_gw = rab->hnb_gw;
|
||||
struct osmo_pfcp_msg *m = osmo_pfcp_cp_peer_new_req(hnb_gw->pfcp.cp_peer, msg_type);
|
||||
|
||||
m->h.seid_present = true;
|
||||
m->h.seid = rab->up_f_seid.seid;
|
||||
ps_rab_pfcp_set_msg_ctx(rab, m);
|
||||
return m;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (rab->rab_id != rab_id)
|
||||
continue;
|
||||
return rab;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ps_rab_is_established(const struct ps_rab *rab)
|
||||
{
|
||||
return rab && rab->fi->state == PS_RAB_ST_ESTABLISHED;
|
||||
}
|
||||
|
||||
void ps_rab_failure(struct ps_rab *rab)
|
||||
{
|
||||
if (rab->req_fi)
|
||||
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);
|
||||
}
|
||||
|
||||
struct ps_rab *ps_rab_start(struct hnbgw_context_map *map, uint8_t rab_id,
|
||||
const struct addr_teid *core_f_teid, bool use_x213_nsap,
|
||||
struct osmo_fsm_inst *req_fi)
|
||||
{
|
||||
struct osmo_fsm_inst *fi;
|
||||
struct ps_rab *rab;
|
||||
|
||||
rab = ps_rab_alloc(map, rab_id);
|
||||
fi = rab->fi;
|
||||
rab->req_fi = req_fi;
|
||||
rab->core.remote = *core_f_teid;
|
||||
rab->core.use_x213_nsap = use_x213_nsap;
|
||||
|
||||
/* Got the RAB's Core side GTP info. Route the GTP for via the local UPF.
|
||||
* Establish a PFCP session with the UPF: tell it about the Core side GTP endpoint and request local F-TEIDs. */
|
||||
if (ps_rab_fsm_state_chg(PS_RAB_ST_WAIT_PFCP_EST_RESP)) {
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return rab;
|
||||
}
|
||||
|
||||
/* Add two PDR and two FAR to the PFCP Session Establishment Request message, according to the information found in rab.
|
||||
*/
|
||||
static int rab_to_pfcp_session_est_req(struct osmo_pfcp_msg_session_est_req *ser, struct ps_rab *rab)
|
||||
{
|
||||
if (ser->create_pdr_count + 2 > ARRAY_SIZE(ser->create_pdr)
|
||||
|| ser->create_far_count + 2 > ARRAY_SIZE(ser->create_far)) {
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "insufficient space for Create PDR / Create FAR IEs\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Core to Access:
|
||||
* - UPF should return an F-TEID for the PDR, to be forwarded back to Core later in
|
||||
* RANAP RAB Assignment Response.
|
||||
* - we don't know the Access side GTP address yet, so set FAR to DROP.
|
||||
*/
|
||||
ser->create_pdr[ser->create_pdr_count] = (struct osmo_pfcp_ie_create_pdr){
|
||||
.pdr_id = ID_CORE_TO_ACCESS,
|
||||
.precedence = 255,
|
||||
.pdi = {
|
||||
.source_iface = OSMO_PFCP_SOURCE_IFACE_CORE,
|
||||
.local_f_teid_present = true,
|
||||
.local_f_teid = {
|
||||
.choose_flag = true,
|
||||
.choose = {
|
||||
.ipv4_addr = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
.outer_header_removal_present = true,
|
||||
.outer_header_removal = {
|
||||
.desc = OSMO_PFCP_OUTER_HEADER_REMOVAL_GTP_U_UDP_IPV4,
|
||||
},
|
||||
.far_id_present = true,
|
||||
.far_id = ID_CORE_TO_ACCESS,
|
||||
};
|
||||
ser->create_pdr_count++;
|
||||
|
||||
ser->create_far[ser->create_far_count] = (struct osmo_pfcp_ie_create_far){
|
||||
.far_id = ID_CORE_TO_ACCESS,
|
||||
};
|
||||
osmo_pfcp_bits_set(ser->create_far[ser->create_far_count].apply_action.bits,
|
||||
OSMO_PFCP_APPLY_ACTION_DROP, true);
|
||||
ser->create_far_count++;
|
||||
|
||||
/* Access to Core:
|
||||
* - UPF should return an F-TEID for the PDR, to be forwarded to Access in the modified
|
||||
* RANAP RAB Assignment Request.
|
||||
* - we already know the Core's GTP endpoint F-TEID, so fully set up this FAR.
|
||||
*/
|
||||
ser->create_pdr[ser->create_pdr_count] = (struct osmo_pfcp_ie_create_pdr){
|
||||
.pdr_id = ID_ACCESS_TO_CORE,
|
||||
.precedence = 255,
|
||||
.pdi = {
|
||||
.source_iface = OSMO_PFCP_SOURCE_IFACE_ACCESS,
|
||||
.local_f_teid_present = true,
|
||||
.local_f_teid = {
|
||||
.choose_flag = true,
|
||||
.choose = {
|
||||
.ipv4_addr = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
.outer_header_removal_present = true,
|
||||
.outer_header_removal = {
|
||||
.desc = OSMO_PFCP_OUTER_HEADER_REMOVAL_GTP_U_UDP_IPV4,
|
||||
},
|
||||
.far_id_present = true,
|
||||
.far_id = ID_ACCESS_TO_CORE,
|
||||
};
|
||||
ser->create_pdr_count++;
|
||||
|
||||
ser->create_far[ser->create_far_count] = (struct osmo_pfcp_ie_create_far){
|
||||
.far_id = ID_ACCESS_TO_CORE,
|
||||
.forw_params_present = true,
|
||||
.forw_params = {
|
||||
.destination_iface = OSMO_PFCP_DEST_IFACE_CORE,
|
||||
.outer_header_creation_present = true,
|
||||
.outer_header_creation = {
|
||||
.teid_present = true,
|
||||
.teid = rab->core.remote.teid,
|
||||
.ip_addr.v4_present = true,
|
||||
.ip_addr.v4 = rab->core.remote.addr,
|
||||
},
|
||||
},
|
||||
};
|
||||
osmo_pfcp_bits_set(ser->create_far[ser->create_far_count].forw_params.outer_header_creation.desc_bits,
|
||||
OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4, true);
|
||||
osmo_pfcp_bits_set(ser->create_far[ser->create_far_count].apply_action.bits,
|
||||
OSMO_PFCP_APPLY_ACTION_FORW, true);
|
||||
ser->create_far_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_pfcp_est_resp(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg);
|
||||
|
||||
static void ps_rab_fsm_wait_pfcp_est_resp_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct ps_rab *rab = fi->priv;
|
||||
struct hnb_gw *hnb_gw = rab->hnb_gw;
|
||||
struct osmo_pfcp_msg *m;
|
||||
struct osmo_pfcp_ie_f_seid cp_f_seid;
|
||||
struct osmo_pfcp_msg_session_est_req *ser;
|
||||
|
||||
/* So far we have the rab->core.remote information. Send that to the UPF.
|
||||
* Also request all local GTP endpoints from UPF (rab->{core,access}.local) */
|
||||
m = ps_rab_new_pfcp_msg_req(rab, OSMO_PFCP_MSGT_SESSION_EST_REQ);
|
||||
|
||||
/* Send UP-SEID as zero, the UPF has yet to assign a SEID for itself remotely */
|
||||
m->h.seid = 0;
|
||||
|
||||
/* Make a new CP-SEID, our local reference for the PFCP session. */
|
||||
rab->cp_seid = osmo_pfcp_next_seid(&hnb_gw->pfcp.cp_peer->next_seid_state);
|
||||
cp_f_seid = (struct osmo_pfcp_ie_f_seid){
|
||||
.seid = rab->cp_seid,
|
||||
};
|
||||
osmo_pfcp_ip_addrs_set(&cp_f_seid.ip_addr, &osmo_pfcp_endpoint_get_cfg(hnb_gw->pfcp.ep)->local_addr);
|
||||
|
||||
m->ies.session_est_req = (struct osmo_pfcp_msg_session_est_req){
|
||||
.node_id = m->ies.session_est_req.node_id,
|
||||
.cp_f_seid_present = true,
|
||||
.cp_f_seid = cp_f_seid,
|
||||
};
|
||||
ser = &m->ies.session_est_req;
|
||||
|
||||
/* Create PDR+FAR pairs */
|
||||
if (rab_to_pfcp_session_est_req(ser, rab)) {
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "Failed to compose PFCP message\n");
|
||||
osmo_pfcp_msg_free(m);
|
||||
ps_rab_failure(rab);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send PFCP Session Establishment Request to UPF, wait for response. */
|
||||
m->ctx.resp_cb = on_pfcp_est_resp;
|
||||
if (osmo_pfcp_endpoint_tx(hnb_gw->pfcp.ep, m)) {
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "Failed to send PFCP message\n");
|
||||
ps_rab_failure(rab);
|
||||
}
|
||||
}
|
||||
|
||||
static int on_pfcp_est_resp(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg)
|
||||
{
|
||||
struct ps_rab *rab = req->ctx.session_fi->priv;
|
||||
|
||||
/* Send as FSM event to ensure this step is currently allowed */
|
||||
osmo_fsm_inst_dispatch(rab->fi, PS_RAB_EV_PFCP_EST_RESP, rx_resp);
|
||||
|
||||
/* By returning 0 here, the rx_resp message is not dispatched "again" to pfcp_ep->rx_msg(). We've handled it
|
||||
* here already. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ps_rab_rx_pfcp_est_resp(struct osmo_fsm_inst *fi, struct osmo_pfcp_msg *rx);
|
||||
|
||||
static void ps_rab_fsm_wait_pfcp_est_resp(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case PS_RAB_EV_PFCP_EST_RESP:
|
||||
ps_rab_rx_pfcp_est_resp(fi, data);
|
||||
break;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Look for dst->local.pdr_id in ser->created_pdr[], and copy the GTP endpoint info to dst->local.addr_teid, if found. */
|
||||
static int get_local_f_teid_from_created_pdr(struct half_gtp_map *dst, struct osmo_pfcp_msg_session_est_resp *ser,
|
||||
uint8_t pdr_id)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ser->created_pdr_count; i++) {
|
||||
struct osmo_pfcp_ie_created_pdr *cpdr = &ser->created_pdr[i];
|
||||
if (cpdr->pdr_id != pdr_id)
|
||||
continue;
|
||||
if (!cpdr->local_f_teid_present)
|
||||
continue;
|
||||
if (cpdr->local_f_teid.choose_flag)
|
||||
continue;
|
||||
if (!cpdr->local_f_teid.fixed.ip_addr.v4_present)
|
||||
continue;
|
||||
dst->local.addr = cpdr->local_f_teid.fixed.ip_addr.v4;
|
||||
dst->local.teid = cpdr->local_f_teid.fixed.teid;
|
||||
dst->local.present = true;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ps_rab_rx_pfcp_est_resp(struct osmo_fsm_inst *fi, struct osmo_pfcp_msg *rx)
|
||||
{
|
||||
struct ps_rab *rab = fi->priv;
|
||||
enum osmo_pfcp_cause *cause;
|
||||
struct osmo_pfcp_msg_session_est_resp *ser;
|
||||
|
||||
if (!rx) {
|
||||
/* This happens when no response has arrived after all PFCP timeouts and retransmissions. */
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "No response to PFCP Session Establishment Request\n");
|
||||
goto pfcp_session_est_failed;
|
||||
}
|
||||
|
||||
ser = &rx->ies.session_est_resp;
|
||||
|
||||
cause = osmo_pfcp_msg_cause(rx);
|
||||
if (!cause || *cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED) {
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "PFCP Session Establishment Response was not successful: %s\n",
|
||||
cause ? osmo_pfcp_cause_str(*cause) : "NULL");
|
||||
goto pfcp_session_est_failed;
|
||||
}
|
||||
|
||||
/* Get the UPF's SEID for future messages for this PFCP session */
|
||||
if (!ser->up_f_seid_present) {
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "PFCP Session Establishment Response lacks a UP F-SEID\n");
|
||||
goto pfcp_session_est_failed;
|
||||
}
|
||||
rab->up_f_seid = ser->up_f_seid;
|
||||
|
||||
if (rab->release_requested) {
|
||||
/* The UE conn or the entire HNB has released while we were waiting for a PFCP response. Now that there
|
||||
* is a remote SEID, we can finally delete the session that we asked for earlier. */
|
||||
ps_rab_fsm_state_chg(PS_RAB_ST_WAIT_PFCP_DEL_RESP);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the UPF's local F-TEIDs for both Core and Access */
|
||||
if (get_local_f_teid_from_created_pdr(&rab->core, ser, ID_CORE_TO_ACCESS)
|
||||
|| get_local_f_teid_from_created_pdr(&rab->access, ser, ID_ACCESS_TO_CORE)) {
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "Missing F-TEID in PFCP Session Establishment Response\n");
|
||||
ps_rab_failure(rab);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rab->req_fi)
|
||||
osmo_fsm_inst_dispatch(rab->req_fi, PS_RAB_ASS_EV_LOCAL_F_TEIDS_RX, rab);
|
||||
|
||||
/* The RAB Assignment Response will yield the hNodeB's F-TEID, i.e. the F-TEID we are supposed to send to Access
|
||||
* in outgoing GTP packets. */
|
||||
ps_rab_fsm_state_chg(PS_RAB_ST_WAIT_ACCESS_REMOTE_F_TEID);
|
||||
return;
|
||||
|
||||
pfcp_session_est_failed:
|
||||
if (rab->release_requested) {
|
||||
/* the RAB was released and we were waiting for some PFCP responsewhile waiting for a response, and now
|
||||
* we know that no session has been created. No PFCP left, deallocate. */
|
||||
ps_rab_fsm_state_chg(PS_RAB_ST_WAIT_USE_COUNT);
|
||||
return;
|
||||
}
|
||||
ps_rab_failure(rab);
|
||||
}
|
||||
|
||||
int ps_rab_rx_access_remote_f_teid(struct hnbgw_context_map *map, uint8_t rab_id,
|
||||
const struct ps_rab_rx_args *args)
|
||||
{
|
||||
int rc;
|
||||
struct ps_rab *rab = ps_rab_get(map, rab_id);
|
||||
if (!rab) {
|
||||
LOG_MAP(map, DLPFCP, LOGL_ERROR, "There is no RAB with id %u\n", rab_id);
|
||||
return -ENOENT;
|
||||
}
|
||||
/* Dispatch as event to make sure this is currently allowed */
|
||||
rc = osmo_fsm_inst_dispatch(rab->fi, PS_RAB_EV_RX_ACCESS_REMOTE_F_TEID, (void *)args);
|
||||
if (rc)
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ps_rab_fsm_wait_access_remote_f_teid(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct ps_rab *rab = fi->priv;
|
||||
const struct ps_rab_rx_args *args;
|
||||
switch (event) {
|
||||
case PS_RAB_EV_RX_ACCESS_REMOTE_F_TEID:
|
||||
args = data;
|
||||
rab->resp_fi = args->notify_fi;
|
||||
rab->access.use_x213_nsap = args->use_x213_nsap;
|
||||
rab->access.remote = args->f_teid;
|
||||
ps_rab_fsm_state_chg(PS_RAB_ST_WAIT_PFCP_MOD_RESP);
|
||||
return;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add an Update FAR to the PFCP Session Modification Request message, updating a remote F-TEID. */
|
||||
static int rab_to_pfcp_session_mod_req_upd_far(struct osmo_pfcp_msg_session_mod_req *smr,
|
||||
uint32_t far_id, const struct addr_teid *remote_f_teid)
|
||||
{
|
||||
if (smr->upd_far_count + 1 > ARRAY_SIZE(smr->upd_far))
|
||||
return -1;
|
||||
|
||||
smr->upd_far[smr->upd_far_count] = (struct osmo_pfcp_ie_upd_far){
|
||||
.far_id = far_id,
|
||||
.apply_action_present = true,
|
||||
/* apply_action.bits set below */
|
||||
.upd_forw_params_present = true,
|
||||
.upd_forw_params = {
|
||||
.outer_header_creation_present = true,
|
||||
.outer_header_creation = {
|
||||
/* desc_bits set below */
|
||||
.teid_present = true,
|
||||
.teid = remote_f_teid->teid,
|
||||
.ip_addr.v4_present = true,
|
||||
.ip_addr.v4 = remote_f_teid->addr,
|
||||
},
|
||||
},
|
||||
};
|
||||
osmo_pfcp_bits_set(smr->upd_far[smr->upd_far_count].apply_action.bits,
|
||||
OSMO_PFCP_APPLY_ACTION_FORW, true);
|
||||
osmo_pfcp_bits_set(smr->upd_far[smr->upd_far_count].upd_forw_params.outer_header_creation.desc_bits,
|
||||
OSMO_PFCP_OUTER_HEADER_CREATION_GTP_U_UDP_IPV4, true);
|
||||
smr->upd_far_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_pfcp_mod_resp(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg);
|
||||
|
||||
static void ps_rab_fsm_wait_pfcp_mod_resp_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
/* We have been given the Access side's remote F-TEID, now in rab->access.remote, and we need to tell the UPF
|
||||
* about it. */
|
||||
struct ps_rab *rab = fi->priv;
|
||||
struct hnb_gw *hnb_gw = rab->hnb_gw;
|
||||
struct osmo_pfcp_msg *m;
|
||||
|
||||
if (!(rab->up_f_seid.ip_addr.v4_present /* || rab->up_f_seid.ip_addr.v6_present */)) {
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "no valid PFCP session\n");
|
||||
ps_rab_failure(rab);
|
||||
return;
|
||||
}
|
||||
|
||||
m = ps_rab_new_pfcp_msg_req(rab, OSMO_PFCP_MSGT_SESSION_MOD_REQ);
|
||||
|
||||
if (rab_to_pfcp_session_mod_req_upd_far(&m->ies.session_mod_req, ID_ACCESS_TO_CORE, &rab->access.remote)) {
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "error composing Update FAR IE in PFCP msg\n");
|
||||
ps_rab_failure(rab);
|
||||
return;
|
||||
}
|
||||
|
||||
m->ctx.resp_cb = on_pfcp_mod_resp;
|
||||
if (osmo_pfcp_endpoint_tx(hnb_gw->pfcp.ep, m)) {
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "Failed to send PFCP message\n");
|
||||
ps_rab_failure(rab);
|
||||
}
|
||||
}
|
||||
|
||||
static int on_pfcp_mod_resp(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg)
|
||||
{
|
||||
struct ps_rab *rab = req->ctx.session_fi->priv;
|
||||
|
||||
/* Send as FSM event to ensure this step is currently allowed */
|
||||
osmo_fsm_inst_dispatch(rab->fi, PS_RAB_EV_PFCP_MOD_RESP, rx_resp);
|
||||
|
||||
/* By returning 0 here, the rx_resp message is not dispatched "again" to pfcp_ep->rx_msg(). We've handled it
|
||||
* here already. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ps_rab_rx_pfcp_mod_resp(struct osmo_fsm_inst *fi, struct osmo_pfcp_msg *rx);
|
||||
|
||||
static void ps_rab_fsm_wait_pfcp_mod_resp(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case PS_RAB_EV_PFCP_MOD_RESP:
|
||||
ps_rab_rx_pfcp_mod_resp(fi, data);
|
||||
return;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void ps_rab_rx_pfcp_mod_resp(struct osmo_fsm_inst *fi, struct osmo_pfcp_msg *rx)
|
||||
{
|
||||
struct ps_rab *rab = fi->priv;
|
||||
enum osmo_pfcp_cause *cause;
|
||||
|
||||
if (!rx) {
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "No response to PFCP Session Modification Request\n");
|
||||
ps_rab_failure(rab);
|
||||
return;
|
||||
}
|
||||
|
||||
cause = osmo_pfcp_msg_cause(rx);
|
||||
if (!cause || *cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED) {
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "PFCP Session Modification Response was not successful: %s\n",
|
||||
cause ? osmo_pfcp_cause_str(*cause) : "NULL");
|
||||
ps_rab_failure(rab);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This RAB is now complete. Everything went as expected, now we can forward the RAB Assignment Response to the
|
||||
* CN. */
|
||||
ps_rab_fsm_state_chg(PS_RAB_ST_ESTABLISHED);
|
||||
}
|
||||
|
||||
static void ps_rab_fsm_established_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_ESTABLISHED, rab);
|
||||
}
|
||||
|
||||
static int on_pfcp_del_resp(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg);
|
||||
|
||||
static void ps_rab_fsm_wait_pfcp_del_resp_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
/* If a PFCP session has been established, send a Session Deletion Request and wait for the response.
|
||||
* If no session is established, just terminate. */
|
||||
struct ps_rab *rab = fi->priv;
|
||||
struct hnb_gw *hnb_gw = rab->hnb_gw;
|
||||
struct osmo_pfcp_msg *m;
|
||||
|
||||
if (!(rab->up_f_seid.ip_addr.v4_present /* || rab->up_f_seid.ip_addr.v6_present */)) {
|
||||
/* There is no valid PFCP session, so no need to send a Session Deletion Request */
|
||||
ps_rab_fsm_state_chg(PS_RAB_ST_WAIT_USE_COUNT);
|
||||
return;
|
||||
}
|
||||
|
||||
m = ps_rab_new_pfcp_msg_req(rab, OSMO_PFCP_MSGT_SESSION_DEL_REQ);
|
||||
m->ctx.resp_cb = on_pfcp_del_resp;
|
||||
if (osmo_pfcp_endpoint_tx(hnb_gw->pfcp.ep, m)) {
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "Failed to send PFCP message\n");
|
||||
ps_rab_failure(rab);
|
||||
}
|
||||
}
|
||||
|
||||
static int on_pfcp_del_resp(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg)
|
||||
{
|
||||
struct ps_rab *rab = req->ctx.session_fi->priv;
|
||||
if (errmsg)
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "PFCP Session Deletion Response: %s\n", errmsg);
|
||||
osmo_fsm_inst_dispatch(rab->fi, PS_RAB_EV_PFCP_DEL_RESP, rx_resp);
|
||||
|
||||
/* By returning 0 here, the rx_resp message is not dispatched "again" to pfcp_ep->rx_msg(). We've handled it
|
||||
* here already. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ps_rab_fsm_wait_pfcp_del_resp(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case PS_RAB_EV_PFCP_DEL_RESP:
|
||||
/* All done, terminate. Even if the Session Deletion failed, there's nothing we can do about it. */
|
||||
ps_rab_fsm_state_chg(PS_RAB_ST_WAIT_USE_COUNT);
|
||||
return;
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static int ps_rab_fsm_use_cb(struct osmo_use_count_entry *e, int32_t old_use_count, const char *file, int line)
|
||||
{
|
||||
struct ps_rab *rab = e->use_count->talloc_object;
|
||||
if (!osmo_use_count_total(&rab->use_count))
|
||||
osmo_fsm_inst_dispatch(rab->fi, PS_RAB_EV_USE_COUNT_ZERO, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ps_rab_fsm_wait_use_count_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct ps_rab *rab = fi->priv;
|
||||
OSMO_ASSERT(osmo_use_count_get_put(&rab->use_count, PS_RAB_USE_ACTIVE, -1) == 0);
|
||||
}
|
||||
|
||||
static void ps_rab_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
|
||||
case PS_RAB_EV_USE_COUNT_ZERO:
|
||||
if (fi->state == PS_RAB_ST_WAIT_USE_COUNT)
|
||||
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
|
||||
/* else, ignore. */
|
||||
return;
|
||||
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
static void ps_rab_forget_map(struct ps_rab *rab)
|
||||
{
|
||||
if (rab->map)
|
||||
llist_del(&rab->entry);
|
||||
rab->map = NULL;
|
||||
}
|
||||
|
||||
static void ps_rab_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
struct ps_rab *rab = fi->priv;
|
||||
ps_rab_forget_map(rab);
|
||||
}
|
||||
|
||||
void ps_rab_release(struct ps_rab *rab)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = rab->fi;
|
||||
ps_rab_forget_map(rab);
|
||||
switch (fi->state) {
|
||||
case PS_RAB_ST_RX_CORE_REMOTE_F_TEID:
|
||||
/* No session requested yet. Nothing to be deleted. */
|
||||
LOG_PS_RAB(rab, LOGL_NOTICE, "RAB release before PFCP Session Establishment Request, terminating\n");
|
||||
ps_rab_fsm_state_chg(PS_RAB_ST_WAIT_USE_COUNT);
|
||||
return;
|
||||
case PS_RAB_ST_WAIT_PFCP_EST_RESP:
|
||||
/* Session was requested via PFCP, but we only know the SEID to send in a deletion when the PFCP Session
|
||||
* Establishment Response arrives. */
|
||||
rab->release_requested = true;
|
||||
LOG_PS_RAB(rab, LOGL_ERROR, "RAB release while waiting for PFCP Session Establishment Response\n");
|
||||
return;
|
||||
default:
|
||||
/* Session has been established (and we know the SEID). Initiate deletion. */
|
||||
LOG_PS_RAB(rab, LOGL_INFO, "RAB release, deleting PFCP session\n");
|
||||
ps_rab_fsm_state_chg(PS_RAB_ST_WAIT_PFCP_DEL_RESP);
|
||||
return;
|
||||
case PS_RAB_ST_WAIT_PFCP_DEL_RESP:
|
||||
/* Already requested a PFCP Session Deletion. Nothing else to do, wait for the Deletion Response (or
|
||||
* timeout). */
|
||||
LOG_PS_RAB(rab, LOGL_INFO, "RAB release while waiting for PFCP Session Deletion Response\n");
|
||||
return;
|
||||
case PS_RAB_ST_WAIT_USE_COUNT:
|
||||
/* Already released, just wait for the last users (queued PFCP messages) to expire. */
|
||||
LOG_PS_RAB(rab, LOGL_INFO, "RAB release, already waiting for deallocation\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
static const struct osmo_fsm_state ps_rab_fsm_states[] = {
|
||||
[PS_RAB_ST_RX_CORE_REMOTE_F_TEID] = {
|
||||
.name = "RX_CORE_REMOTE_F_TEID",
|
||||
.out_state_mask = 0
|
||||
| S(PS_RAB_ST_WAIT_PFCP_EST_RESP)
|
||||
| S(PS_RAB_ST_WAIT_USE_COUNT)
|
||||
,
|
||||
},
|
||||
[PS_RAB_ST_WAIT_PFCP_EST_RESP] = {
|
||||
.name = "WAIT_PFCP_EST_RESP",
|
||||
.onenter = ps_rab_fsm_wait_pfcp_est_resp_onenter,
|
||||
.action = ps_rab_fsm_wait_pfcp_est_resp,
|
||||
.in_event_mask = 0
|
||||
| S(PS_RAB_EV_PFCP_EST_RESP)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(PS_RAB_ST_WAIT_ACCESS_REMOTE_F_TEID)
|
||||
| S(PS_RAB_ST_WAIT_USE_COUNT)
|
||||
,
|
||||
},
|
||||
[PS_RAB_ST_WAIT_ACCESS_REMOTE_F_TEID] = {
|
||||
.name = "WAIT_ACCESS_REMOTE_F_TEID",
|
||||
.action = ps_rab_fsm_wait_access_remote_f_teid,
|
||||
.in_event_mask = 0
|
||||
| S(PS_RAB_EV_RX_ACCESS_REMOTE_F_TEID)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(PS_RAB_ST_WAIT_PFCP_MOD_RESP)
|
||||
| S(PS_RAB_ST_WAIT_PFCP_DEL_RESP)
|
||||
| S(PS_RAB_ST_WAIT_USE_COUNT)
|
||||
,
|
||||
},
|
||||
[PS_RAB_ST_WAIT_PFCP_MOD_RESP] = {
|
||||
.name = "WAIT_PFCP_MOD_RESP",
|
||||
.onenter = ps_rab_fsm_wait_pfcp_mod_resp_onenter,
|
||||
.action = ps_rab_fsm_wait_pfcp_mod_resp,
|
||||
.in_event_mask = 0
|
||||
| S(PS_RAB_EV_PFCP_MOD_RESP)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(PS_RAB_ST_ESTABLISHED)
|
||||
| S(PS_RAB_ST_WAIT_PFCP_DEL_RESP)
|
||||
| S(PS_RAB_ST_WAIT_USE_COUNT)
|
||||
,
|
||||
},
|
||||
[PS_RAB_ST_ESTABLISHED] = {
|
||||
.name = "ESTABLISHED",
|
||||
.onenter = ps_rab_fsm_established_onenter,
|
||||
.out_state_mask = 0
|
||||
| S(PS_RAB_ST_WAIT_PFCP_DEL_RESP)
|
||||
| S(PS_RAB_ST_WAIT_USE_COUNT)
|
||||
,
|
||||
},
|
||||
[PS_RAB_ST_WAIT_PFCP_DEL_RESP] = {
|
||||
.name = "WAIT_PFCP_DEL_RESP",
|
||||
.onenter = ps_rab_fsm_wait_pfcp_del_resp_onenter,
|
||||
.action = ps_rab_fsm_wait_pfcp_del_resp,
|
||||
.in_event_mask = 0
|
||||
| S(PS_RAB_EV_PFCP_DEL_RESP)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(PS_RAB_ST_WAIT_USE_COUNT)
|
||||
,
|
||||
},
|
||||
[PS_RAB_ST_WAIT_USE_COUNT] = {
|
||||
.name = "WAIT_USE_COUNT",
|
||||
.onenter = ps_rab_fsm_wait_use_count_onenter,
|
||||
.in_event_mask = 0
|
||||
| S(PS_RAB_EV_USE_COUNT_ZERO)
|
||||
,
|
||||
},
|
||||
};
|
||||
|
||||
static struct osmo_fsm ps_rab_fsm = {
|
||||
.name = "ps_rab",
|
||||
.states = ps_rab_fsm_states,
|
||||
.num_states = ARRAY_SIZE(ps_rab_fsm_states),
|
||||
.log_subsys = DLPFCP,
|
||||
.event_names = ps_rab_fsm_event_names,
|
||||
.cleanup = ps_rab_fsm_cleanup,
|
||||
.allstate_event_mask = S(PS_RAB_EV_USE_COUNT_ZERO),
|
||||
.allstate_action = ps_rab_fsm_allstate_action,
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void ps_rab_fsm_register(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&ps_rab_fsm) == 0);
|
||||
}
|
||||
627
src/osmo-hnbgw/ranap_rab_ass.c
Normal file
627
src/osmo-hnbgw/ranap_rab_ass.c
Normal file
@@ -0,0 +1,627 @@
|
||||
/* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Note: This files contains tools to decode and re-encode the RAB-AssignmentRequest. This set of tools is used by
|
||||
* mgcp_fsm.c to extract and manipulate the transportLayerAddress. */
|
||||
|
||||
#include <errno.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/sockaddr_str.h>
|
||||
#include <osmocom/hnbgw/hnbgw.h>
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/ranap_common_cn.h>
|
||||
#include <osmocom/ranap/ranap_common_ran.h>
|
||||
#include <osmocom/ranap/iu_helpers.h>
|
||||
#include <asn1c/asn1helpers.h>
|
||||
|
||||
/*! Encode RABAP RAB AssignmentRequest from RANAP_RAB_AssignmentRequestIEs.
|
||||
* \ptmap[out] data user provided memory to store resulting ASN.1 encoded message.
|
||||
* \ptmap[in] len length of user provided memory to store resulting ASN.1 encoded message.
|
||||
* \ptmap[in] ies user provided memory with RANAP_RAB_AssignmentRequestIEs.
|
||||
* \returns resulting message length on success; negative on error. */
|
||||
struct msgb *ranap_rab_ass_req_encode(RANAP_RAB_AssignmentRequestIEs_t *rab_assignment_request_ies)
|
||||
{
|
||||
int rc;
|
||||
struct msgb *msg;
|
||||
RANAP_RAB_AssignmentRequest_t _rab_assignment_request;
|
||||
RANAP_RAB_AssignmentRequest_t *rab_assignment_request = &_rab_assignment_request;
|
||||
|
||||
memset(rab_assignment_request, 0, sizeof(*rab_assignment_request));
|
||||
|
||||
rc = ranap_encode_rab_assignmentrequesties(rab_assignment_request, rab_assignment_request_ies);
|
||||
if (rc < 0)
|
||||
return NULL;
|
||||
|
||||
/* generate an Initiating Mesasage */
|
||||
msg = ranap_generate_initiating_message(RANAP_ProcedureCode_id_RAB_Assignment,
|
||||
RANAP_Criticality_reject,
|
||||
&asn_DEF_RANAP_RAB_AssignmentRequest, rab_assignment_request);
|
||||
|
||||
/* 'msg' has been generated, we cann now release the input 'out' */
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_AssignmentRequest, rab_assignment_request);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*! Encode RABAP RAB AssignmentRequest from RANAP_RAB_AssignmentResponseIEs.
|
||||
* \ptmap[out] data user provided memory to store resulting ASN.1 encoded message.
|
||||
* \ptmap[in] len length of user provided memory to store resulting ASN.1 encoded message.
|
||||
* \ptmap[in] ies user provided memory with RANAP_RAB_AssignmentResponseIEs.
|
||||
* \returns resulting message length on success; negative on error. */
|
||||
int ranap_rab_ass_resp_encode(uint8_t *data, unsigned int len,
|
||||
RANAP_RAB_AssignmentResponseIEs_t *rab_assignment_response_ies)
|
||||
{
|
||||
int rc;
|
||||
struct msgb *msg;
|
||||
|
||||
RANAP_RAB_AssignmentResponse_t _rab_assignment_response;
|
||||
RANAP_RAB_AssignmentResponse_t *rab_assignment_response = &_rab_assignment_response;
|
||||
|
||||
memset(data, 0, len);
|
||||
memset(rab_assignment_response, 0, sizeof(*rab_assignment_response));
|
||||
|
||||
rc = ranap_encode_rab_assignmentresponseies(rab_assignment_response, rab_assignment_response_ies);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* generate an outcome mesasage */
|
||||
msg = ranap_generate_outcome(RANAP_ProcedureCode_id_RAB_Assignment,
|
||||
RANAP_Criticality_reject,
|
||||
&asn_DEF_RANAP_RAB_AssignmentResponse, rab_assignment_response);
|
||||
|
||||
/* 'msg' has been generated, we cann now release the input 'out' */
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_AssignmentResponse, rab_assignment_response);
|
||||
|
||||
if (!msg)
|
||||
return -EINVAL;
|
||||
if (msg->len > len)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(data, msg->data, msg->len);
|
||||
rc = msg->len;
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Pick the indexed item from the RAB setup-or-modify list and return the first protocol-ie-field-pair. */
|
||||
static RANAP_ProtocolIE_FieldPair_t *prot_ie_field_pair_from_ass_req_ies(const RANAP_RAB_AssignmentRequestIEs_t *ies,
|
||||
unsigned int index)
|
||||
{
|
||||
RANAP_ProtocolIE_ContainerPair_t *protocol_ie_container_pair;
|
||||
RANAP_ProtocolIE_FieldPair_t *protocol_ie_field_pair;
|
||||
|
||||
/* Make sure we indeed deal with a setup-or-modify list */
|
||||
if (!(ies->presenceMask & RAB_ASSIGNMENTREQUESTIES_RANAP_RAB_SETUPORMODIFYLIST_PRESENT)) {
|
||||
RANAP_DEBUG
|
||||
("Decoding failed, the RANAP RAB AssignmentRequest did not contain a setup-or-modify list!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Detect the end of the list */
|
||||
if (index >= ies->raB_SetupOrModifyList.list.count)
|
||||
return NULL;
|
||||
|
||||
protocol_ie_container_pair = ies->raB_SetupOrModifyList.list.array[index];
|
||||
protocol_ie_field_pair = protocol_ie_container_pair->list.array[0];
|
||||
|
||||
return protocol_ie_field_pair;
|
||||
}
|
||||
|
||||
/* Pick the indexed item from the RAB release-list list and return a pointer to it */
|
||||
static RANAP_IE_t *release_item_from_ass_req_ies(const RANAP_RAB_AssignmentRequestIEs_t *ies, unsigned int index)
|
||||
{
|
||||
/* Make sure we indeed deal with a setup-or-modify list */
|
||||
if (!(ies->presenceMask & RAB_ASSIGNMENTREQUESTIES_RANAP_RAB_RELEASELIST_PRESENT)) {
|
||||
RANAP_DEBUG
|
||||
("Decoding failed, the RANAP RAB AssignmentRequest did not contain a release list!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Detect the end of the list */
|
||||
if (index >= ies->raB_ReleaseList.raB_ReleaseList_ies.list.count)
|
||||
return NULL;
|
||||
|
||||
return ies->raB_ReleaseList.raB_ReleaseList_ies.list.array[index];
|
||||
}
|
||||
|
||||
/* Pick the indexed item from the RAB setup-or-modified list and return a pointer to it */
|
||||
static RANAP_IE_t *setup_or_modif_item_from_rab_ass_resp(const RANAP_RAB_AssignmentResponseIEs_t *ies,
|
||||
unsigned int index)
|
||||
{
|
||||
/* Make sure we indeed deal with a setup-or-modified list */
|
||||
if (!(ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_SETUPORMODIFIEDLIST_PRESENT)) {
|
||||
RANAP_DEBUG("RANAP RAB AssignmentResponse did not contain a setup-or-modified list!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Detect the end of the list */
|
||||
if (index >= ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.count)
|
||||
return NULL;
|
||||
|
||||
return ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[index];
|
||||
}
|
||||
|
||||
/* Pick the indexed item from the RAB failed list and return a pointer to it */
|
||||
static RANAP_IE_t *failed_list_item_from_rab_ass_resp(const RANAP_RAB_AssignmentResponseIEs_t *ies,
|
||||
unsigned int index)
|
||||
{
|
||||
/* Make sure we indeed deal with a failed list */
|
||||
if (!(ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_FAILEDLIST_PRESENT)) {
|
||||
RANAP_DEBUG("RANAP RAB AssignmentResponse did not contain a failed list!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Detect the end of the list */
|
||||
if (index >= ies->raB_FailedList.raB_FailedList_ies.list.count)
|
||||
return NULL;
|
||||
|
||||
return ies->raB_FailedList.raB_FailedList_ies.list.array[index];
|
||||
}
|
||||
|
||||
/* find the RAB specified by rab_id in ies and when found, decode the result into items_ies */
|
||||
static int decode_rab_smditms_from_resp_ies(RANAP_RAB_SetupOrModifiedItemIEs_t *items_ies,
|
||||
RANAP_RAB_AssignmentResponseIEs_t *ies, uint8_t rab_id)
|
||||
{
|
||||
RANAP_IE_t *setup_or_modified_list_ie;
|
||||
RANAP_RAB_SetupOrModifiedItem_t *rab_setup_or_modified_item;
|
||||
int rc;
|
||||
uint8_t rab_id_decoded;
|
||||
unsigned int index = 0;
|
||||
|
||||
while (1) {
|
||||
setup_or_modified_list_ie = setup_or_modif_item_from_rab_ass_resp(ies, index);
|
||||
if (!setup_or_modified_list_ie)
|
||||
return -EINVAL;
|
||||
|
||||
rc = ranap_decode_rab_setupormodifieditemies_fromlist(items_ies, &setup_or_modified_list_ie->value);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
rab_setup_or_modified_item = &items_ies->raB_SetupOrModifiedItem;
|
||||
/* 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_decoded = rab_setup_or_modified_item->rAB_ID.buf[0];
|
||||
if (rab_id_decoded == rab_id)
|
||||
return index;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, items_ies);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
/* See comment above decode_rab_smditms_from_resp_ies() */
|
||||
static int decode_rab_flitms_from_resp_ies(RANAP_RAB_FailedItemIEs_t *items_ies,
|
||||
RANAP_RAB_AssignmentResponseIEs_t *ies, uint8_t rab_id)
|
||||
{
|
||||
RANAP_IE_t *failed_list_ie;
|
||||
RANAP_RAB_FailedItem_t *rab_failed_item;
|
||||
int rc;
|
||||
uint8_t rab_id_decoded;
|
||||
unsigned int index = 0;
|
||||
|
||||
while (1) {
|
||||
failed_list_ie = failed_list_item_from_rab_ass_resp(ies, index);
|
||||
if (!failed_list_ie)
|
||||
return -EINVAL;
|
||||
|
||||
rc = ranap_decode_rab_faileditemies_fromlist(items_ies, &failed_list_ie->value);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
rab_failed_item = &items_ies->raB_FailedItem;
|
||||
/* 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_decoded = rab_failed_item->rAB_ID.buf[0];
|
||||
if (rab_id_decoded == rab_id)
|
||||
return index;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_FailedItem, items_ies);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
/* find the RAB specified by rab_id in ies and when found, decode the result into item */
|
||||
static int decode_rab_smditms_from_req_ies(RANAP_RAB_SetupOrModifyItemFirst_t *item,
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies, uint8_t rab_id)
|
||||
{
|
||||
RANAP_ProtocolIE_FieldPair_t *protocol_ie_field_pair;
|
||||
int rc;
|
||||
uint8_t rab_id_decoded;
|
||||
unsigned int index = 0;
|
||||
|
||||
while (1) {
|
||||
protocol_ie_field_pair = prot_ie_field_pair_from_ass_req_ies(ies, index);
|
||||
if (!protocol_ie_field_pair)
|
||||
return -EINVAL;
|
||||
|
||||
if (protocol_ie_field_pair->id != RANAP_ProtocolIE_ID_id_RAB_SetupOrModifyItem) {
|
||||
RANAP_DEBUG
|
||||
("Decoding failed, the protocol IE field-pair is not of type RANAP RAB setup-or-modify-item!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = ranap_decode_rab_setupormodifyitemfirst(item, &protocol_ie_field_pair->firstValue);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
rab_id_decoded = item->rAB_ID.buf[0];
|
||||
if (rab_id_decoded == rab_id)
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
static int decode_rab_relitms_from_req_ies(RANAP_RAB_ReleaseItemIEs_t *items_ies,
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies, uint8_t rab_id)
|
||||
{
|
||||
RANAP_IE_t *release_list_ie;
|
||||
RANAP_RAB_ReleaseItem_t *rab_release_item;
|
||||
int rc;
|
||||
uint8_t rab_id_decoded;
|
||||
unsigned int index = 0;
|
||||
|
||||
while (1) {
|
||||
release_list_ie = release_item_from_ass_req_ies(ies, index);
|
||||
if (!release_list_ie)
|
||||
return -EINVAL;
|
||||
|
||||
if (release_list_ie->id != RANAP_ProtocolIE_ID_id_RAB_ReleaseItem) {
|
||||
RANAP_DEBUG("Decoding failed, the protocol IE is not of type RANAP RAB ReleaseItem!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = ranap_decode_rab_releaseitemies_fromlist(items_ies, &release_list_ie->value);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
rab_release_item = &items_ies->raB_ReleaseItem;
|
||||
/* 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_decoded = rab_release_item->rAB_ID.buf[0];
|
||||
if (rab_id_decoded == rab_id)
|
||||
return index;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_ReleaseItem, items_ies);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
/*! Extract IP address and port from RANAP_RAB_AssignmentRequestIEs.
|
||||
* \ptmap[out] addr user provided memory to store extracted RTP stream IP-Address and port number.
|
||||
* \ptmap[out] rab_id pointer to store RAB-ID (optional, can be NULL).
|
||||
* \ptmap[in] ies user provided memory with RANAP_RAB_AssignmentRequestIEs.
|
||||
* \ptmap[in] index index of the SetupOrModifyItem (e.g. 0 for the first list item).
|
||||
* \returns 0 on success; negative on error. */
|
||||
int ranap_rab_ass_req_ies_extract_inet_addr(struct osmo_sockaddr *addr, uint8_t *rab_id,
|
||||
RANAP_RAB_AssignmentRequestIEs_t *ies, unsigned int index)
|
||||
{
|
||||
RANAP_ProtocolIE_FieldPair_t *protocol_ie_field_pair;
|
||||
RANAP_RAB_SetupOrModifyItemFirst_t _rab_setup_or_modify_item_first;
|
||||
RANAP_RAB_SetupOrModifyItemFirst_t *rab_setup_or_modify_item_first = &_rab_setup_or_modify_item_first;
|
||||
RANAP_TransportLayerAddress_t *trasp_layer_addr;
|
||||
RANAP_IuTransportAssociation_t *transp_assoc;
|
||||
uint16_t port;
|
||||
int rc;
|
||||
|
||||
protocol_ie_field_pair = prot_ie_field_pair_from_ass_req_ies(ies, index);
|
||||
if (!protocol_ie_field_pair)
|
||||
return -EINVAL;
|
||||
|
||||
if (protocol_ie_field_pair->id != RANAP_ProtocolIE_ID_id_RAB_SetupOrModifyItem) {
|
||||
RANAP_DEBUG
|
||||
("Decoding failed, the protocol IE field-pair is not of type RANAP RAB setup-or-modify-item!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = ranap_decode_rab_setupormodifyitemfirst(rab_setup_or_modify_item_first,
|
||||
&protocol_ie_field_pair->firstValue);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (rab_id) {
|
||||
/* 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_setup_or_modify_item_first->rAB_ID.buf[0];
|
||||
}
|
||||
|
||||
/* Decode IP-Address */
|
||||
trasp_layer_addr = &rab_setup_or_modify_item_first->transportLayerInformation->transportLayerAddress;
|
||||
rc = ranap_transp_layer_addr_decode2(addr, NULL, trasp_layer_addr);
|
||||
if (rc < 0) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Decode port number */
|
||||
transp_assoc = &rab_setup_or_modify_item_first->transportLayerInformation->iuTransportAssociation;
|
||||
rc = ranap_transp_assoc_decode(&port, transp_assoc);
|
||||
if (rc < 0) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch (addr->u.sin.sin_family) {
|
||||
case AF_INET:
|
||||
addr->u.sin.sin_port = htons(port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
addr->u.sin6.sin6_port = htons(port);
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
error:
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyItemFirst, rab_setup_or_modify_item_first);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Extract IP address and port from RANAP_RAB_AssignmentResponseIEs.
|
||||
* \ptmap[out] addr user provided memory to store extracted RTP stream IP-Address and port number.
|
||||
* \ptmap[in] ies user provided memory with RANAP_RAB_AssignmentResponseIEs.
|
||||
* \ptmap[in] rab_id expected rab id to look for.
|
||||
* \returns 0 on success; negative on error. */
|
||||
int ranap_rab_ass_resp_ies_extract_inet_addr(struct osmo_sockaddr *addr, RANAP_RAB_AssignmentResponseIEs_t *ies, uint8_t rab_id)
|
||||
{
|
||||
RANAP_RAB_SetupOrModifiedItemIEs_t _rab_setup_or_modified_items_ies;
|
||||
RANAP_RAB_SetupOrModifiedItemIEs_t *rab_setup_or_modified_items_ies = &_rab_setup_or_modified_items_ies;
|
||||
RANAP_RAB_SetupOrModifiedItem_t *rab_setup_or_modified_item;
|
||||
uint16_t port;
|
||||
int rc;
|
||||
|
||||
rc = decode_rab_smditms_from_resp_ies(rab_setup_or_modified_items_ies, ies, rab_id);
|
||||
if (rc < 0)
|
||||
return -EINVAL;
|
||||
|
||||
rab_setup_or_modified_item = &rab_setup_or_modified_items_ies->raB_SetupOrModifiedItem;
|
||||
|
||||
/* Decode IP-Address */
|
||||
rc = ranap_transp_layer_addr_decode2(addr, NULL, rab_setup_or_modified_item->transportLayerAddress);
|
||||
if (rc < 0) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Decode port number */
|
||||
rc = ranap_transp_assoc_decode(&port, rab_setup_or_modified_item->iuTransportAssociation);
|
||||
if (rc < 0) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch (addr->u.sin.sin_family) {
|
||||
case AF_INET:
|
||||
addr->u.sin.sin_port = htons(port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
addr->u.sin6.sin6_port = htons(port);
|
||||
break;
|
||||
default:
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
error:
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, rab_setup_or_modified_items_ies);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Replace IP address and port in RANAP_RAB_AssignmentRequestIEs.
|
||||
* \ptmap[inout] ies user provided memory with RANAP_RAB_AssignmentRequestIEs.
|
||||
* \ptmap[in] addr user provided memory that contains the new RTP stream IP-Address and port number.
|
||||
* \ptmap[in] rab_id expected rab id to look for.
|
||||
* \returns 0 on success; negative on error. */
|
||||
int ranap_rab_ass_req_ies_replace_inet_addr(RANAP_RAB_AssignmentRequestIEs_t *ies, struct osmo_sockaddr *addr, uint8_t rab_id)
|
||||
{
|
||||
RANAP_ProtocolIE_FieldPair_t *protocol_ie_field_pair;
|
||||
RANAP_RAB_SetupOrModifyItemFirst_t _rab_setup_or_modify_item_first;
|
||||
RANAP_RAB_SetupOrModifyItemFirst_t *rab_setup_or_modify_item_first = &_rab_setup_or_modify_item_first;
|
||||
RANAP_TransportLayerInformation_t *old_transport_layer_information = NULL;
|
||||
RANAP_TransportLayerInformation_t *new_transport_layer_information = NULL;
|
||||
struct osmo_sockaddr addr_old;
|
||||
bool uses_x213_nsap;
|
||||
int rc;
|
||||
int index;
|
||||
|
||||
index = decode_rab_smditms_from_req_ies(rab_setup_or_modify_item_first, ies, rab_id);
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Replace transport-layer-information */
|
||||
if (rab_setup_or_modify_item_first->transportLayerInformation->iuTransportAssociation.present ==
|
||||
RANAP_IuTransportAssociation_PR_bindingID) {
|
||||
old_transport_layer_information = rab_setup_or_modify_item_first->transportLayerInformation;
|
||||
|
||||
/* Before we can re-encode the transport layer information, we need to know the format it was
|
||||
* encoded in. */
|
||||
rc = ranap_transp_layer_addr_decode2(&addr_old, &uses_x213_nsap,
|
||||
&old_transport_layer_information->transportLayerAddress);
|
||||
if (rc < 0) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Encode a new transport layer information field */
|
||||
new_transport_layer_information = ranap_new_transp_info_rtp(addr, uses_x213_nsap);
|
||||
if (!new_transport_layer_information) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
rab_setup_or_modify_item_first->transportLayerInformation = new_transport_layer_information;
|
||||
} else {
|
||||
RANAP_DEBUG("Rewriting transport layer information failed, no bindingID (port)!\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Reencode transport-layer-information */
|
||||
protocol_ie_field_pair = prot_ie_field_pair_from_ass_req_ies(ies, index);
|
||||
rc = ANY_fromType_aper(&protocol_ie_field_pair->firstValue, &asn_DEF_RANAP_RAB_SetupOrModifyItemFirst,
|
||||
rab_setup_or_modify_item_first);
|
||||
if (rc < 0) {
|
||||
RANAP_DEBUG("Rewriting transport layer information failed, could not reencode\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
/* Restore original state of the modified ASN.1 struct so that the asn1c free mechanisms can work properly */
|
||||
if (old_transport_layer_information)
|
||||
rab_setup_or_modify_item_first->transportLayerInformation = old_transport_layer_information;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyItemFirst, rab_setup_or_modify_item_first);
|
||||
if (new_transport_layer_information)
|
||||
ASN_STRUCT_FREE(asn_DEF_RANAP_TransportLayerInformation, new_transport_layer_information);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Replace IP address and port in RANAP_RAB_AssignmentResponseIEs.
|
||||
* \ptmap[inout] ies user provided memory with RANAP_RAB_AssignmentResponseIEs.
|
||||
* \ptmap[in] addr user provided memory that contains the new RTP stream IP-Address and port number.
|
||||
* \ptmap[in] rab_id expected rab id to look for.
|
||||
* \returns 0 on success; negative on error. */
|
||||
int ranap_rab_ass_resp_ies_replace_inet_addr(RANAP_RAB_AssignmentResponseIEs_t *ies, struct osmo_sockaddr *addr, uint8_t rab_id)
|
||||
{
|
||||
RANAP_IE_t *setup_or_modified_list_ie;
|
||||
RANAP_RAB_SetupOrModifiedItemIEs_t _rab_setup_or_modified_items_ies;
|
||||
RANAP_RAB_SetupOrModifiedItemIEs_t *rab_setup_or_modified_items_ies = &_rab_setup_or_modified_items_ies;
|
||||
RANAP_RAB_SetupOrModifiedItem_t *rab_setup_or_modified_item;
|
||||
RANAP_TransportLayerInformation_t *temp_transport_layer_information = NULL;
|
||||
RANAP_TransportLayerAddress_t *old_transport_layer_address = NULL;
|
||||
RANAP_IuTransportAssociation_t *old_iu_transport_association = NULL;
|
||||
struct osmo_sockaddr addr_old;
|
||||
bool uses_x213_nsap;
|
||||
int rc;
|
||||
int index;
|
||||
|
||||
index = decode_rab_smditms_from_resp_ies(rab_setup_or_modified_items_ies, ies, rab_id);
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
|
||||
rab_setup_or_modified_item = &rab_setup_or_modified_items_ies->raB_SetupOrModifiedItem;
|
||||
|
||||
/* Before we can re-encode the transport layer address, we need to know the format it was encoded in. */
|
||||
rc = ranap_transp_layer_addr_decode2(&addr_old, &uses_x213_nsap,
|
||||
rab_setup_or_modified_item->transportLayerAddress);
|
||||
if (rc < 0) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Generate a temporary transport layer information, from which we can use the transport layer address and
|
||||
* the iu transport association to update the setup or modified item */
|
||||
temp_transport_layer_information = ranap_new_transp_info_rtp(addr, uses_x213_nsap);
|
||||
if (!temp_transport_layer_information) {
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Replace transport layer address and iu transport association */
|
||||
old_transport_layer_address = rab_setup_or_modified_item->transportLayerAddress;
|
||||
old_iu_transport_association = rab_setup_or_modified_item->iuTransportAssociation;
|
||||
rab_setup_or_modified_item->transportLayerAddress = &temp_transport_layer_information->transportLayerAddress;
|
||||
rab_setup_or_modified_item->iuTransportAssociation = &temp_transport_layer_information->iuTransportAssociation;
|
||||
|
||||
/* Reencode modified setup or modified list */
|
||||
setup_or_modified_list_ie = setup_or_modif_item_from_rab_ass_resp(ies, index);
|
||||
rc = ANY_fromType_aper(&setup_or_modified_list_ie->value, &asn_DEF_RANAP_RAB_SetupOrModifiedItem,
|
||||
rab_setup_or_modified_items_ies);
|
||||
if (rc < 0) {
|
||||
RANAP_DEBUG("Rewriting transport layer address failed, could not reencode\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
/* Restore original state of the modified ASN.1 struct so that the asn1c free mechanisms can work properly */
|
||||
if (old_transport_layer_address)
|
||||
rab_setup_or_modified_item->transportLayerAddress = old_transport_layer_address;
|
||||
if (old_iu_transport_association)
|
||||
rab_setup_or_modified_item->iuTransportAssociation = old_iu_transport_association;
|
||||
if (temp_transport_layer_information)
|
||||
ASN_STRUCT_FREE(asn_DEF_RANAP_TransportLayerInformation, temp_transport_layer_information);
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifiedItem, rab_setup_or_modified_items_ies);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! Check if a specific RAB is present in an RAB-Failed-Item-List inside RANAP_RAB_AssignmentResponseIEs.
|
||||
* \ptmap[in] ies user provided memory with RANAP_RAB_AssignmentResponseIEs.
|
||||
* \ptmap[in] rab_id expected rab id to look for.
|
||||
* \returns true when RAB could be identified as failed; false otherwise */
|
||||
bool ranap_rab_ass_resp_ies_check_failure(RANAP_RAB_AssignmentResponseIEs_t *ies, uint8_t rab_id)
|
||||
{
|
||||
RANAP_RAB_FailedItemIEs_t _rab_failed_items_ies;
|
||||
RANAP_RAB_FailedItemIEs_t *rab_failed_items_ies = &_rab_failed_items_ies;
|
||||
int rc;
|
||||
bool result = true;
|
||||
|
||||
/* If we can get a failed item for the specified RAB ID, then we know that the
|
||||
* HNB reported the RAB Assignment as failed */
|
||||
rc = decode_rab_flitms_from_resp_ies(rab_failed_items_ies, ies, rab_id);
|
||||
if (rc < 0)
|
||||
result = false;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_FailedItem, rab_failed_items_ies);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*! Check if a specific RAB is present in an RAB-ReleaseList inside RANAP_RAB_AssignmentRequestIEs.
|
||||
* \ptmap[in] ies user provided memory with RANAP_RAB_AssignmentRequestIEs.
|
||||
* \ptmap[in] rab_id expected rab id to look for.
|
||||
* \returns true when RAB is intended for release; false otherwise */
|
||||
bool ranap_rab_ass_req_ies_check_release(RANAP_RAB_AssignmentRequestIEs_t *ies, uint8_t rab_id)
|
||||
{
|
||||
RANAP_RAB_ReleaseItemIEs_t _rab_release_items_ies;
|
||||
RANAP_RAB_ReleaseItemIEs_t *rab_release_items_ies = &_rab_release_items_ies;
|
||||
int rc;
|
||||
bool result = true;
|
||||
|
||||
/* If we can get a rlease list item for the specified RAB ID, then we know that the
|
||||
* MSC intends to release the specified RAB */
|
||||
rc = decode_rab_relitms_from_req_ies(rab_release_items_ies, ies, rab_id);
|
||||
if (rc < 0)
|
||||
result = false;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_ReleaseItem, rab_release_items_ies);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*! Find out how many RAB items are present in a RAB-SetupOrModifyList inside RANAP_RAB_AssignmentRequestIEs.
|
||||
* \ptmap[in] ies user provided memory with RANAP_RAB_AssignmentRequestIEs.
|
||||
* \returns number of RAB items, -1 on failure. */
|
||||
int ranap_rab_ass_req_ies_get_count(RANAP_RAB_AssignmentRequestIEs_t *ies)
|
||||
{
|
||||
/* Make sure we indeed deal with a setup-or-modify list */
|
||||
if (!(ies->presenceMask & RAB_ASSIGNMENTREQUESTIES_RANAP_RAB_SETUPORMODIFYLIST_PRESENT)) {
|
||||
RANAP_DEBUG
|
||||
("Decoding failed, the RANAP RAB AssignmentRequest did not contain a setup-or-modify list!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ies->raB_SetupOrModifyList.list.count;
|
||||
}
|
||||
46
src/osmo-hnbgw/tdefs.c
Normal file
46
src/osmo-hnbgw/tdefs.c
Normal file
@@ -0,0 +1,46 @@
|
||||
/* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <osmocom/hnbgw/tdefs.h>
|
||||
|
||||
#if ENABLE_PFCP
|
||||
#include <osmocom/pfcp/pfcp_endpoint.h>
|
||||
#endif
|
||||
|
||||
struct osmo_tdef mgw_fsm_T_defs[] = {
|
||||
{.T = -1001, .default_val = 5, .desc = "Timeout for HNB side call-leg (to-HNB) creation" },
|
||||
{.T = -1002, .default_val = 10, .desc = "Timeout for the HNB to respond to RAB Assignment Request" },
|
||||
{.T = -1003, .default_val = 5, .desc = "Timeout for HNB side call-leg (to-HNB) completion" },
|
||||
{.T = -1004, .default_val = 5, .desc = "Timeout for MSC side call-leg (to-MSC) completion" },
|
||||
{.T = -2427, .default_val = 5, .desc = "timeout for MGCP response from MGW" },
|
||||
{ }
|
||||
};
|
||||
|
||||
struct osmo_tdef ps_T_defs[] = {
|
||||
{.T = -1002, .default_val = 10, .desc = "Timeout for the HNB to respond to PS RAB Assignment Request" },
|
||||
{ }
|
||||
};
|
||||
|
||||
struct osmo_tdef_group hnbgw_tdef_group[] = {
|
||||
{.name = "mgw", .tdefs = mgw_fsm_T_defs, .desc = "MGW (Media Gateway) interface" },
|
||||
{.name = "ps", .tdefs = ps_T_defs, .desc = "timers for Packet Switched domain" },
|
||||
#if ENABLE_PFCP
|
||||
{.name = "pfcp", .tdefs = osmo_pfcp_tdefs, .desc = "PFCP timers" },
|
||||
#endif
|
||||
{ }
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
SUBDIRS = \
|
||||
ranap_rab_ass \
|
||||
$(NULL)
|
||||
|
||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
|
||||
|
||||
43
tests/ranap_rab_ass/Makefile.am
Normal file
43
tests/ranap_rab_ass/Makefile.am
Normal file
@@ -0,0 +1,43 @@
|
||||
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)
|
||||
|
||||
EXTRA_DIST = \
|
||||
ranap_rab_ass_test.ok \
|
||||
$(NULL)
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
ranap_rab_ass_test \
|
||||
$(NULL)
|
||||
|
||||
ranap_rab_ass_test_SOURCES = \
|
||||
ranap_rab_ass_test.c \
|
||||
$(NULL)
|
||||
|
||||
ranap_rab_ass_test_LDADD = \
|
||||
$(LIBASN1C_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMORANAP_LIBS) \
|
||||
$(LIBOSMOSIGTRAN_LIBS) \
|
||||
$(LIBOSMOMGCPCLIENT_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(top_builddir)/src/osmo-hnbgw/ranap_rab_ass.o \
|
||||
$(NULL)
|
||||
|
||||
.PHONY: update_exp
|
||||
update_exp:
|
||||
$(builddir)/ranap_rab_ass_test >$(srcdir)/ranap_rab_ass_test.ok
|
||||
427
tests/ranap_rab_ass/ranap_rab_ass_test.c
Normal file
427
tests/ranap_rab_ass/ranap_rab_ass_test.c
Normal file
@@ -0,0 +1,427 @@
|
||||
/* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Philipp Maier
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#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>
|
||||
|
||||
static void *tall_hnb_ctx;
|
||||
static void *msgb_ctx;
|
||||
extern void *talloc_asn1_ctx;
|
||||
|
||||
void test_ranap_rab_ass_req_decode_encode(void)
|
||||
{
|
||||
int rc;
|
||||
ranap_message message;
|
||||
uint8_t testvec[] = {
|
||||
0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x01, 0x00,
|
||||
0x36, 0x40, 0x52, 0x00, 0x00, 0x01, 0x00, 0x35,
|
||||
0x00, 0x48, 0x78, 0x22, 0xcd, 0x80, 0x10, 0x2f,
|
||||
0xa7, 0x20, 0x1a, 0x2c, 0x00, 0x00, 0xf4, 0x4c,
|
||||
0x08, 0x0a, 0x02, 0x80, 0x00, 0x51, 0x40, 0x00,
|
||||
0x27, 0x20, 0x28, 0x14, 0x00, 0x67, 0x40, 0x00,
|
||||
0x00, 0x22, 0x28, 0x14, 0x00, 0x3c, 0x40, 0x00,
|
||||
0x00, 0x00, 0x50, 0x3d, 0x02, 0x00, 0x02, 0x27,
|
||||
0xc0, 0x35, 0x00, 0x01, 0x0a, 0x09, 0x01, 0xa2,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1f, 0x76,
|
||||
0x00, 0x00, 0x40, 0x01, 0x00
|
||||
};
|
||||
struct msgb *encoded;
|
||||
|
||||
rc = ranap_ran_rx_co_decode(talloc_asn1_ctx, &message, testvec, sizeof(testvec));
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
encoded = ranap_rab_ass_req_encode(&message.msg.raB_AssignmentRequestIEs);
|
||||
OSMO_ASSERT(encoded != NULL);
|
||||
|
||||
printf("INPUT: %s\n", osmo_hexdump_nospc(testvec, sizeof(testvec)));
|
||||
printf("RESULT: %s\n", osmo_hexdump_nospc(encoded->data, encoded->len));
|
||||
OSMO_ASSERT(encoded->len == sizeof(testvec));
|
||||
OSMO_ASSERT(memcmp(testvec, encoded->data, sizeof(testvec)) == 0);
|
||||
|
||||
ranap_ran_rx_co_free(&message);
|
||||
msgb_free(encoded);
|
||||
}
|
||||
|
||||
void test_ranap_rab_ass_resp_decode_encode(void)
|
||||
{
|
||||
int rc;
|
||||
ranap_message message;
|
||||
uint8_t testvec[] = {
|
||||
0x60, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x01, 0x00,
|
||||
0x34, 0x40, 0x23, 0x00, 0x00, 0x01, 0x00, 0x33,
|
||||
0x40, 0x1c, 0x60, 0x3a, 0x7c, 0x35, 0x00, 0x01,
|
||||
0x0a, 0x09, 0x01, 0xa4, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x40, 0x04, 0x0a, 0x00, 0x00
|
||||
};
|
||||
uint8_t encoded[sizeof(testvec)];
|
||||
|
||||
rc = ranap_cn_rx_co_decode(talloc_asn1_ctx, &message, testvec, sizeof(testvec));
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
rc = ranap_rab_ass_resp_encode(encoded, sizeof(encoded), &message.msg.raB_AssignmentResponseIEs);
|
||||
printf("ranap_rab_ass_resp_encode rc=%d\n", rc);
|
||||
|
||||
printf("INPUT: %s\n", osmo_hexdump_nospc(testvec, sizeof(testvec)));
|
||||
printf("RESULT: %s\n", osmo_hexdump_nospc(encoded, sizeof(encoded)));
|
||||
OSMO_ASSERT(memcmp(testvec, encoded, sizeof(testvec)) == 0);
|
||||
|
||||
ranap_cn_rx_co_free(&message);
|
||||
}
|
||||
|
||||
void test_ranap_rab_ass_req_ies_extract_inet_addr(void)
|
||||
{
|
||||
int rc;
|
||||
struct osmo_sockaddr addr;
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
uint8_t rab_id;
|
||||
ranap_message message;
|
||||
uint8_t testvec[] = {
|
||||
0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x01, 0x00,
|
||||
0x36, 0x40, 0x52, 0x00, 0x00, 0x01, 0x00, 0x35,
|
||||
0x00, 0x48, 0x78, 0x22, 0xcd, 0x80, 0x10, 0x2f,
|
||||
0xa7, 0x20, 0x1a, 0x2c, 0x00, 0x00, 0xf4, 0x4c,
|
||||
0x08, 0x0a, 0x02, 0x80, 0x00, 0x51, 0x40, 0x00,
|
||||
0x27, 0x20, 0x28, 0x14, 0x00, 0x67, 0x40, 0x00,
|
||||
0x00, 0x22, 0x28, 0x14, 0x00, 0x3c, 0x40, 0x00,
|
||||
0x00, 0x00, 0x50, 0x3d, 0x02, 0x00, 0x02, 0x27,
|
||||
0xc0, 0x35, 0x00, 0x01, 0x0a, 0x09, 0x01, 0xa2,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1f, 0x76,
|
||||
0x00, 0x00, 0x40, 0x01, 0x00, 0x00
|
||||
};
|
||||
|
||||
rc = ranap_ran_rx_co_decode(talloc_asn1_ctx, &message, testvec, sizeof(testvec));
|
||||
OSMO_ASSERT(rc == 0);
|
||||
rc = ranap_rab_ass_req_ies_extract_inet_addr(&addr, &rab_id, &message.msg.raB_AssignmentRequestIEs, 0);
|
||||
osmo_sockaddr_str_from_sockaddr(&addr_str, &addr.u.sas);
|
||||
printf("ranap_rab_ass_req_extract_inet_addr rc=%d\n", rc);
|
||||
printf("RESULT: addr=%s, port=%u, rab-id=%02x\n", addr_str.ip, addr_str.port, rab_id);
|
||||
ranap_ran_rx_co_free(&message);
|
||||
}
|
||||
|
||||
void test_ranap_rab_ass_resp_ies_extract_inet_addr(void)
|
||||
{
|
||||
int rc;
|
||||
struct osmo_sockaddr addr;
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
ranap_message message;
|
||||
uint8_t testvec[] = {
|
||||
0x60, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x01, 0x00,
|
||||
0x34, 0x40, 0x23, 0x00, 0x00, 0x01, 0x00, 0x33,
|
||||
0x40, 0x1c, 0x60, 0x3a, 0x7c, 0x35, 0x00, 0x01,
|
||||
0x0a, 0x09, 0x01, 0xa4, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x40, 0x04, 0x0a, 0x00, 0x00
|
||||
};
|
||||
|
||||
rc = ranap_cn_rx_co_decode(talloc_asn1_ctx, &message, testvec, sizeof(testvec));
|
||||
OSMO_ASSERT(rc == 0);
|
||||
rc = ranap_rab_ass_resp_ies_extract_inet_addr(&addr, &message.msg.raB_AssignmentResponseIEs, 7);
|
||||
osmo_sockaddr_str_from_sockaddr(&addr_str, &addr.u.sas);
|
||||
printf("ranap_rab_ass_resp_extract_inet_addr rc=%d\n", rc);
|
||||
printf("RESULT: addr=%s, port=%u\n", addr_str.ip, addr_str.port);
|
||||
ranap_cn_rx_co_free(&message);
|
||||
}
|
||||
|
||||
void test_ranap_rab_ass_req_ies_replace_inet_addr(void)
|
||||
{
|
||||
int rc;
|
||||
struct osmo_sockaddr addr;
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
ranap_message message;
|
||||
struct msgb *encoded;
|
||||
uint8_t rab_id;
|
||||
uint8_t testvec_in[] = {
|
||||
0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x01, 0x00,
|
||||
0x36, 0x40, 0x52, 0x00, 0x00, 0x01, 0x00, 0x35,
|
||||
0x00, 0x48, 0x78, 0x4e, 0xcd, 0x80, 0x10, 0x2f,
|
||||
0xa7, 0x20, 0x1a, 0x2c, 0x00, 0x00, 0xf4, 0x4c,
|
||||
0x08, 0x0a, 0x02, 0x80, 0x00, 0x51, 0x40, 0x00,
|
||||
0x27, 0x20, 0x28, 0x14, 0x00, 0x67, 0x40, 0x00,
|
||||
0x00, 0x22, 0x28, 0x14, 0x00, 0x3c, 0x40, 0x00,
|
||||
0x00, 0x00, 0x50, 0x3d, 0x02, 0x00, 0x02, 0x27,
|
||||
0xc0, 0x35, 0x00, 0x01, 0x0a, 0x09, 0x01, 0xa2,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1f, 0xba,
|
||||
0x00, 0x00, 0x40, 0x01, 0x00
|
||||
};
|
||||
uint8_t testvec_expected_out[] = {
|
||||
0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x01, 0x00,
|
||||
0x36, 0x40, 0x52, 0x00, 0x00, 0x01, 0x00, 0x35,
|
||||
0x00, 0x48, 0x78, 0x4e, 0xcd, 0x80, 0x10, 0x2f,
|
||||
0xa7, 0x20, 0x1a, 0x2c, 0x00, 0x00, 0xf4, 0x4c,
|
||||
0x08, 0x0a, 0x02, 0x80, 0x00, 0x51, 0x40, 0x00,
|
||||
0x27, 0x20, 0x28, 0x14, 0x00, 0x67, 0x40, 0x00,
|
||||
0x00, 0x22, 0x28, 0x14, 0x00, 0x3c, 0x40, 0x00,
|
||||
0x00, 0x00, 0x50, 0x3d, 0x02, 0x00, 0x02, 0x27,
|
||||
0xc0, 0x35, 0x00, 0x01, 0x01, 0x02, 0x03, 0x04,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0xd2,
|
||||
0x00, 0x00, 0x40, 0x01, 0x00
|
||||
};
|
||||
|
||||
rc = ranap_ran_rx_co_decode(talloc_asn1_ctx, &message, testvec_in, sizeof(testvec_in));
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
rc = ranap_rab_ass_req_ies_extract_inet_addr(&addr, &rab_id, &message.msg.raB_AssignmentRequestIEs, 0);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
osmo_sockaddr_str_from_sockaddr(&addr_str, &addr.u.sas);
|
||||
printf("before: addr=%s, port=%u, rab_id=%u\n", addr_str.ip, addr_str.port, rab_id);
|
||||
|
||||
memset(&addr_str, 0, sizeof(addr_str));
|
||||
addr_str.af = AF_INET;
|
||||
addr_str.port = 1234;
|
||||
osmo_strlcpy(addr_str.ip, "1.2.3.4", sizeof(addr_str.ip));
|
||||
osmo_sockaddr_str_to_sockaddr(&addr_str, &addr.u.sas);
|
||||
|
||||
rc = ranap_rab_ass_req_ies_replace_inet_addr(&message.msg.raB_AssignmentRequestIEs, &addr, rab_id);
|
||||
printf("ranap_rab_ass_req_replace_inet_addr rc=%d\n", rc);
|
||||
|
||||
rc = ranap_rab_ass_req_ies_extract_inet_addr(&addr, &rab_id, &message.msg.raB_AssignmentRequestIEs, 0);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
osmo_sockaddr_str_from_sockaddr(&addr_str, &addr.u.sas);
|
||||
printf("after: addr=%s, port=%u, rab_id=%u\n", addr_str.ip, addr_str.port, rab_id);
|
||||
|
||||
encoded = ranap_rab_ass_req_encode(&message.msg.raB_AssignmentRequestIEs);
|
||||
OSMO_ASSERT(encoded != NULL);
|
||||
OSMO_ASSERT(encoded->len == sizeof(testvec_expected_out));
|
||||
OSMO_ASSERT(memcmp(encoded->data, testvec_expected_out, encoded->len) == 0);
|
||||
|
||||
ranap_ran_rx_co_free(&message);
|
||||
msgb_free(encoded);
|
||||
}
|
||||
|
||||
void test_ranap_rab_ass_resp_ies_replace_inet_addr(void)
|
||||
{
|
||||
int rc;
|
||||
struct osmo_sockaddr addr;
|
||||
struct osmo_sockaddr_str addr_str;
|
||||
ranap_message message;
|
||||
uint8_t testvec_in[] = {
|
||||
0x60, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x01, 0x00,
|
||||
0x34, 0x40, 0x23, 0x00, 0x00, 0x01, 0x00, 0x33,
|
||||
0x40, 0x1c, 0x60, 0x32, 0x7c, 0x35, 0x00, 0x01,
|
||||
0x0a, 0x09, 0x01, 0xa4, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x40, 0x04, 0x0a, 0x00, 0x00
|
||||
};
|
||||
uint8_t testvec_expected_out[] = {
|
||||
0x60, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x01, 0x00,
|
||||
0x34, 0x40, 0x23, 0x00, 0x00, 0x01, 0x00, 0x33,
|
||||
0x40, 0x1c, 0x60, 0x32, 0x7c, 0x35, 0x00, 0x01,
|
||||
0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x40, 0x04, 0xd2, 0x00, 0x00
|
||||
};
|
||||
|
||||
rc = ranap_cn_rx_co_decode(talloc_asn1_ctx, &message, testvec_in, sizeof(testvec_in));
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
rc = ranap_rab_ass_resp_ies_extract_inet_addr(&addr, &message.msg.raB_AssignmentResponseIEs, 6);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
osmo_sockaddr_str_from_sockaddr(&addr_str, &addr.u.sas);
|
||||
printf("before: addr=%s, port=%u\n", addr_str.ip, addr_str.port);
|
||||
|
||||
memset(&addr_str, 0, sizeof(addr_str));
|
||||
addr_str.af = AF_INET;
|
||||
addr_str.port = 1234;
|
||||
osmo_strlcpy(addr_str.ip, "1.2.3.4", sizeof(addr_str.ip));
|
||||
osmo_sockaddr_str_to_sockaddr(&addr_str, &addr.u.sas);
|
||||
|
||||
rc = ranap_rab_ass_resp_ies_replace_inet_addr(&message.msg.raB_AssignmentResponseIEs, &addr, 6);
|
||||
printf("ranap_rab_ass_resp_replace_inet_addr rc=%d\n", rc);
|
||||
|
||||
rc = ranap_rab_ass_resp_ies_extract_inet_addr(&addr, &message.msg.raB_AssignmentResponseIEs, 6);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
osmo_sockaddr_str_from_sockaddr(&addr_str, &addr.u.sas);
|
||||
printf("after: addr=%s, port=%u\n", addr_str.ip, addr_str.port);
|
||||
|
||||
rc = ranap_rab_ass_resp_encode(testvec_in, sizeof(testvec_in), &message.msg.raB_AssignmentResponseIEs);
|
||||
OSMO_ASSERT(rc == sizeof(testvec_in));
|
||||
OSMO_ASSERT(memcmp(testvec_in, testvec_expected_out, sizeof(testvec_in)) == 0);
|
||||
|
||||
ranap_cn_rx_co_free(&message);
|
||||
}
|
||||
|
||||
void test_ranap_rab_ass_resp_ies_check_failure(void)
|
||||
{
|
||||
int rc;
|
||||
ranap_message message;
|
||||
bool rab_failed_at_hnb;
|
||||
uint8_t testvec[] = {
|
||||
0x60, 0x00, 0x00, 0x11, 0x00, 0x00, 0x01, 0x00,
|
||||
0x23, 0x40, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x22,
|
||||
0x40, 0x03, 0x05, 0xd0, 0x00
|
||||
};
|
||||
|
||||
rc = ranap_cn_rx_co_decode(talloc_asn1_ctx, &message, testvec, sizeof(testvec));
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
rab_failed_at_hnb =
|
||||
ranap_rab_ass_resp_ies_check_failure(&message.msg.raB_AssignmentResponseIEs, 23);
|
||||
printf("ranap_rab_ass_resp_ies_check_failure rab_failed_at_hnb=%u (RAB ID 23)\n", rab_failed_at_hnb);
|
||||
|
||||
rab_failed_at_hnb =
|
||||
ranap_rab_ass_resp_ies_check_failure(&message.msg.raB_AssignmentResponseIEs, 44);
|
||||
printf("ranap_rab_ass_resp_ies_check_failure rab_failed_at_hnb=%u (RAB ID 44, which is not in the message)\n",
|
||||
rab_failed_at_hnb);
|
||||
|
||||
ranap_cn_rx_co_free(&message);
|
||||
}
|
||||
|
||||
void test_ranap_rab_ass_req_ies_check_release(void)
|
||||
{
|
||||
int rc;
|
||||
ranap_message message;
|
||||
bool rab_release_req;
|
||||
uint8_t testvec[] = {
|
||||
0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x01, 0x00,
|
||||
0x29, 0x40, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x28,
|
||||
0x40, 0x03, 0x05, 0xd0, 0x00
|
||||
};
|
||||
|
||||
rc = ranap_ran_rx_co_decode(talloc_asn1_ctx, &message, testvec, sizeof(testvec));
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
rab_release_req =
|
||||
ranap_rab_ass_req_ies_check_release(&message.msg.raB_AssignmentRequestIEs, 23);
|
||||
printf("ranap_rab_ass_req_ies_check_release rab_release_req=%u (RAB ID 23)\n", rab_release_req);
|
||||
|
||||
rab_release_req =
|
||||
ranap_rab_ass_req_ies_check_release(&message.msg.raB_AssignmentRequestIEs, 44);
|
||||
printf("ranap_rab_ass_req_ies_check_release rab_release_req=%u (RAB ID 44, which is not in the message)\n", rab_release_req);
|
||||
|
||||
ranap_ran_rx_co_free(&message);
|
||||
}
|
||||
|
||||
void test_ranap_rab_ass_req_ies_get_count(void)
|
||||
{
|
||||
int rc;
|
||||
ranap_message message;
|
||||
uint8_t testvec[] = {
|
||||
0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x01, 0x00,
|
||||
0x36, 0x40, 0x52, 0x00, 0x00, 0x01, 0x00, 0x35,
|
||||
0x00, 0x48, 0x78, 0x22, 0xcd, 0x80, 0x10, 0x2f,
|
||||
0xa7, 0x20, 0x1a, 0x2c, 0x00, 0x00, 0xf4, 0x4c,
|
||||
0x08, 0x0a, 0x02, 0x80, 0x00, 0x51, 0x40, 0x00,
|
||||
0x27, 0x20, 0x28, 0x14, 0x00, 0x67, 0x40, 0x00,
|
||||
0x00, 0x22, 0x28, 0x14, 0x00, 0x3c, 0x40, 0x00,
|
||||
0x00, 0x00, 0x50, 0x3d, 0x02, 0x00, 0x02, 0x27,
|
||||
0xc0, 0x35, 0x00, 0x01, 0x0a, 0x09, 0x01, 0xa2,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x1f, 0x76,
|
||||
0x00, 0x00, 0x40, 0x01, 0x00, 0x00
|
||||
};
|
||||
|
||||
rc = ranap_ran_rx_co_decode(talloc_asn1_ctx, &message, testvec, sizeof(testvec));
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
rc = ranap_rab_ass_req_ies_get_count(&message.msg.raB_AssignmentRequestIEs);
|
||||
printf("ranap_rab_ass_req_ies_get_count rc=%d\n", rc);
|
||||
ranap_ran_rx_co_free(&message);
|
||||
}
|
||||
|
||||
static const struct log_info_cat log_cat[] = {
|
||||
[DRANAP] = {
|
||||
.name = "RANAP", .loglevel = LOGL_DEBUG, .enabled = 1,
|
||||
.color = "",
|
||||
.description = "RAN Application Part",
|
||||
},
|
||||
};
|
||||
|
||||
static const struct log_info test_log_info = {
|
||||
.cat = log_cat,
|
||||
.num_cat = ARRAY_SIZE(log_cat),
|
||||
};
|
||||
|
||||
int test_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
tall_hnb_ctx = talloc_named_const(NULL, 0, "hnb_context");
|
||||
msgb_ctx = msgb_talloc_ctx_init(NULL, 0);
|
||||
talloc_asn1_ctx = talloc_named_const(NULL, 0, "asn1_context");
|
||||
|
||||
rc = osmo_init_logging2(tall_hnb_ctx, &test_log_info);
|
||||
if (rc < 0)
|
||||
exit(1);
|
||||
|
||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_category(osmo_stderr_target, 0);
|
||||
log_set_print_category_hex(osmo_stderr_target, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void test_cleanup(void)
|
||||
{
|
||||
if (talloc_total_blocks(msgb_ctx) != 1 || talloc_total_size(msgb_ctx) != 0)
|
||||
talloc_report_full(msgb_ctx, stderr);
|
||||
|
||||
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
|
||||
OSMO_ASSERT(talloc_total_size(msgb_ctx) == 0);
|
||||
talloc_free(msgb_ctx);
|
||||
|
||||
if (talloc_total_blocks(talloc_asn1_ctx) != 1 || talloc_total_size(talloc_asn1_ctx) != 0)
|
||||
talloc_report_full(talloc_asn1_ctx, stderr);
|
||||
|
||||
OSMO_ASSERT(talloc_total_blocks(talloc_asn1_ctx) == 1);
|
||||
OSMO_ASSERT(talloc_total_size(talloc_asn1_ctx) == 0);
|
||||
talloc_free(talloc_asn1_ctx);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
test_init();
|
||||
|
||||
test_ranap_rab_ass_req_decode_encode();
|
||||
test_ranap_rab_ass_resp_decode_encode();
|
||||
|
||||
test_ranap_rab_ass_req_ies_extract_inet_addr();
|
||||
test_ranap_rab_ass_resp_ies_extract_inet_addr();
|
||||
test_ranap_rab_ass_req_ies_replace_inet_addr();
|
||||
test_ranap_rab_ass_resp_ies_replace_inet_addr();
|
||||
test_ranap_rab_ass_resp_ies_check_failure();
|
||||
test_ranap_rab_ass_req_ies_check_release();
|
||||
test_ranap_rab_ass_req_ies_get_count();
|
||||
|
||||
test_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Stub */
|
||||
const char *hnb_context_name(struct hnb_context *ctx)
|
||||
{
|
||||
return "TEST";
|
||||
}
|
||||
20
tests/ranap_rab_ass/ranap_rab_ass_test.ok
Normal file
20
tests/ranap_rab_ass/ranap_rab_ass_test.ok
Normal file
@@ -0,0 +1,20 @@
|
||||
INPUT: 0000005900000100364052000001003500487822cd80102fa7201a2c0000f44c080a028000514000272028140067400000222814003c40000000503d02000227c03500010a0901a200000000000000000000000000401f760000400100
|
||||
RESULT: 0000005900000100364052000001003500487822cd80102fa7201a2c0000f44c080a028000514000272028140067400000222814003c40000000503d02000227c03500010a0901a200000000000000000000000000401f760000400100
|
||||
ranap_rab_ass_resp_encode rc=46
|
||||
INPUT: 6000002a000001003440230000010033401c603a7c3500010a0901a40000000000000000000000000040040a0000
|
||||
RESULT: 6000002a000001003440230000010033401c603a7c3500010a0901a40000000000000000000000000040040a0000
|
||||
ranap_rab_ass_req_extract_inet_addr rc=0
|
||||
RESULT: addr=10.9.1.162, port=8054, rab-id=11
|
||||
ranap_rab_ass_resp_extract_inet_addr rc=0
|
||||
RESULT: addr=10.9.1.164, port=1034
|
||||
before: addr=10.9.1.162, port=8122, rab_id=39
|
||||
ranap_rab_ass_req_replace_inet_addr rc=0
|
||||
after: addr=1.2.3.4, port=1234, rab_id=39
|
||||
before: addr=10.9.1.164, port=1034
|
||||
ranap_rab_ass_resp_replace_inet_addr rc=0
|
||||
after: addr=1.2.3.4, port=1234
|
||||
ranap_rab_ass_resp_ies_check_failure rab_failed_at_hnb=1 (RAB ID 23)
|
||||
ranap_rab_ass_resp_ies_check_failure rab_failed_at_hnb=0 (RAB ID 44, which is not in the message)
|
||||
ranap_rab_ass_req_ies_check_release rab_release_req=1 (RAB ID 23)
|
||||
ranap_rab_ass_req_ies_check_release rab_release_req=0 (RAB ID 44, which is not in the message)
|
||||
ranap_rab_ass_req_ies_get_count rc=1
|
||||
@@ -1,8 +1,8 @@
|
||||
AT_INIT
|
||||
AT_BANNER([Regression tests.])
|
||||
|
||||
#AT_SETUP([foobar])
|
||||
#AT_KEYWORDS([foobar])
|
||||
#cat $abs_srcdir/foobar/foobar_test.ok > expout
|
||||
#AT_CHECK([$abs_top_builddir/tests/foobar/foobar_test], [], [expout], [ignore])
|
||||
#AT_CLEANUP
|
||||
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
|
||||
Reference in New Issue
Block a user