Compare commits
58 Commits
Author | SHA1 | Date |
---|---|---|
Oliver Smith | 1db9b60023 | |
Oliver Smith | d15ef114cc | |
Max | 460b553971 | |
Pau Espin | a9b33a40af | |
Pau Espin | b04885005b | |
Pau Espin | ab8db8fabd | |
Pau Espin | d3cd9e7afa | |
Pau Espin | 8ff216bf02 | |
Pau Espin | 9f2a584fb6 | |
Andreas Eversberg | 4bc90a6be7 | |
Harald Welte | fed4bf196a | |
Vadim Yanitskiy | b303e296b7 | |
Vadim Yanitskiy | 11b379d95a | |
Vadim Yanitskiy | be5af799c3 | |
Pau Espin | 4f7362272f | |
Pau Espin | 69682c8deb | |
Oliver Smith | 4d94531f6f | |
Vadim Yanitskiy | b4a5d3d9a8 | |
Oliver Smith | eee4534364 | |
Vadim Yanitskiy | a2aa6b19b2 | |
Vadim Yanitskiy | 54899f76b0 | |
Vadim Yanitskiy | 9b356e1c5c | |
arehbein | ad5981352b | |
Pau Espin | 69f91b1773 | |
Oliver Smith | 1bad165c69 | |
Max | 9c2c2654ba | |
Vadim Yanitskiy | f74f944784 | |
Pau Espin | 8ffe838a81 | |
Michael Iedema | 52636f32c1 | |
Pau Espin | 19a475bcf4 | |
Harald Welte | 71f758939a | |
Pau Espin | f320ac5841 | |
Pau Espin | 780def0114 | |
Pau Espin | 0843c8ae6e | |
Pau Espin | fc660017e8 | |
Pau Espin | 3dffff3147 | |
Pau Espin | 8875abd3f9 | |
Pau Espin | 70b65c55f6 | |
Pau Espin | 14cceb5dfb | |
Pau Espin | e4f127f1a3 | |
Pau Espin | 387b892b1d | |
Pau Espin | 272f296cd3 | |
Pau Espin | 5869201039 | |
Pau Espin | 64d8fd6298 | |
Pau Espin | 3363db5c90 | |
Pau Espin | 381c4cccc5 | |
Pau Espin | 7550ba3d05 | |
Pau Espin | ecc76ef69a | |
Pau Espin | a7dc04ef26 | |
Pau Espin | b3af382157 | |
Pau Espin | cd1baf0649 | |
Pau Espin | d8da64bb46 | |
Pau Espin | 5689081325 | |
Pau Espin | 807e52a2da | |
Pau Espin | fd440b7a67 | |
Pau Espin | 65489ed7c6 | |
Pau Espin | 9828dbed18 | |
Pau Espin | 4310b0d57a |
|
@ -19,7 +19,6 @@ SUBDIRS = \
|
|||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
contrib/osmo-hnodeb.spec.in \
|
||||
debian \
|
||||
git-version-gen \
|
||||
osmoappdesc.py \
|
||||
|
|
|
@ -22,9 +22,9 @@ GIT Repository
|
|||
|
||||
You can clone from the official osmo-hnodeb.git repository using
|
||||
|
||||
git clone git://git.osmocom.org/osmo-hnodeb.git
|
||||
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-hnodeb
|
||||
|
||||
There is a cgit interface at https://git.osmocom.org/osmo-hnodeb/
|
||||
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-hnodeb>
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
@ -40,7 +40,7 @@ as well as the [VTY Reference Manual](https://ftp.osmocom.org/docs/latest/osmohn
|
|||
Mailing List
|
||||
------------
|
||||
|
||||
Discussions related to osmo-bts are happening on the
|
||||
Discussions related to osmo-hnodeb are happening on the
|
||||
openbsc@lists.osmocom.org mailing list, please see
|
||||
https://lists.osmocom.org/mailman/listinfo/openbsc for subscription
|
||||
options and the list archive.
|
||||
|
@ -60,5 +60,5 @@ contributions. Please see
|
|||
https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit for
|
||||
more details
|
||||
|
||||
The current patch queue for osmo-bts can be seen at
|
||||
The current patch queue for osmo-hnodeb can be seen at
|
||||
https://gerrit.osmocom.org/#/q/project:osmo-hnodeb+status:open
|
||||
|
|
|
@ -7,3 +7,5 @@
|
|||
# If any interfaces have been added since the last public release: c:r:a + 1.
|
||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
|
||||
#library what description / commit summary line
|
||||
libosmocore depend >= 1.9.0 we need osmo_sockaddr_from_str_and_uint()
|
||||
libosmo-netif depend > 1.4.0 osmo_prim_srv{_link}_set_name()
|
38
configure.ac
38
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,17 @@ 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(LIBOSMOABIS, libosmoabis >= 1.2.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 >= 0.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 0.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 0.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 1.5.0)
|
||||
|
||||
|
||||
dnl checks for header files
|
||||
|
@ -114,13 +108,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,
|
||||
|
@ -155,7 +142,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])
|
||||
|
@ -239,5 +226,4 @@ AC_OUTPUT(
|
|||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
contrib/osmo-hnodeb.spec
|
||||
Makefile)
|
||||
|
|
|
@ -52,7 +52,7 @@ set -x
|
|||
|
||||
cd "$base"
|
||||
autoreconf --install --force
|
||||
./configure --enable-sanitize --enable-external-tests $CONFIG
|
||||
./configure --enable-sanitize --enable-external-tests --enable-werror $CONFIG
|
||||
$MAKE $PARALLEL_MAKE
|
||||
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
#
|
||||
# spec file for package osmo-hnodeb
|
||||
#
|
||||
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
# upon. The license for this file, and modifications and additions to the
|
||||
# file, is the same license as for the pristine package itself (unless the
|
||||
# license for the pristine package is not an Open Source License, in which
|
||||
# case the license is the MIT License). An "Open Source License" is a
|
||||
# license that conforms to the Open Source Definition (Version 1.9)
|
||||
# published by the Open Source Initiative.
|
||||
|
||||
## Disable LTO for now since it breaks compilation of the tests
|
||||
## https://osmocom.org/issues/4113
|
||||
%define _lto_cflags %{nil}
|
||||
|
||||
Name: osmo-hnodeb
|
||||
Version: @VERSION@
|
||||
Release: 0
|
||||
Summary: Osmohnodeb: Osmocom's Base Station Controller for 2G CS mobile networks
|
||||
License: AGPL-3.0-or-later AND GPL-2.0-or-later
|
||||
Group: Hardware/Mobile
|
||||
URL: https://osmocom.org/projects/osmohnodeb
|
||||
Source: %{name}-%{version}.tar.xz
|
||||
BuildRequires: autoconf-archive
|
||||
BuildRequires: automake >= 1.9
|
||||
BuildRequires: libtool >= 2
|
||||
BuildRequires: lksctp-tools-devel
|
||||
BuildRequires: pkgconfig >= 0.20
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: pkgconfig(libcrypto) >= 0.9.5
|
||||
BuildRequires: pkgconfig(libosmo-netif) >= 1.1.0
|
||||
BuildRequires: pkgconfig(libosmo-sigtran) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmoabis) >= 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) >= 0.8.0
|
||||
BuildRequires: pkgconfig(libosmo-ranap) >= 0.8.0
|
||||
BuildRequires: pkgconfig(libosmo-rua) >= 0.8.0
|
||||
BuildRequires: pkgconfig(talloc)
|
||||
BuildRequires: pkgconfig(libasn1c) >= 0.9.30
|
||||
%{?systemd_requires}
|
||||
|
||||
%description
|
||||
OsmoHNodeB: Osmocom's Home NodeB for 3G mobile networks.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
echo "%{version}" >.tarball-version
|
||||
autoreconf -fi
|
||||
%configure \
|
||||
--docdir=%{_docdir}/%{name} \
|
||||
--with-systemdsystemunitdir=%{_unitdir}
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
%make_install
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%preun
|
||||
%service_del_preun %{name}.service
|
||||
|
||||
%postun
|
||||
%service_del_postun %{name}.service
|
||||
|
||||
%pre
|
||||
%service_add_pre %{name}.service
|
||||
|
||||
%post
|
||||
%service_add_post %{name}.service
|
||||
%endif
|
||||
|
||||
%check
|
||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
|
||||
%files
|
||||
%license COPYING
|
||||
%doc AUTHORS README.md
|
||||
%{_bindir}/osmo-hnodeb
|
||||
%dir %{_docdir}/%{name}/examples
|
||||
%dir %{_docdir}/%{name}/examples/osmo-hnodeb
|
||||
%{_docdir}/%{name}/examples/osmo-hnodeb/osmo-hnodeb.cfg
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-hnodeb.cfg
|
||||
%{_unitdir}/%{name}.service
|
||||
|
||||
%changelog
|
|
@ -1,9 +1,15 @@
|
|||
[Unit]
|
||||
Description=Osmocom Home NodeB (hNodeB)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
StateDirectory=osmocom
|
||||
WorkingDirectory=%S/osmocom
|
||||
Restart=always
|
||||
User=osmocom
|
||||
Group=osmocom
|
||||
ExecStart=/usr/bin/osmo-hnodeb -c /etc/osmocom/osmo-hnodeb.cfg
|
||||
RestartSec=2
|
||||
|
||||
|
|
|
@ -1,3 +1,76 @@
|
|||
osmo-hnodeb (0.1.2) unstable; urgency=medium
|
||||
|
||||
[ arehbein ]
|
||||
* Transition to use of 'telnet_init_default'
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* build: remove $(COVERAGE_LDFLAGS) from osmo_hnodeb_LDADD
|
||||
* tests: make 'vty-test' target depend on the respective binary
|
||||
* tests: do not depend on undefined $(BUILT_SOURCES)
|
||||
* copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
|
||||
|
||||
[ Oliver Smith ]
|
||||
* debian: set compat level to 10
|
||||
* systemd: depend on networking-online.target
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* rua: Fix null ptr access in hnb->llsk.srv
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 17:22:10 +0200
|
||||
|
||||
osmo-hnodeb (0.1.1) unstable; urgency=medium
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* configure.ac: do not require unused dlopen
|
||||
|
||||
[ Max ]
|
||||
* Set working directory in systemd service file
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib/jenkins.sh: use --enable-werror
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 18:03:48 +0100
|
||||
|
||||
osmo-hnodeb (0.1.0) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* Move Iuh code to its own module
|
||||
* Introduce SCTP log category
|
||||
* iuh: Improve logging of SCTP notifications
|
||||
* Fix typos and non-related test content
|
||||
* Implement initial shutdown + reconnect FSM
|
||||
* hnbap: Improve rx path and implement HnbRegisterReject
|
||||
* Drop NAS and RANAP code and VTY commands
|
||||
* Initial implementation of LowerLayer UD Socket
|
||||
* First implementation of the LLSK audio SAPI
|
||||
* First implementation of the LLSK gtp SAPI
|
||||
* Fix several Coverity found issues
|
||||
* Update AUDIO and GTP SAPs to support multiple conns per UE
|
||||
* rtp: Use dynamic Payload Type 96
|
||||
* llsk_gtp: Early return if conn alloc fails
|
||||
* llsk_audio: Fix false positive about uninitialized var access
|
||||
* contrib/osmo-hnodeb.spec.in: Fix missing dep libosmotrau
|
||||
* gtp: Fix possible null pointer dereference
|
||||
* Add example default config file
|
||||
* Add initial support for IuUP RTP based Transport Layer
|
||||
* Get rid of libgtp for GTP-U
|
||||
* gtp: Fix uninitialized var if no extension flags set in gtp header
|
||||
* Do not turn some compiler warnings into errors by default
|
||||
* rtp: Update code to libosmogsm IuUP API changes
|
||||
* llsk: Fix typo in log message
|
||||
* hnb: move llsk fields inside substruct
|
||||
* llsk: Validate received SAPI versions at startup
|
||||
* audio: Introduce SAPI v1 with unordered RFCI support
|
||||
* llsk: Fix previous audio sapi version passed instead of current
|
||||
|
||||
[ Harald Welte ]
|
||||
* update git URLs (git -> https; gitea)
|
||||
|
||||
[ Michael Iedema ]
|
||||
* typo: SAC needs 16bit range, not 8bit as copied from RAC entry
|
||||
|
||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 29 Jun 2022 12:37:29 +0200
|
||||
|
||||
osmo-hnodeb (0.0.1) unstable; urgency=medium
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
|
|
|
@ -1 +1 @@
|
|||
9
|
||||
10
|
||||
|
|
|
@ -2,7 +2,7 @@ Source: osmo-hnodeb
|
|||
Section: net
|
||||
Priority: extra
|
||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||
Build-Depends: debhelper (>=9),
|
||||
Build-Depends: debhelper (>= 10),
|
||||
dh-autoreconf,
|
||||
autotools-dev,
|
||||
autoconf,
|
||||
|
@ -14,17 +14,17 @@ 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 (>= 0.8.0),
|
||||
libosmo-ranap-dev (>= 0.8.0),
|
||||
libosmo-rua-dev (>= 0.8.0),
|
||||
osmo-gsm-manuals-dev (>= 1.2.0)
|
||||
libosmocore-dev (>= 1.9.0),
|
||||
libosmo-sigtran-dev (>= 1.8.0),
|
||||
libosmo-abis-dev (>= 1.5.0),
|
||||
libosmo-netif-dev (>= 1.4.0),
|
||||
libosmo-hnbap-dev (>= 1.5.0),
|
||||
libosmo-ranap-dev (>= 1.5.0),
|
||||
libosmo-rua-dev (>= 1.5.0),
|
||||
osmo-gsm-manuals-dev (>= 1.5.0)
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: git://git.osmocom.org/osmo-hnodeb.git
|
||||
Vcs-Browser: https://git.osmocom.org/osmo-hnodeb/
|
||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-hnodeb
|
||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-hnodeb
|
||||
Homepage: https://projects.osmocom.org/projects/osmo-hnodeb
|
||||
|
||||
Package: osmo-hnodeb
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: osmo-hnodeb
|
||||
Source: git://git.osmocom.org/osmo-hnodeb
|
||||
Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-hnodeb
|
||||
|
||||
Files: *
|
||||
Copyright: 2021 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
# Create the osmocom group and user (if it doesn't exist yet)
|
||||
if ! getent group osmocom >/dev/null; then
|
||||
groupadd --system osmocom
|
||||
fi
|
||||
if ! getent passwd osmocom >/dev/null; then
|
||||
useradd \
|
||||
--system \
|
||||
--gid osmocom \
|
||||
--home-dir /var/lib/osmocom \
|
||||
--shell /sbin/nologin \
|
||||
--comment "Open Source Mobile Communications" \
|
||||
osmocom
|
||||
fi
|
||||
|
||||
# Fix permissions of previous (root-owned) install (OS#4107)
|
||||
chown osmocom:osmocom /etc/osmocom/osmo-hnodeb.cfg
|
||||
chmod 0660 /etc/osmocom/osmo-hnodeb.cfg
|
||||
chown root:osmocom /etc/osmocom
|
||||
chmod 2775 /etc/osmocom
|
||||
mkdir -p /var/lib/osmocom
|
||||
chown -R osmocom:osmocom /var/lib/osmocom
|
||||
;;
|
||||
esac
|
||||
|
||||
# dh_installdeb(1) will replace this with shell code automatically
|
||||
# generated by other debhelper scripts.
|
||||
#DEBHELPER#
|
|
@ -0,0 +1,24 @@
|
|||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
hnodeb
|
||||
cell_identity 1
|
||||
location_area_code 2
|
||||
routing_area_code 3
|
||||
service_area_code 4
|
||||
iuh
|
||||
local-ip 127.0.0.1
|
||||
remote-ip 127.0.0.1
|
||||
ll-socket
|
||||
path /tmp/hnb_prim_sock
|
||||
gtp
|
||||
local-ip 127.0.0.3
|
|
@ -0,0 +1,86 @@
|
|||
msc {
|
||||
hscale="3";
|
||||
ue [label="Customer"], trx [label="Lower Layer TRX"], hnodeb [label="osmo-hnodeb"], hnbgw [label="HNBGW"], ggsn [label="GGSN"], mgw [label="MGW"];
|
||||
|
||||
|||;
|
||||
--- [ label = "hNodeB starts up" ];
|
||||
hnodeb => hnbgw [label="HNBAP HnbRegisterRequest"];
|
||||
hnodeb <= hnbgw [label="HNBAP HnbRegisterResponse"];
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
|||;
|
||||
--- [ label = "TRX starts up & connects to hNodeB" ];
|
||||
trx => hnodeb [label="CTL-HELLO.req(SAPI=CTL, VERSION)"];
|
||||
trx <= hnodeb [label="CTL-HELLO.cnf(SAPI=CTL, VERSION)"];
|
||||
trx => hnodeb [label="CTL-HELLO.req(SAPI=IUH, VERSION)"];
|
||||
trx <= hnodeb [label="CTL-HELLO.cnf(SAPI=IUH, VERSION)"];
|
||||
trx => hnodeb [label="CTL-HELLO.req(SAPI=GTP, VERSION)"];
|
||||
trx <= hnodeb [label="CTL-HELLO.cnf(SAPI=GTP, VERSION)"];
|
||||
trx => hnodeb [label="CTL-HELLO.req(SAPI=AUDIO, VERSION)"];
|
||||
trx <= hnodeb [label="CTL-HELLO.cnf(SAPI=AUDIO, VERSION)"];
|
||||
trx <= hnodeb [label="IUH-CONFIGURE.ind(PLMN,LAC,SAC,RAC,CI,RNC_ID from HnbRegisterResponse)"];
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
|||;
|
||||
--- [ label = "Subscriber Paging" ];
|
||||
hnodeb <= hnbgw [label="RUA-InitiatingMessage(RANAP Paging)"];
|
||||
trx <= hnodeb [label="IUH-UNITDATA.ind[RANAP Paging]"];
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
|||;
|
||||
--- [ label = "Subscriber Sign Up" ];
|
||||
ue => trx [label="CM Service Req"];
|
||||
trx => hnodeb [label="IUH-CONN_ESTABLISH.req[RANAP CM ServiceReq]"];
|
||||
hnodeb => hnbgw [label="HNBAP UE Register Req(IMSI?)"];
|
||||
hnodeb <= hnbgw [label="HNBAP UE Register Acc(context_id)"];
|
||||
hnodeb => hnbgw [label="RUA-Connect(RANAP-GMM ServiceRequest)"];
|
||||
hnodeb <= hnbgw [label="RUA-DirectTransfer(RANAP GMM ServiceAccept)"];
|
||||
trx <= hnodeb [label="IUH-CONN_ESTABLISH.cnf(context_id])"];
|
||||
trx <= hnodeb [label="IUH-CONN_DATA.ind[context_id, RANAP GMM ServiceAccept]"];
|
||||
--- [ label = "Subscriber set up PS data:" ];
|
||||
hnodeb <= hnbgw [label="RANAP RAB-Assignment Request(TEI, ADDR)"];
|
||||
trx <= hnodeb [label="IUH-CONN_DATA.ind[RANAP RAB-Assignment Request(remote_ip, remote_port, remote_tei)]"];
|
||||
trx => hnodeb [label="GTP-CONN_ESTABLISH.req(context_id,remote_ip,remote_port,remote_tei)"];
|
||||
... [ label = "HnodeB sets up GTP-U connection" ];
|
||||
trx <= hnodeb [label="GTP-CONN_ESTABLISH.cnf(context_id,gtp_conn_id,local_ip,local_port,local_tei)"];
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
|||;
|
||||
--- [ label = "PS data transmission over GTP-U:" ];
|
||||
ue => trx [label="..."];
|
||||
trx => hnodeb [label="GTP-CONN_DATA.req[gtp_conn_id,payload]"];
|
||||
hnodeb => ggsn [label="GTP-U(remote_tei, local_addr, remote_addr, payload)"];
|
||||
hnodeb <= ggsn [label="GTP-U(local_tei, remote_addr, local_addr, payload)"];
|
||||
trx <= hnodeb [label="GTP-CONN_DATA.ind[gtp_conn_id,payload]"];
|
||||
ue <= trx [label="..."];
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
--- [ label = "MO/MT PS data Release:" ];
|
||||
ue => trx [label="..."];
|
||||
trx => hnodeb [label="IUH-CONN_DATA.req[RANAP IU Release Request]"];
|
||||
hnodeb => hnbgw [label="RANAP IU Release Request"];
|
||||
hnodeb <= hnbgw [label="RANAP IU Release Command"];
|
||||
trx <= hnodeb [label="IUH-CONN_DATA.ind[RANAP IU Release Command]"];
|
||||
...;
|
||||
trx => hnodeb [label="GTP-CONN_RELEASE.req(gtp_conn_id)"];
|
||||
|
||||
trx => hnodeb [label="IUH-CONN_RELEASE.req[RANAP IU Release Complete]"];
|
||||
hnodeb => hnbgw [label="RUA-Disconnect(RANAP IU Release Complete)"];
|
||||
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
--- [ label = "For voice call (CS): Similar to SAPI_GTP, but using SAPI_AUDIO and osmo-hnodeb sets up RTP stream" ];
|
||||
|
||||
|
||||
|||;
|
||||
...;
|
||||
|||;
|
||||
--- [ label = "Iuh SCTP link goes down" ];
|
||||
trx <= hnodeb [label="UD socket is closed, osmo-hnodeb cleans up state and attempts reconnect. lowerlayer can re-connect and wait for next CONFIGURE.req"];
|
||||
}
|
|
@ -1,8 +1,13 @@
|
|||
noinst_HEADERS = \
|
||||
gtp.h \
|
||||
hnb_shutdown_fsm.h \
|
||||
hnb_prim.h \
|
||||
hnbap.h \
|
||||
hnodeb.h \
|
||||
nas.h \
|
||||
iuh.h \
|
||||
llsk.h \
|
||||
ranap.h \
|
||||
rtp.h \
|
||||
rua.h \
|
||||
vty.h \
|
||||
$(NULL)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
|
||||
struct hnb;
|
||||
struct hnb_ue;
|
||||
|
||||
int hnb_gtp_bind(struct hnb *hnb);
|
||||
void hnb_gtp_unbind(struct hnb *hnb);
|
||||
|
||||
struct gtp_conn {
|
||||
struct llist_head list; /* Item in struct hnb->ue_list */
|
||||
struct hnb_ue *ue; /* backpointer */
|
||||
uint32_t id;
|
||||
struct osmo_sockaddr loc_addr;
|
||||
struct osmo_sockaddr rem_addr;
|
||||
uint32_t loc_tei;
|
||||
uint32_t rem_tei;
|
||||
uint16_t seq_nr;
|
||||
};
|
||||
|
||||
struct gtp_conn *gtp_conn_alloc(struct hnb_ue *ue);
|
||||
void gtp_conn_free(struct gtp_conn *conn);
|
||||
|
||||
int gtp_conn_setup(struct gtp_conn *conn, const struct osmo_sockaddr *rem_addr, uint32_t rem_tei);
|
||||
int gtp_conn_tx(struct gtp_conn *conn, const uint8_t *gtpu_payload, unsigned gtpu_payload_len);
|
|
@ -0,0 +1,324 @@
|
|||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
/* This header includes information relative to protocol and message structure
|
||||
* spoken between osmo-hnodeb, facing the HNBGW and other RAN/CN nodes, and a
|
||||
* Lower Layer program (aka the TRX), implementing the RLC/MAC/RRC towards the
|
||||
* UE in the Uu interface. This protocol is usually referenced as HNBLLIF. The
|
||||
* protocol is primitive based and follows the concepts described in ITU-T
|
||||
* X.210, with osmo-hnodeb taking the "service provider" role and the TRX taking
|
||||
* the "user" role in this case.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/prim.h>
|
||||
|
||||
#define HNB_PRIM_API_VERSION 0
|
||||
#define HNB_PRIM_UD_SOCK_DEFAULT "/tmp/hnb_prim_sock"
|
||||
|
||||
#define HNB_PRIM_SAPI_IUH 1
|
||||
#define HNB_PRIM_SAPI_GTP 2
|
||||
#define HNB_PRIM_SAPI_AUDIO 3
|
||||
|
||||
enum u_addr_type {
|
||||
HNB_PRIM_ADDR_TYPE_UNSPEC,
|
||||
HNB_PRIM_ADDR_TYPE_IPV4,
|
||||
HNB_PRIM_ADDR_TYPE_IPV6
|
||||
};
|
||||
union u_addr {
|
||||
struct in_addr v4;
|
||||
struct in6_addr v6;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*! \brief HNB_IUH primitives */
|
||||
enum hnb_iuh_prim_type {
|
||||
HNB_IUH_PRIM_CONFIGURE,
|
||||
HNB_IUH_PRIM_CONN_ESTABLISH,
|
||||
HNB_IUH_PRIM_CONN_RELEASE,
|
||||
HNB_IUH_PRIM_CONN_DATA,
|
||||
HNB_IUH_PRIM_UNITDATA,
|
||||
_HNB_IUH_PRIM_MAX
|
||||
};
|
||||
|
||||
/* HNB_IUH_PRIM_CONFIGURE.ind, DL */
|
||||
struct hnb_iuh_configure_ind_param {
|
||||
uint16_t mcc;
|
||||
uint16_t mnc;
|
||||
uint16_t cell_identity;
|
||||
uint16_t lac;
|
||||
uint8_t rac;
|
||||
uint8_t reserved;
|
||||
uint16_t sac;
|
||||
uint16_t rnc_id;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_HNB_IUH_PRIM_CONN_ESTABLISH.ind, DL */
|
||||
struct hnb_iuh_conn_establish_ind_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t cause;
|
||||
uint8_t csg_membership_status;
|
||||
uint8_t spare1;
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_HNB_IUH_PRIM_CONN_ESTABLISH.req, UL */
|
||||
struct hnb_iuh_conn_establish_req_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t est_cause;
|
||||
/* TODO: Check if we can copy it as an encoded buffer RRC <-> RUA
|
||||
* RRC: 3GPP TS 25.331 10.3.1.6 Intra Domain NAS Node Selector
|
||||
* RUA: 3GPP TS 25.468 9.2.4 */
|
||||
uint16_t reserved; //uint16_t nas_node_selector_bitlen;
|
||||
//uint8_t nas_node_selector[128]; /* TODO: check whether we can decrease this buffer size */
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_HNB_IUH_PRIM_CONN_ESTABLISH.cnf, DL */
|
||||
struct hnb_iuh_conn_establish_cnf_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t cause; /* 0 = success, !0 = failure */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_IUH_PRIM_CONN_RELEASE.req, DL */
|
||||
struct hnb_iuh_conn_release_req_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t spare1;
|
||||
uint8_t cause_type; /* 3GPP TS 25.468 9.2.7 Cause */
|
||||
uint8_t cause; /* 3GPP TS 25.468 9.2.7 Cause */
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_IUH_PRIM_CONN_RELEASE.ind, UL */
|
||||
struct hnb_iuh_conn_release_ind_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t spare1;
|
||||
uint8_t cause_type;
|
||||
uint8_t cause;
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_IUH_PRIM_CONN_DATA.req, DL */
|
||||
struct hnb_iuh_conn_data_req_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t spare1;
|
||||
uint16_t spare2;
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_IUH_PRIM_CONN_DATA.ind, UL */
|
||||
struct hnb_iuh_conn_data_ind_param {
|
||||
uint32_t context_id;
|
||||
uint8_t domain;
|
||||
uint8_t spare1;
|
||||
uint16_t spare2;
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_IUH_PRIM_UNITDATA.req, UL */
|
||||
struct hnb_iuh_unitdata_req_param {
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_IUH_PRIM_UNITDATA.ind, DL */
|
||||
struct hnb_iuh_unitdata_ind_param {
|
||||
uint32_t data_len; /* RANAP message length in bytes */
|
||||
uint8_t data[0]; /* RANAP message */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct hnb_iuh_prim {
|
||||
struct osmo_prim_hdr hdr;
|
||||
union {
|
||||
struct hnb_iuh_configure_ind_param configure_ind;
|
||||
struct hnb_iuh_conn_establish_req_param conn_establish_req;
|
||||
struct hnb_iuh_conn_establish_ind_param conn_establish_ind;
|
||||
struct hnb_iuh_conn_establish_cnf_param conn_establish_cnf;
|
||||
struct hnb_iuh_conn_release_req_param conn_release_req;
|
||||
struct hnb_iuh_conn_release_ind_param conn_release_ind;
|
||||
struct hnb_iuh_conn_data_req_param conn_data_req;
|
||||
struct hnb_iuh_conn_data_ind_param conn_data_ind;
|
||||
struct hnb_iuh_unitdata_req_param unitdata_req;
|
||||
struct hnb_iuh_unitdata_ind_param unitdata_ind;
|
||||
} u;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/****************************
|
||||
* AUDIO
|
||||
***************************/
|
||||
/*! \brief HNB_AUDIO primitives */
|
||||
enum hnb_audio_prim_type {
|
||||
HNB_AUDIO_PRIM_CONN_ESTABLISH,
|
||||
HNB_AUDIO_PRIM_CONN_RELEASE,
|
||||
HNB_AUDIO_PRIM_CONN_DATA,
|
||||
_HNB_AUDIO_PRIM_MAX
|
||||
};
|
||||
|
||||
/* HNB_AUDIO_PRIM_CONN_ESTABLISH, UL */
|
||||
#define HNB_MAX_RFCIS 64
|
||||
#define HNB_MAX_SUBFLOWS 7
|
||||
struct hnb_audio_conn_establish_req_param_v0 {
|
||||
uint32_t context_id;
|
||||
uint16_t remote_rtp_port;
|
||||
uint8_t spare1;
|
||||
uint8_t remote_rtp_address_type; /* enum u_addr_type */
|
||||
union u_addr remote_rtp_addr;
|
||||
/* IuUP related: */
|
||||
uint8_t transparent; /* 1=transparent; 0=SMpSDU */
|
||||
uint8_t data_pdu_type;
|
||||
uint16_t supported_versions_mask; /* host byte order */
|
||||
uint8_t num_rfci;
|
||||
uint8_t num_subflows;
|
||||
uint16_t subflow_sizes[HNB_MAX_RFCIS][HNB_MAX_SUBFLOWS];
|
||||
uint8_t IPTIs_present; /* 1=present; 0=not present */
|
||||
uint8_t IPTIs[HNB_MAX_RFCIS]; /* values range 0-15, 4 bits */
|
||||
} __attribute__ ((packed));
|
||||
struct hnb_audio_conn_establish_req_param_v1 {
|
||||
struct hnb_audio_conn_establish_req_param_v0 v0;
|
||||
uint8_t rfci[HNB_MAX_RFCIS]; /* values range 6 bits */
|
||||
} __attribute__ ((packed));
|
||||
struct hnb_audio_conn_establish_req_param {
|
||||
union {
|
||||
struct hnb_audio_conn_establish_req_param_v0 v0;
|
||||
struct hnb_audio_conn_establish_req_param_v1 v1;
|
||||
} __attribute__ ((packed));
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_AUDIO_PRIM_CONN_ESTABLISH, DL */
|
||||
struct hnb_audio_conn_establish_cnf_param {
|
||||
uint32_t context_id;
|
||||
uint32_t audio_conn_id;
|
||||
uint16_t local_rtp_port;
|
||||
uint8_t error_code; /* 0 = success, !0 = failure */
|
||||
uint8_t local_rtp_address_type; /* enum u_addr_type */
|
||||
union u_addr local_rtp_addr;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_AUDIO_PRIM_CONN_RELEASE, UL */
|
||||
struct hnb_audio_conn_release_req_param {
|
||||
uint32_t audio_conn_id;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_AUDIO_PRIM_CONN_DATA, UL */
|
||||
struct hnb_audio_conn_data_req_param {
|
||||
uint32_t audio_conn_id;
|
||||
uint8_t frame_nr;
|
||||
uint8_t fqc;
|
||||
uint8_t rfci;
|
||||
uint8_t spare;
|
||||
uint32_t data_len; /* RTP payload length in bytes */
|
||||
uint8_t data[0]; /* RTP payload (aka IP packet) */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_AUDIO_PRIM_CONN_DATA, DL */
|
||||
struct hnb_audio_conn_data_ind_param {
|
||||
uint32_t audio_conn_id;
|
||||
uint8_t frame_nr;
|
||||
uint8_t fqc;
|
||||
uint8_t rfci;
|
||||
uint8_t spare;
|
||||
uint32_t data_len; /* RTP payload length in bytes */
|
||||
uint8_t data[0]; /* RTP payload (aka IP packet) */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct hnb_audio_prim {
|
||||
struct osmo_prim_hdr hdr;
|
||||
union {
|
||||
struct hnb_audio_conn_establish_req_param conn_establish_req;
|
||||
struct hnb_audio_conn_establish_cnf_param conn_establish_cnf;
|
||||
struct hnb_audio_conn_release_req_param conn_release_req;
|
||||
struct hnb_audio_conn_data_req_param conn_data_req;
|
||||
struct hnb_audio_conn_data_ind_param conn_data_ind;
|
||||
} u;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/****************************
|
||||
* GTP
|
||||
***************************/
|
||||
/*! \brief HNB_GTP primitives */
|
||||
enum hnb_gtp_prim_type {
|
||||
HNB_GTP_PRIM_CONN_ESTABLISH,
|
||||
HNB_GTP_PRIM_CONN_RELEASE,
|
||||
HNB_GTP_PRIM_CONN_DATA,
|
||||
_HNB_GTP_PRIM_MAX
|
||||
};
|
||||
|
||||
/* HNB_GTP_PRIM_CONN_ESTABLISH, UL */
|
||||
struct hnb_gtp_conn_establish_req_param {
|
||||
uint32_t context_id;
|
||||
uint32_t remote_tei;
|
||||
uint8_t spare1;
|
||||
uint8_t remote_gtpu_address_type;
|
||||
union u_addr remote_gtpu_addr;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_GTP_PRIM_CONN_ESTABLISH, DL */
|
||||
struct hnb_gtp_conn_establish_cnf_param {
|
||||
uint32_t context_id;
|
||||
uint32_t gtp_conn_id;
|
||||
uint32_t local_tei;
|
||||
uint8_t error_code; /* 0 = success, !0 = failure */
|
||||
uint8_t local_gtpu_address_type; /* enum u_addr_type */
|
||||
union u_addr local_gtpu_addr;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_GTP_PRIM_CONN_RELEASE, UL */
|
||||
struct hnb_gtp_conn_release_req_param {
|
||||
uint32_t gtp_conn_id;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_GTP_PRIM_CONN_DATA, DL */
|
||||
struct hnb_gtp_conn_data_ind_param {
|
||||
uint32_t gtp_conn_id;
|
||||
uint32_t data_len; /* GTP-U payload length in bytes */
|
||||
uint8_t data[0]; /* GTP-U payload (aka IP packet) */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* HNB_GTP_PRIM_CONN_DATA, UL */
|
||||
struct hnb_gtp_conn_data_req_param {
|
||||
uint32_t gtp_conn_id;
|
||||
uint32_t data_len; /* GTP-U payload length in bytes */
|
||||
uint8_t data[0]; /* GTP-U payload (aka IP packet) */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct hnb_gtp_prim {
|
||||
struct osmo_prim_hdr hdr;
|
||||
union {
|
||||
struct hnb_gtp_conn_establish_req_param conn_establish_req;
|
||||
struct hnb_gtp_conn_establish_cnf_param conn_establish_cnf;
|
||||
struct hnb_gtp_conn_release_req_param conn_release_req;
|
||||
struct hnb_gtp_conn_data_req_param conn_data_req;
|
||||
struct hnb_gtp_conn_data_ind_param conn_data_ind;
|
||||
} u;
|
||||
} __attribute__ ((packed));
|
|
@ -0,0 +1,43 @@
|
|||
/* hNodeB shutdown FSM */
|
||||
|
||||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
|
||||
enum hnb_shutdown_fsm_states {
|
||||
HNB_SHUTDOWN_ST_NONE,
|
||||
HNB_SHUTDOWN_ST_IN_PROGRESS,
|
||||
HNB_SHUTDOWN_ST_EXIT,
|
||||
};
|
||||
|
||||
enum hnb_shutdown_fsm_events {
|
||||
HNB_SHUTDOWN_EV_START,
|
||||
};
|
||||
|
||||
extern struct osmo_fsm hnb_shutdown_fsm;
|
||||
|
||||
struct hnb;
|
||||
void hnb_shutdown(struct hnb *hnb, const char *reason, bool exit_proc);
|
||||
bool hnb_shutdown_in_progress(const struct hnb *hnb);
|
|
@ -19,40 +19,58 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <asn1c/asn1helpers.h>
|
||||
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/write_queue.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
|
||||
#include <osmocom/netif/stream.h>
|
||||
|
||||
#include <osmocom/hnodeb/llsk.h>
|
||||
|
||||
enum {
|
||||
DMAIN,
|
||||
DHNBAP,
|
||||
DRUA,
|
||||
DRANAP,
|
||||
DNAS,
|
||||
DSCTP,
|
||||
DLLSK,
|
||||
DRTP,
|
||||
DGTP,
|
||||
};
|
||||
extern const struct log_info hnb_log_info;
|
||||
|
||||
/* 25.467 Section 7.1 */
|
||||
#define IUH_DEFAULT_SCTP_PORT 29169
|
||||
#define RNA_DEFAULT_SCTP_PORT 25471
|
||||
struct hnb;
|
||||
|
||||
#define IUH_PPI_RUA 19
|
||||
#define IUH_PPI_HNBAP 20
|
||||
#define IUH_PPI_SABP 31
|
||||
#define IUH_PPI_RNA 42
|
||||
#define IUH_PPI_PUA 55
|
||||
|
||||
#define IUH_MSGB_SIZE 2048
|
||||
|
||||
struct hnb_chan {
|
||||
int is_ps;
|
||||
struct hnb_ue {
|
||||
struct llist_head list; /* Item in struct hnb->ue_list */
|
||||
struct hnb *hnb; /* backpointer */
|
||||
uint32_t conn_id;
|
||||
char *imsi;
|
||||
char imsi[OSMO_IMSI_BUF_SIZE];
|
||||
struct hnb_ue_cs_ctx {
|
||||
bool active; /* Is this chan in use? */
|
||||
bool conn_est_cnf_pending; /* Did we send CONN_ESTABLISH_CNF to lower layers? */
|
||||
struct llist_head conn_list; /* list of struct rtp_conn */
|
||||
} conn_cs;
|
||||
struct hnb_ue_ps_ctx {
|
||||
bool active; /* Is this chan in use? */
|
||||
bool conn_est_cnf_pending; /* Did we send CONN_ESTABLISH_CNF to lower layers? */
|
||||
struct llist_head conn_list; /* list of struct gtp_conn */
|
||||
} conn_ps;
|
||||
};
|
||||
struct hnb_ue *hnb_ue_alloc(struct hnb *hnb, uint32_t conn_id);
|
||||
void hnb_ue_free(struct hnb_ue *ue);
|
||||
void hnb_ue_reset_chan(struct hnb_ue *ue, bool is_ps);
|
||||
|
||||
|
||||
struct hnb {
|
||||
char *identity; /* HNB-Identity */
|
||||
|
@ -69,19 +87,54 @@ struct hnb {
|
|||
struct osmo_stream_cli *client;
|
||||
} iuh;
|
||||
|
||||
/* Lower Layer UD socket */
|
||||
struct {
|
||||
struct osmo_prim_srv_link *link;
|
||||
struct osmo_prim_srv *srv;
|
||||
uint8_t valid_sapi_mask;
|
||||
uint16_t sapi_version_iuh;
|
||||
uint16_t sapi_version_gtp;
|
||||
uint16_t sapi_version_audio;
|
||||
struct osmo_timer_list defer_configure_ind_timer;
|
||||
} llsk;
|
||||
|
||||
struct {
|
||||
unsigned int jitter_buf_ms;
|
||||
bool jitter_adaptive;
|
||||
|
||||
uint16_t port_range_start;
|
||||
uint16_t port_range_end;
|
||||
uint16_t port_range_next;
|
||||
int ip_dscp;
|
||||
int priority;
|
||||
} rtp;
|
||||
|
||||
struct gtp {
|
||||
char *cfg_local_addr;
|
||||
struct osmo_sockaddr local_addr;
|
||||
struct osmo_wqueue wq1u;
|
||||
} gtp;
|
||||
|
||||
uint16_t rnc_id;
|
||||
bool registered; /* Set to true once HnbRegisterAccept was received from Iuh. rnc_id is valid iif registered==true */
|
||||
|
||||
uint32_t ctx_id;
|
||||
|
||||
struct {
|
||||
struct hnb_chan *chan;
|
||||
} cs;
|
||||
struct osmo_fsm_inst *shutdown_fi; /* FSM instance to manage shutdown procedure during process exit */
|
||||
bool shutdown_fi_exit_proc; /* exit process when shutdown_fsm is finished? */
|
||||
|
||||
struct llist_head ue_list; /* list of struct hnb_ue */
|
||||
};
|
||||
|
||||
struct hnb *hnb_alloc(void *tall_ctx);
|
||||
void hnb_free(struct hnb *hnb);
|
||||
int hnb_connect(struct hnb *hnb);
|
||||
struct hnb_ue *hnb_find_ue_by_id(const struct hnb *hnb, uint32_t conn_id);
|
||||
struct hnb_ue *hnb_find_ue_by_imsi(const struct hnb *hnb, char *imsi);
|
||||
|
||||
int hnb_iuh_send(struct hnb *hnb, struct msgb *msg);
|
||||
struct rtp_conn *hnb_find_rtp_conn_by_id(const struct hnb *hnb, uint32_t audio_conn_id);
|
||||
struct gtp_conn *hnb_find_gtp_conn_by_id(const struct hnb *hnb, uint32_t gtp_conn_id);
|
||||
|
||||
extern void *tall_hnb_ctx;
|
||||
extern struct hnb *g_hnb;
|
||||
|
||||
#define LOGUE(ue, ss, lvl, fmt, args...) LOGP(ss, lvl, "UE(%" PRIu32 ") " fmt, (ue)->conn_id, ## args)
|
||||
|
|
|
@ -17,8 +17,25 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
/* 25.467 Section 7.1 */
|
||||
#define IUH_DEFAULT_SCTP_PORT 29169
|
||||
#define RNA_DEFAULT_SCTP_PORT 25471
|
||||
|
||||
#define IUH_PPI_RUA 19
|
||||
#define IUH_PPI_HNBAP 20
|
||||
#define IUH_PPI_SABP 31
|
||||
#define IUH_PPI_RNA 42
|
||||
#define IUH_PPI_PUA 55
|
||||
|
||||
#define IUH_MSGB_SIZE 2048
|
||||
|
||||
struct hnb;
|
||||
void hnb_nas_rx_dtap(struct hnb *hnb, void *data, int len);
|
||||
|
||||
void hnb_iuh_alloc(struct hnb *hnb);
|
||||
void hnb_iuh_free(struct hnb *hnb);
|
||||
int hnb_iuh_connect(struct hnb *hnb);
|
||||
int hnb_iuh_send(struct hnb *hnb, struct msgb *msg);
|
|
@ -0,0 +1,68 @@
|
|||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/netif/prim.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
|
||||
struct hnb;
|
||||
struct rtp_conn;
|
||||
|
||||
int hnb_llsk_alloc(struct hnb *hnb);
|
||||
void hnb_llsk_free(struct hnb *hnb);
|
||||
int hnb_llsk_start_listen(struct hnb *hnb);
|
||||
bool hnb_llsk_connected(const struct hnb *hnb);
|
||||
bool hnb_llsk_can_be_configured(struct hnb *hnb);
|
||||
void hnb_llsk_close_conn(const struct hnb *hnb);
|
||||
|
||||
int ll_addr_type2af(enum u_addr_type t);
|
||||
int ll_addr2osa(enum u_addr_type t, const union u_addr *uaddr, uint16_t port, struct osmo_sockaddr *osa);
|
||||
enum u_addr_type osa2_ll_addr(const struct osmo_sockaddr *osa, union u_addr *uaddr, uint16_t *port);
|
||||
|
||||
#define LLSK_SAPI_IUH_VERSION_MIN 0
|
||||
#define LLSK_SAPI_IUH_VERSION_MAX 0
|
||||
extern const struct value_string hnb_iuh_prim_type_names[];
|
||||
int llsk_rx_iuh(struct hnb *hnb, struct osmo_prim_hdr *oph);
|
||||
int llsk_iuh_tx_configure_ind(struct hnb *hnb);
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_conn_establish_cnf(uint32_t context_id, uint8_t domain,
|
||||
uint8_t cause);
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_conn_data_ind(uint32_t context_id,
|
||||
uint8_t domain,
|
||||
const uint8_t *data,
|
||||
uint32_t data_len);
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_unitdata_ind(const uint8_t *data, uint32_t data_len);
|
||||
|
||||
#define LLSK_SAPI_AUDIO_VERSION_MIN 0
|
||||
#define LLSK_SAPI_AUDIO_VERSION_MAX 1
|
||||
extern const struct value_string hnb_audio_prim_type_names[];
|
||||
int llsk_audio_sapi_version_confirmed(uint16_t sapi_version);
|
||||
int llsk_rx_audio(struct hnb *hnb, struct osmo_prim_hdr *oph);
|
||||
int llsk_audio_tx_conn_data_ind(struct rtp_conn *conn, uint8_t frame_nr, uint8_t fqc, uint8_t rfci,
|
||||
const uint8_t *payload, uint32_t len);
|
||||
|
||||
#define LLSK_SAPI_GTP_VERSION_MIN 0
|
||||
#define LLSK_SAPI_GTP_VERSION_MAX 0
|
||||
extern const struct value_string hnb_gtp_prim_type_names[];
|
||||
int llsk_rx_gtp(struct hnb *hnb, struct osmo_prim_hdr *oph);
|
||||
struct hnb_gtp_prim *hnb_gtp_makeprim_conn_data_ind(uint32_t gtp_conn_id, const uint8_t *data, uint32_t data_len);
|
|
@ -19,15 +19,6 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
struct hnb;
|
||||
struct ranap_message_s;
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
void hnb_rx_iu_release(struct hnb *hnb);
|
||||
void hnb_rx_paging(struct hnb *hnb, const char *imsi);
|
||||
void hnb_rx_secmode_cmd(struct hnb *hnb, long ip_alg);
|
||||
|
||||
void hnb_rua_dt_handle_ranap(void *ctx, struct ranap_message_s *ranap_msg);
|
||||
void hnb_rua_cl_handle_ranap(void *ctx, struct ranap_message_s *ranap_msg);
|
||||
void hnb_tx_iu_release_req(struct hnb *hnb);
|
||||
struct msgb *gen_initue_lu(const struct hnb *hnb, int is_ps, uint32_t conn_id, const char *imsi);
|
||||
struct msgb *hnb_ranap_msgb_alloc(void);
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
|
||||
struct hnb;
|
||||
struct hnb_ue;
|
||||
|
||||
struct rtp_conn {
|
||||
struct llist_head list; /* Item in struct hnb->ue_list */
|
||||
struct hnb_ue *ue; /* backpointer */
|
||||
uint32_t id;
|
||||
struct osmo_rtp_socket *socket;
|
||||
struct osmo_sockaddr loc_addr;
|
||||
struct osmo_sockaddr rem_addr;
|
||||
struct osmo_iuup_instance *iui;
|
||||
};
|
||||
|
||||
struct rtp_conn *rtp_conn_alloc(struct hnb_ue *ue);
|
||||
void rtp_conn_free(struct rtp_conn *conn);
|
||||
|
||||
int rtp_conn_setup(struct rtp_conn *conn, const char *local_ipstr, const struct osmo_sockaddr *rem_addr,
|
||||
const struct hnb_audio_conn_establish_req_param *ce_req);
|
||||
int rtp_conn_tx_data(struct rtp_conn *conn, uint8_t frame_nr, uint8_t fqc, uint8_t rfci, const uint8_t *data, unsigned int data_len);
|
|
@ -22,7 +22,9 @@
|
|||
#include <asn1c/ANY.h>
|
||||
|
||||
struct hnb;
|
||||
struct hnb_ue;
|
||||
struct msgb;
|
||||
|
||||
struct msgb *hnb_rua_msgb_alloc(void);
|
||||
|
||||
int hnb_rua_rx(struct hnb *hnb, struct msgb *msg);
|
||||
int hnb_tx_dt(struct hnb *hnb, struct msgb *txm);
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
enum hnb_vty_nodes {
|
||||
HNODEB_NODE = _LAST_OSMOVTY_NODE,
|
||||
IUH_NODE,
|
||||
CHAN_NODE,
|
||||
LLSK_NODE,
|
||||
GTP_NODE,
|
||||
};
|
||||
|
||||
void hnb_vty_init(void);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# (C) 2021 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
|
||||
# (C) 2021 by sysmocom - s.f.m.c. GmbH <info@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 3 of the License, or
|
||||
|
|
|
@ -14,6 +14,7 @@ AM_CFLAGS = \
|
|||
$(LIBOSMONETIF_CFLAGS) \
|
||||
$(COVERAGE_CFLAGS) \
|
||||
$(LIBOSMOABIS_CFLAGS) \
|
||||
$(LIBOSMOTRAU_CFLAGS) \
|
||||
$(LIBOSMOSIGTRAN_CFLAGS) \
|
||||
$(LIBOSMORUA_CFLAGS) \
|
||||
$(LIBOSMORANAP_CFLAGS) \
|
||||
|
@ -31,10 +32,17 @@ bin_PROGRAMS = \
|
|||
osmo_hnodeb_SOURCES = \
|
||||
main.c \
|
||||
debug.c \
|
||||
gtp.c \
|
||||
hnbap.c \
|
||||
hnb.c \
|
||||
nas.c \
|
||||
hnb_shutdown_fsm.c \
|
||||
iuh.c \
|
||||
llsk.c \
|
||||
llsk_audio.c \
|
||||
llsk_gtp.c \
|
||||
llsk_iuh.c \
|
||||
ranap.c \
|
||||
rtp.c \
|
||||
rua.c \
|
||||
vty.c \
|
||||
$(NULL)
|
||||
|
@ -46,8 +54,8 @@ osmo_hnodeb_LDADD = \
|
|||
$(LIBOSMOVTY_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMONETIF_LIBS) \
|
||||
$(COVERAGE_LDFLAGS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOTRAU_LIBS) \
|
||||
$(LIBOSMOSIGTRAN_LIBS) \
|
||||
$(LIBOSMORUA_LIBS) \
|
||||
$(LIBOSMORANAP_LIBS) \
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
static const struct log_info_cat log_cat[] = {
|
||||
[DMAIN] = {
|
||||
.name = "DMAIN", .loglevel = LOGL_NOTICE, .enabled = 1,
|
||||
.color = "",
|
||||
.color = "\033[1;37m",
|
||||
.description = "Main program",
|
||||
},
|
||||
[DHNBAP] = {
|
||||
|
@ -42,10 +42,25 @@ static const struct log_info_cat log_cat[] = {
|
|||
.color = "\033[1;35m",
|
||||
.description = "RANAP User Adaptation",
|
||||
},
|
||||
[DNAS] = {
|
||||
.name = "NAS", .loglevel = LOGL_NOTICE, .enabled = 1,
|
||||
[DSCTP] = {
|
||||
.name = "DSCTP", .loglevel = LOGL_NOTICE, .enabled = 1,
|
||||
.color = "\033[1;36m",
|
||||
.description = "SCTP connection on the Iuh link",
|
||||
},
|
||||
[DLLSK] = {
|
||||
.name = "DLLSK", .loglevel = LOGL_NOTICE, .enabled = 1,
|
||||
.color = "\033[1;31m",
|
||||
.description = "Lower Layer Unix Domain Socket",
|
||||
},
|
||||
[DRTP] = {
|
||||
.name = "DRTP", .loglevel = LOGL_NOTICE, .enabled = 1,
|
||||
.color = "\033[1;32m",
|
||||
.description = "Non-Access Stratum",
|
||||
.description = "RTP Core Network side",
|
||||
},
|
||||
[DGTP] = {
|
||||
.name = "DGTP", .loglevel = LOGL_NOTICE, .enabled = 1,
|
||||
.color = "\033[1;30m",
|
||||
.description = "GPRS Tunnelling Protocol",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,345 @@
|
|||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <osmocom/hnodeb/gtp.h>
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/llsk.h>
|
||||
#include <osmocom/core/endian.h>
|
||||
|
||||
/* 3GPP TS 29.281 */
|
||||
struct gtp1u_hdr { /* 3GPP TS 29.281 */
|
||||
#if OSMO_IS_LITTLE_ENDIAN
|
||||
uint8_t pn:1, /* N-PDU Number flag */
|
||||
s:1, /* Sequence number flag */
|
||||
e:1, /* Extension header flag */
|
||||
spare:1,
|
||||
pt:1, /* Protocol Type: GTP=1, GTP'=0 */
|
||||
version:3; /* Version: 1 */
|
||||
#elif OSMO_IS_BIG_ENDIAN
|
||||
uint8_t version:3, pt:1, spare:1, e:1, s:1, pn:1;
|
||||
#endif
|
||||
uint8_t msg_type;
|
||||
uint16_t length;
|
||||
uint32_t tei; /* 05 - 08 Tunnel Endpoint ID */
|
||||
uint8_t data[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
static uint32_t next_gtp_conn_id = 1;
|
||||
|
||||
struct gtp_conn *gtp_conn_alloc(struct hnb_ue *ue)
|
||||
{
|
||||
struct gtp_conn *conn;
|
||||
|
||||
conn = talloc_zero(ue, struct gtp_conn);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
|
||||
conn->ue = ue;
|
||||
|
||||
llist_add(&conn->list, &ue->conn_ps.conn_list);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
void gtp_conn_free(struct gtp_conn *conn)
|
||||
{
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
llist_del(&conn->list);
|
||||
talloc_free(conn);
|
||||
}
|
||||
|
||||
int gtp_conn_setup(struct gtp_conn *conn, const struct osmo_sockaddr *rem_addr, uint32_t rem_tei)
|
||||
{
|
||||
struct hnb_ue *ue = conn->ue;
|
||||
struct hnb *hnb = ue->hnb;
|
||||
|
||||
LOGUE(ue, DGTP, LOGL_INFO, "Creating PDP context\n");
|
||||
|
||||
|
||||
if (rem_addr->u.sa.sa_family != AF_INET) {
|
||||
LOGUE(ue, DGTP, LOGL_ERROR, "Failed creating PDP context: unsupported proto family %u\n",
|
||||
rem_addr->u.sa.sa_family);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
conn->id = next_gtp_conn_id++; /* TODO: validate next one is not already taken due to wrap-around */
|
||||
|
||||
conn->loc_addr = hnb->gtp.local_addr;
|
||||
conn->rem_addr = *rem_addr;
|
||||
conn->rem_tei = rem_tei;
|
||||
conn->loc_tei = conn->id; /* We use conn ID as local TEI */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gtp_conn_tx(struct gtp_conn *conn, const uint8_t *gtpu_payload, unsigned gtpu_payload_len)
|
||||
{
|
||||
int rc;
|
||||
struct hnb_ue *ue;
|
||||
struct hnb *hnb;
|
||||
struct msgb *msg;
|
||||
struct gtp1u_hdr *hdr;
|
||||
struct osmo_sockaddr *sa;
|
||||
const uint8_t opt_hdr_len = 4;
|
||||
unsigned msg_len = sizeof(*hdr) + opt_hdr_len + gtpu_payload_len;
|
||||
|
||||
if (!conn) {
|
||||
LOGP(DGTP, LOGL_ERROR, "Tx: GTP-U conn not available\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ue = conn->ue;
|
||||
hnb = ue->hnb;
|
||||
|
||||
if (hnb->gtp.wq1u.bfd.fd == -1) {
|
||||
LOGP(DGTP, LOGL_ERROR, "Tx: GTP-U socket not available\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (gtpu_payload_len == 0) {
|
||||
LOGP(DGTP, LOGL_ERROR, "Tx: GTP-U len=0\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
msg = msgb_alloc_c(hnb, sizeof(*sa) + msg_len, "gtp-ul");
|
||||
|
||||
sa = (struct osmo_sockaddr *)msgb_put(msg, sizeof(*sa));
|
||||
*sa = conn->rem_addr;
|
||||
|
||||
hdr = (struct gtp1u_hdr *)msgb_put(msg, msg_len);
|
||||
hdr->pn = 0;
|
||||
hdr->s = 1;
|
||||
hdr->e = 0;
|
||||
hdr->spare = 0;
|
||||
hdr->pt = 1;
|
||||
hdr->version = 1;
|
||||
hdr->msg_type = 0xff;
|
||||
osmo_store16be(gtpu_payload_len + opt_hdr_len, &hdr->length);
|
||||
osmo_store32be(conn->rem_tei, &hdr->tei);
|
||||
osmo_store16be(conn->seq_nr, &hdr->data[0]);
|
||||
conn->seq_nr++;
|
||||
/* byes 2 and 3 are set to 0 */
|
||||
memcpy(&hdr->data[opt_hdr_len], gtpu_payload, gtpu_payload_len);
|
||||
|
||||
rc = osmo_wqueue_enqueue(&hnb->gtp.wq1u, msg);
|
||||
if (rc < 0)
|
||||
msgb_free(msg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Called whenever we receive a DATA packet */
|
||||
static int hnb_gtp_rx_gtp(struct hnb *hnb, struct msgb *msg, const struct osmo_sockaddr *from)
|
||||
{
|
||||
struct gtp_conn *conn;
|
||||
struct hnb_ue *ue;
|
||||
struct gtp1u_hdr *hdr;
|
||||
uint32_t tei;
|
||||
struct hnb_gtp_prim *gtp_prim;
|
||||
int rc;
|
||||
|
||||
hdr = (struct gtp1u_hdr *)msgb_data(msg);
|
||||
tei = osmo_load32be(&hdr->tei);
|
||||
|
||||
/* The local TEI is the GTP conn_id: */
|
||||
conn = hnb_find_gtp_conn_by_id(hnb, tei);
|
||||
if (!conn) {
|
||||
LOGP(DGTP, LOGL_NOTICE, "Tx GTP-CONN_DATA.ind len=%u with no conn!\n",
|
||||
msgb_l3len(msg));
|
||||
rc = -EINVAL;
|
||||
goto free_ret;
|
||||
}
|
||||
ue = conn->ue;
|
||||
|
||||
if (osmo_sockaddr_cmp(from, &conn->rem_addr)) {
|
||||
LOGUE(ue, DGTP, LOGL_NOTICE, "Rx DL GTP-U loc_tei=0x%08x from unexpected addr=%s!\n",
|
||||
tei, osmo_sockaddr_to_str(from));
|
||||
rc = -EINVAL;
|
||||
goto free_ret;
|
||||
}
|
||||
ue = conn->ue;
|
||||
|
||||
if (!ue->conn_ps.active) {
|
||||
LOGUE(ue, DGTP, LOGL_NOTICE, "Tx GTP-CONN_DATA.ind len=%u but UE conn_ps is not active!\n",
|
||||
msgb_l3len(msg));
|
||||
rc = -EINVAL;
|
||||
goto free_ret;
|
||||
}
|
||||
hnb = ue->hnb;
|
||||
|
||||
LOGUE(ue, DGTP, LOGL_DEBUG, "Tx GTP-CONN_DATA.ind len=%u\n", msgb_l3len(msg));
|
||||
gtp_prim = hnb_gtp_makeprim_conn_data_ind(conn->id, msgb_l3(msg), msgb_l3len(msg));
|
||||
if ((rc = osmo_prim_srv_send(hnb->llsk.srv, gtp_prim->hdr.msg)) < 0) {
|
||||
LOGUE(ue, DGTP, LOGL_ERROR, "Failed Tx GTP-CONN_DATA.ind len=%u\n",
|
||||
msgb_l3len(msg));
|
||||
}
|
||||
free_ret:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnb_gtp_wq_read_cb(struct osmo_fd *fd)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)fd->data;
|
||||
struct msgb *msg;
|
||||
struct gtp1u_hdr *hdr;
|
||||
int rc;
|
||||
struct osmo_sockaddr peer;
|
||||
socklen_t peerlen = sizeof(peer.u.sas);
|
||||
unsigned int opt_hdr_len;
|
||||
uint16_t pl_len;
|
||||
|
||||
msg = msgb_alloc_c(hnb, 4096, "gtp-dl");
|
||||
|
||||
rc = recvfrom(hnb->gtp.wq1u.bfd.fd, msgb_data(msg), msg->data_len, 0,
|
||||
&peer.u.sa, &peerlen);
|
||||
if (rc <= 0) {
|
||||
LOGP(DGTP, LOGL_ERROR, "recvfrom() failed: rc = %d error = %s\n",
|
||||
rc, rc ? strerror(errno) : "No error");
|
||||
goto free_ret;
|
||||
}
|
||||
msgb_put(msg, rc);
|
||||
|
||||
/* Do some sanity checks: */
|
||||
if (msgb_length(msg) < sizeof(*hdr)) {
|
||||
LOGP(DGTP, LOGL_ERROR, "Rx GTP-U packet with size %u < %zu (header)\n",
|
||||
msgb_length(msg), sizeof(*hdr));
|
||||
rc = -EINVAL;
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
hdr = (struct gtp1u_hdr *)msgb_data(msg);
|
||||
pl_len = osmo_load16be(&hdr->length);
|
||||
|
||||
/* Do some sanity checks: */
|
||||
if (hdr->version != 1) {
|
||||
LOGP(DGTP, LOGL_ERROR, "Rx GTP-U version %u != 1\n", hdr->version);
|
||||
rc = -ENOTSUP;
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
if (hdr->e == 1) {
|
||||
LOGP(DGTP, LOGL_ERROR, "Rx GTP-U with Extension Header not supported\n");
|
||||
rc = -ENOTSUP;
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
if (hdr->s || hdr->pn || hdr->e)
|
||||
opt_hdr_len = 4;
|
||||
else
|
||||
opt_hdr_len = 0;
|
||||
|
||||
if (msgb_length(msg) < sizeof(*hdr) + opt_hdr_len) {
|
||||
LOGP(DGTP, LOGL_ERROR, "Rx GTP-U packet with size %u < %zu (header + opt)\n",
|
||||
msgb_length(msg), sizeof(*hdr) + opt_hdr_len);
|
||||
rc = -EINVAL;
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
msg->l3h = msgb_data(msg) + sizeof(*hdr) + opt_hdr_len;
|
||||
|
||||
if (pl_len < opt_hdr_len || msgb_l3len(msg) != (pl_len - opt_hdr_len)) {
|
||||
LOGP(DGTP, LOGL_ERROR, "Rx GTP-U packet with payload size %u != %u (header)\n",
|
||||
msgb_length(msg), pl_len - opt_hdr_len);
|
||||
rc = -EINVAL;
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
return hnb_gtp_rx_gtp(hnb, msg, &peer);
|
||||
free_ret:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnb_gtp_wq_write_cb(struct osmo_fd *fd, struct msgb *msg)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)fd->data;
|
||||
struct osmo_sockaddr *rem_addr;
|
||||
int rc;
|
||||
|
||||
rem_addr = (struct osmo_sockaddr *)msgb_data(msg);
|
||||
msgb_pull(msg, sizeof(*rem_addr));
|
||||
|
||||
rc = sendto(hnb->gtp.wq1u.bfd.fd, msgb_data(msg), msgb_length(msg), 0,
|
||||
&rem_addr->u.sa, sizeof(*rem_addr));
|
||||
if (rc < 0) {
|
||||
int err = errno;
|
||||
LOGP(DGTP, LOGL_ERROR, "GTP1-U sendto(len=%d, to=%s) failed: Error = %s\n",
|
||||
msgb_length(msg), osmo_sockaddr_to_str(rem_addr), strerror(err));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Get osa of locally bound GTP-U socket */
|
||||
static int sk_get_bound_addr(int fd, struct osmo_sockaddr *osa)
|
||||
{
|
||||
int rc;
|
||||
socklen_t alen = sizeof(*osa);
|
||||
|
||||
rc = getsockname(fd, (struct sockaddr *)&osa->u.sa, &alen);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hnb_gtp_bind(struct hnb *hnb)
|
||||
{
|
||||
int rc;
|
||||
|
||||
OSMO_ASSERT(hnb->gtp.wq1u.bfd.fd == -1);
|
||||
|
||||
hnb->gtp.wq1u.read_cb = hnb_gtp_wq_read_cb;
|
||||
hnb->gtp.wq1u.write_cb = hnb_gtp_wq_write_cb;
|
||||
|
||||
rc = osmo_sock_init2_ofd(&hnb->gtp.wq1u.bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
|
||||
hnb->gtp.cfg_local_addr, 2152, NULL, 0, OSMO_SOCK_F_BIND);
|
||||
if (rc < 0) {
|
||||
LOGP(DGTP, LOGL_ERROR, "Failed to set up GTP socket: %s\n", strerror(-rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = sk_get_bound_addr(hnb->gtp.wq1u.bfd.fd, &hnb->gtp.local_addr);
|
||||
if (rc < 0) {
|
||||
LOGP(DGTP, LOGL_ERROR, "Failed to get GTP-U socket bound address: %s\n", strerror(-rc));
|
||||
goto free_ret;
|
||||
}
|
||||
return 0;
|
||||
|
||||
free_ret:
|
||||
if (hnb->gtp.wq1u.bfd.fd != -1) {
|
||||
close(hnb->gtp.wq1u.bfd.fd);
|
||||
hnb->gtp.wq1u.bfd.fd = -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void hnb_gtp_unbind(struct hnb *hnb)
|
||||
{
|
||||
if (hnb->gtp.wq1u.bfd.fd != -1) {
|
||||
osmo_wqueue_clear(&hnb->gtp.wq1u);
|
||||
osmo_fd_unregister(&hnb->gtp.wq1u.bfd);
|
||||
close(hnb->gtp.wq1u.bfd.fd);
|
||||
hnb->gtp.wq1u.bfd.fd = -1;
|
||||
}
|
||||
}
|
|
@ -22,159 +22,180 @@
|
|||
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/sctp.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/netif/stream.h>
|
||||
#include <osmocom/netif/prim.h>
|
||||
|
||||
#include <osmocom/trau/osmo_ortp.h>
|
||||
|
||||
#include <osmocom/hnodeb/hnbap.h>
|
||||
#include <osmocom/hnodeb/rua.h>
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/iuh.h>
|
||||
#include <osmocom/hnodeb/hnb_shutdown_fsm.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
#include <osmocom/hnodeb/rtp.h>
|
||||
#include <osmocom/hnodeb/gtp.h>
|
||||
|
||||
static int hnb_iuh_read_cb(struct osmo_stream_cli *conn)
|
||||
{
|
||||
struct osmo_fd *fd = osmo_stream_cli_get_ofd(conn);
|
||||
struct hnb *hnb = osmo_stream_cli_get_data(conn);
|
||||
struct sctp_sndrcvinfo sinfo;
|
||||
struct msgb *msg = msgb_alloc(IUH_MSGB_SIZE, "Iuh rx");
|
||||
int flags = 0;
|
||||
int rc;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = sctp_recvmsg(fd->fd, msgb_data(msg), msgb_tailroom(msg),
|
||||
NULL, NULL, &sinfo, &flags);
|
||||
if (rc < 0) {
|
||||
LOGP(DMAIN, LOGL_ERROR, "Error during sctp_recvmsg()\n");
|
||||
/* FIXME: clean up after disappeared HNB */
|
||||
osmo_stream_cli_close(conn);
|
||||
goto free_ret;
|
||||
} else if (rc == 0) {
|
||||
LOGP(DMAIN, LOGL_INFO, "Connection to HNB closed\n");
|
||||
osmo_stream_cli_close(conn);
|
||||
rc = -1;
|
||||
goto free_ret;
|
||||
} else {
|
||||
msgb_put(msg, rc);
|
||||
}
|
||||
|
||||
if (flags & MSG_NOTIFICATION) {
|
||||
LOGP(DMAIN, LOGL_DEBUG, "Ignoring SCTP notification\n");
|
||||
rc = 0;
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
sinfo.sinfo_ppid = ntohl(sinfo.sinfo_ppid);
|
||||
|
||||
switch (sinfo.sinfo_ppid) {
|
||||
case IUH_PPI_HNBAP:
|
||||
LOGP(DHNBAP, LOGL_INFO, "HNBAP message received\n");
|
||||
rc = hnb_hnbap_rx(hnb, msg);
|
||||
break;
|
||||
case IUH_PPI_RUA:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA message received\n");
|
||||
rc = hnb_rua_rx(hnb, msg);
|
||||
break;
|
||||
case IUH_PPI_SABP:
|
||||
case IUH_PPI_RNA:
|
||||
case IUH_PPI_PUA:
|
||||
LOGP(DMAIN, LOGL_ERROR, "Unimplemented SCTP PPID=%u received\n",
|
||||
sinfo.sinfo_ppid);
|
||||
rc = 0;
|
||||
break;
|
||||
default:
|
||||
LOGP(DMAIN, LOGL_ERROR, "Unknown SCTP PPID=%u received\n",
|
||||
sinfo.sinfo_ppid);
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
free_ret:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnb_iuh_connect_cb(struct osmo_stream_cli *conn)
|
||||
{
|
||||
LOGP(DMAIN, LOGL_NOTICE, "Iuh connected to HNBGW\n");
|
||||
struct hnb *hnb = osmo_stream_cli_get_data(conn);
|
||||
|
||||
hnb_send_register_req(hnb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct hnb *hnb_alloc(void *tall_ctx)
|
||||
{
|
||||
struct hnb *hnb;
|
||||
struct osmo_stream_cli *cli;
|
||||
|
||||
hnb = talloc_zero(tall_ctx, struct hnb);
|
||||
if (!hnb)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&hnb->ue_list);
|
||||
|
||||
hnb->identity = talloc_strdup(hnb, "OsmoHNodeB");
|
||||
hnb->plmn = (struct osmo_plmn_id){
|
||||
.mcc = 1,
|
||||
.mnc = 1,
|
||||
};
|
||||
|
||||
hnb->iuh.local_addr = talloc_strdup(hnb, "0.0.0.0");
|
||||
hnb->iuh.local_port = 0;
|
||||
hnb->iuh.remote_addr = talloc_strdup(hnb, "127.0.0.1");
|
||||
hnb->iuh.remote_port = IUH_DEFAULT_SCTP_PORT;
|
||||
hnb->rtp.jitter_adaptive = false;
|
||||
hnb->rtp.port_range_start = 16384;
|
||||
hnb->rtp.port_range_end = 17407;
|
||||
hnb->rtp.port_range_next = hnb->rtp.port_range_start;
|
||||
hnb->rtp.ip_dscp = -1;
|
||||
hnb->rtp.priority = -1;
|
||||
|
||||
cli = osmo_stream_cli_create(hnb);
|
||||
OSMO_ASSERT(cli);
|
||||
hnb->iuh.client = cli;
|
||||
osmo_stream_cli_set_nodelay(cli, true);
|
||||
osmo_stream_cli_set_proto(cli, IPPROTO_SCTP);
|
||||
osmo_stream_cli_set_reconnect_timeout(cli, 5);
|
||||
osmo_stream_cli_set_connect_cb(cli, hnb_iuh_connect_cb);
|
||||
osmo_stream_cli_set_read_cb(cli, hnb_iuh_read_cb);
|
||||
osmo_stream_cli_set_data(cli, hnb);
|
||||
hnb->gtp.cfg_local_addr = talloc_strdup(hnb, "0.0.0.0");
|
||||
osmo_wqueue_init(&hnb->gtp.wq1u, 1024);
|
||||
hnb->gtp.wq1u.bfd.data = hnb;
|
||||
hnb->gtp.wq1u.bfd.fd = -1;
|
||||
|
||||
hnb->shutdown_fi = osmo_fsm_inst_alloc(&hnb_shutdown_fsm, hnb, hnb,
|
||||
LOGL_INFO, NULL);
|
||||
|
||||
hnb_llsk_alloc(hnb);
|
||||
|
||||
hnb_iuh_alloc(hnb);
|
||||
|
||||
return hnb;
|
||||
}
|
||||
|
||||
void hnb_free(struct hnb *hnb)
|
||||
{
|
||||
if (hnb->iuh.client) {
|
||||
osmo_stream_cli_destroy(hnb->iuh.client);
|
||||
hnb->iuh.client = NULL;
|
||||
struct hnb_ue *ue, *ue_tmp;
|
||||
|
||||
llist_for_each_entry_safe(ue, ue_tmp, &hnb->ue_list, list)
|
||||
hnb_ue_free(ue);
|
||||
|
||||
if (hnb->shutdown_fi) {
|
||||
osmo_fsm_inst_free(hnb->shutdown_fi);
|
||||
hnb->shutdown_fi = NULL;
|
||||
}
|
||||
hnb_iuh_free(hnb);
|
||||
|
||||
hnb_llsk_free(hnb);
|
||||
|
||||
hnb_gtp_unbind(hnb);
|
||||
|
||||
talloc_free(hnb);
|
||||
}
|
||||
|
||||
int hnb_connect(struct hnb *hnb)
|
||||
struct hnb_ue *hnb_ue_alloc(struct hnb *hnb, uint32_t conn_id)
|
||||
{
|
||||
int rc;
|
||||
struct hnb_ue *ue;
|
||||
|
||||
LOGP(DMAIN, LOGL_INFO, "Iuh Connect: %s[:%u] => %s[:%u]\n",
|
||||
hnb->iuh.local_addr, hnb->iuh.local_port, hnb->iuh.remote_addr, hnb->iuh.remote_port);
|
||||
ue = talloc_zero(hnb, struct hnb_ue);
|
||||
if (!ue)
|
||||
return NULL;
|
||||
|
||||
osmo_stream_cli_set_addrs(hnb->iuh.client, (const char**)&hnb->iuh.remote_addr, 1);
|
||||
osmo_stream_cli_set_port(hnb->iuh.client, hnb->iuh.remote_port);
|
||||
osmo_stream_cli_set_local_addrs(hnb->iuh.client, (const char**)&hnb->iuh.local_addr, 1);
|
||||
osmo_stream_cli_set_local_port(hnb->iuh.client, hnb->iuh.local_port);
|
||||
rc = osmo_stream_cli_open(hnb->iuh.client);
|
||||
if (rc < 0) {
|
||||
LOGP(DMAIN, LOGL_ERROR, "Unable to open stream client for Iuh %s[:%u] => %s[:%u]\n",
|
||||
hnb->iuh.local_addr, hnb->iuh.local_port, hnb->iuh.remote_addr, hnb->iuh.remote_port);
|
||||
/* we don't return error in here because osmo_stream_cli_open()
|
||||
will continue to retry (due to timeout being explicitly set with
|
||||
osmo_stream_cli_set_reconnect_timeout() above) to connect so the error is transient */
|
||||
ue->hnb = hnb;
|
||||
ue->conn_id = conn_id;
|
||||
|
||||
INIT_LLIST_HEAD(&ue->conn_cs.conn_list);
|
||||
INIT_LLIST_HEAD(&ue->conn_ps.conn_list);
|
||||
|
||||
llist_add(&ue->list, &hnb->ue_list);
|
||||
|
||||
return ue;
|
||||
}
|
||||
|
||||
void hnb_ue_free(struct hnb_ue *ue)
|
||||
{
|
||||
hnb_ue_reset_chan(ue, true);
|
||||
hnb_ue_reset_chan(ue, false);
|
||||
llist_del(&ue->list);
|
||||
talloc_free(ue);
|
||||
}
|
||||
|
||||
void hnb_ue_reset_chan(struct hnb_ue *ue, bool is_ps)
|
||||
{
|
||||
if (is_ps) {
|
||||
struct gtp_conn *conn, *conn_tmp;
|
||||
llist_for_each_entry_safe(conn, conn_tmp, &ue->conn_ps.conn_list, list)
|
||||
gtp_conn_free(conn);
|
||||
ue->conn_ps = (struct hnb_ue_ps_ctx){0};
|
||||
INIT_LLIST_HEAD(&ue->conn_ps.conn_list);
|
||||
} else {
|
||||
struct rtp_conn *conn, *conn_tmp;
|
||||
llist_for_each_entry_safe(conn, conn_tmp, &ue->conn_cs.conn_list, list)
|
||||
rtp_conn_free(conn);
|
||||
ue->conn_cs = (struct hnb_ue_cs_ctx){0};
|
||||
INIT_LLIST_HEAD(&ue->conn_cs.conn_list);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hnb_iuh_send(struct hnb *hnb, struct msgb *msg)
|
||||
struct hnb_ue *hnb_find_ue_by_id(const struct hnb *hnb, uint32_t conn_id)
|
||||
{
|
||||
osmo_stream_cli_send(hnb->iuh.client, msg);
|
||||
return 0;
|
||||
struct hnb_ue *ue;
|
||||
|
||||
llist_for_each_entry(ue, &hnb->ue_list, list) {
|
||||
if (ue->conn_id != conn_id)
|
||||
continue;
|
||||
return ue;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct hnb_ue *hnb_find_ue_by_imsi(const struct hnb *hnb, char *imsi)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
|
||||
if (!imsi || imsi[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(ue, &hnb->ue_list, list) {
|
||||
if (ue->imsi[0] == '\0')
|
||||
continue;
|
||||
if (strncmp(&ue->imsi[0], imsi, ARRAY_SIZE(ue->imsi)) != 0)
|
||||
continue;
|
||||
return ue;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct rtp_conn *hnb_find_rtp_conn_by_id(const struct hnb *hnb, uint32_t audio_conn_id)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
|
||||
llist_for_each_entry(ue, &hnb->ue_list, list) {
|
||||
struct rtp_conn *conn;
|
||||
if (!ue->conn_cs.active)
|
||||
continue;
|
||||
llist_for_each_entry(conn, &ue->conn_cs.conn_list, list) {
|
||||
if (conn->id == audio_conn_id)
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct gtp_conn *hnb_find_gtp_conn_by_id(const struct hnb *hnb, uint32_t gtp_conn_id)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
|
||||
llist_for_each_entry(ue, &hnb->ue_list, list) {
|
||||
struct gtp_conn *conn;
|
||||
if (!ue->conn_ps.active)
|
||||
continue;
|
||||
llist_for_each_entry(conn, &ue->conn_ps.conn_list, list) {
|
||||
if (conn->id == gtp_conn_id)
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
/* hNodeB shutdown FSM */
|
||||
|
||||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
|
||||
#include <osmocom/hnodeb/hnb_shutdown_fsm.h>
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/iuh.h>
|
||||
#define X(s) (1 << (s))
|
||||
|
||||
#define hnb_shutdown_fsm_state_chg(fi, NEXT_STATE) \
|
||||
osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
|
||||
|
||||
static void st_none_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)fi->priv;
|
||||
|
||||
/* Reset state: */
|
||||
hnb->registered = false;
|
||||
hnb->rnc_id = 0;
|
||||
|
||||
hnb_iuh_connect(hnb); /* Start reconnect once we are done with shutdown and we didn't exit process */
|
||||
}
|
||||
|
||||
static void st_none(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch (event) {
|
||||
case HNB_SHUTDOWN_EV_START:
|
||||
hnb_shutdown_fsm_state_chg(fi, HNB_SHUTDOWN_ST_IN_PROGRESS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void st_in_progress_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)fi->priv;
|
||||
struct hnb_ue *ue, *ue_tmp;
|
||||
|
||||
/* TODO: Also, if Iuh link is still up, maybe send a Hnb deregister req towards HNBGW */
|
||||
|
||||
/* Drop active UE contexts, together with their GTP/AUDIO sessions: */
|
||||
llist_for_each_entry_safe(ue, ue_tmp, &hnb->ue_list, list)
|
||||
hnb_ue_free(ue);
|
||||
|
||||
if (osmo_stream_cli_is_connected(hnb->iuh.client))
|
||||
osmo_stream_cli_close(hnb->iuh.client);
|
||||
|
||||
/* Close LLSK to notify lower layers that we are shutting down
|
||||
* even if we are not exiting the process. */
|
||||
if (hnb_llsk_connected(hnb))
|
||||
hnb_llsk_close_conn(hnb);
|
||||
|
||||
hnb_shutdown_fsm_state_chg(fi, HNB_SHUTDOWN_ST_EXIT);
|
||||
}
|
||||
|
||||
static void st_exit_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)fi->priv;
|
||||
/* TODO: here we may want to signal the hnb object somehow that we have completed the shut down? */
|
||||
|
||||
if (hnb->shutdown_fi_exit_proc) {
|
||||
LOGPFSML(fi, LOGL_NOTICE, "Shutdown process completed successfully, exiting process\n");
|
||||
exit(0);
|
||||
}
|
||||
hnb_shutdown_fsm_state_chg(fi, HNB_SHUTDOWN_ST_NONE);
|
||||
}
|
||||
|
||||
static struct osmo_fsm_state hnb_shutdown_fsm_states[] = {
|
||||
[HNB_SHUTDOWN_ST_NONE] = {
|
||||
.in_event_mask =
|
||||
X(HNB_SHUTDOWN_EV_START),
|
||||
.out_state_mask =
|
||||
X(HNB_SHUTDOWN_ST_IN_PROGRESS),
|
||||
.name = "NONE",
|
||||
.onenter = st_none_on_enter,
|
||||
.action = st_none,
|
||||
},
|
||||
[HNB_SHUTDOWN_ST_IN_PROGRESS] = {
|
||||
.out_state_mask =
|
||||
X(HNB_SHUTDOWN_ST_EXIT),
|
||||
.name = "IN_PROGRESS",
|
||||
.onenter = st_in_progress_on_enter,
|
||||
},
|
||||
[HNB_SHUTDOWN_ST_EXIT] = {
|
||||
.name = "EXIT",
|
||||
.out_state_mask =
|
||||
X(HNB_SHUTDOWN_ST_NONE),
|
||||
.onenter = st_exit_on_enter,
|
||||
}
|
||||
};
|
||||
|
||||
const struct value_string hnb_shutdown_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(HNB_SHUTDOWN_EV_START),
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
int hnb_shutdown_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
switch (fi->state) {
|
||||
default:
|
||||
OSMO_ASSERT(false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct osmo_fsm hnb_shutdown_fsm = {
|
||||
.name = "HNB_SHUTDOWN",
|
||||
.states = hnb_shutdown_fsm_states,
|
||||
.num_states = ARRAY_SIZE(hnb_shutdown_fsm_states),
|
||||
.event_names = hnb_shutdown_fsm_event_names,
|
||||
.log_subsys = DMAIN,
|
||||
.timer_cb = hnb_shutdown_fsm_timer_cb,
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void hnb_shutdown_fsm_init(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&hnb_shutdown_fsm) == 0);
|
||||
}
|
||||
|
||||
bool hnb_shutdown_in_progress(const struct hnb *hnb)
|
||||
{
|
||||
const struct osmo_fsm_inst *fi = hnb->shutdown_fi;
|
||||
return fi->state != HNB_SHUTDOWN_ST_NONE;
|
||||
}
|
||||
|
||||
void hnb_shutdown(struct hnb *hnb, const char *reason, bool exit_proc)
|
||||
{
|
||||
struct osmo_fsm_inst *fi = hnb->shutdown_fi;
|
||||
if (hnb_shutdown_in_progress(hnb)) {
|
||||
LOGPFSML(fi, LOGL_NOTICE, "hNodeB is already being shutdown.\n");
|
||||
if (exit_proc)
|
||||
hnb->shutdown_fi_exit_proc = true;
|
||||
return;
|
||||
}
|
||||
hnb->shutdown_fi_exit_proc = exit_proc;
|
||||
LOGPFSML(fi, LOGL_NOTICE, "Shutting down hNodeB, exit %u, reason: %s\n",
|
||||
exit_proc, reason);
|
||||
osmo_fsm_inst_dispatch(fi, HNB_SHUTDOWN_EV_START, NULL);
|
||||
}
|
|
@ -32,6 +32,10 @@
|
|||
|
||||
#include <osmocom/hnodeb/hnbap.h>
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/iuh.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
#include <osmocom/hnodeb/hnb_shutdown_fsm.h>
|
||||
#include <osmocom/hnodeb/llsk.h>
|
||||
|
||||
static int hnb_rx_hnb_register_acc(struct hnb *hnb, ANY_t *in)
|
||||
{
|
||||
|
@ -40,12 +44,39 @@ static int hnb_rx_hnb_register_acc(struct hnb *hnb, ANY_t *in)
|
|||
|
||||
rc = hnbap_decode_hnbregisteraccepties(&accept, in);
|
||||
if (rc < 0) {
|
||||
hnb_shutdown(hnb, "Failed decoding HnbRegisterAccept IEs", false);
|
||||
return rc;
|
||||
}
|
||||
|
||||
hnb->rnc_id = accept.rnc_id;
|
||||
LOGP(DHNBAP, LOGL_INFO, "HNB Register accept with RNC ID %u\n", hnb->rnc_id);
|
||||
hnb->registered = true;
|
||||
LOGP(DHNBAP, LOGL_INFO, "Rx HNB Register accept with RNC ID %u\n", hnb->rnc_id);
|
||||
|
||||
hnbap_free_hnbregisteraccepties(&accept);
|
||||
|
||||
if (hnb_llsk_can_be_configured(hnb))
|
||||
llsk_iuh_tx_configure_ind(hnb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnb_rx_hnb_register_rej(struct hnb *hnb, ANY_t *in)
|
||||
{
|
||||
int rc;
|
||||
HNBAP_HNBRegisterRejectIEs_t reject;
|
||||
|
||||
hnb->registered = false;
|
||||
|
||||
rc = hnbap_decode_hnbregisterrejecties(&reject, in);
|
||||
if (rc < 0) {
|
||||
LOGP(DHNBAP, LOGL_NOTICE, "Rx HNB Register Reject: parse failure\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOGP(DHNBAP, LOGL_NOTICE, "Rx HNB Register Reject with cause %s\n",
|
||||
hnbap_cause_str(&reject.cause));
|
||||
hnbap_free_hnbregisterrejecties(&reject);
|
||||
|
||||
hnb_shutdown(hnb, "Rx HNB Register Reject", false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -78,6 +109,53 @@ static int hnb_rx_ue_register_acc(struct hnb *hnb, ANY_t *in)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int hnb_hnbap_rx_initiating(struct hnb *hnb, struct HNBAP_InitiatingMessage *init)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (init->procedureCode) {
|
||||
default:
|
||||
LOGP(DHNBAP, LOGL_ERROR, "Rx HNBAP initiatingMessage %ld unsupported\n", init->procedureCode);
|
||||
rc = -ENOSPC;
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnb_hnbap_rx_successful(struct hnb *hnb, struct HNBAP_SuccessfulOutcome *succ)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (succ->procedureCode) {
|
||||
case HNBAP_ProcedureCode_id_HNBRegister:
|
||||
/* Get HNB id and send UE Register request */
|
||||
rc = hnb_rx_hnb_register_acc(hnb, &succ->value);
|
||||
break;
|
||||
case HNBAP_ProcedureCode_id_UERegister:
|
||||
rc = hnb_rx_ue_register_acc(hnb, &succ->value);
|
||||
break;
|
||||
default:
|
||||
rc = -ENOSPC;
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnb_hnbap_rx_unsuccessful(struct hnb *hnb, struct HNBAP_UnsuccessfulOutcome *unsucc)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (unsucc->procedureCode) {
|
||||
case HNBAP_ProcedureCode_id_HNBRegister:
|
||||
rc = hnb_rx_hnb_register_rej(hnb, &unsucc->value);
|
||||
break;
|
||||
default:
|
||||
rc = -ENOSPC;
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int hnb_hnbap_rx(struct hnb *hnb, struct msgb *msg)
|
||||
{
|
||||
HNBAP_HNBAP_PDU_t _pdu, *pdu = &_pdu;
|
||||
|
@ -92,21 +170,20 @@ int hnb_hnbap_rx(struct hnb *hnb, struct msgb *msg)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pdu->present != HNBAP_HNBAP_PDU_PR_successfulOutcome) {
|
||||
LOGP(DHNBAP, LOGL_ERROR, "Unexpected HNBAP message received\n");
|
||||
}
|
||||
|
||||
switch (pdu->choice.successfulOutcome.procedureCode) {
|
||||
case HNBAP_ProcedureCode_id_HNBRegister:
|
||||
/* Get HNB id and send UE Register request */
|
||||
rc = hnb_rx_hnb_register_acc(hnb, &pdu->choice.successfulOutcome.value);
|
||||
switch (pdu->present) {
|
||||
case HNBAP_HNBAP_PDU_PR_initiatingMessage:
|
||||
rc = hnb_hnbap_rx_initiating(hnb, &pdu->choice.initiatingMessage);
|
||||
break;
|
||||
case HNBAP_ProcedureCode_id_UERegister:
|
||||
rc = hnb_rx_ue_register_acc(hnb, &pdu->choice.successfulOutcome.value);
|
||||
case HNBAP_HNBAP_PDU_PR_successfulOutcome:
|
||||
rc = hnb_hnbap_rx_successful(hnb, &pdu->choice.successfulOutcome);
|
||||
break;
|
||||
case HNBAP_HNBAP_PDU_PR_unsuccessfulOutcome:
|
||||
rc = hnb_hnbap_rx_unsuccessful(hnb, &pdu->choice.unsuccessfulOutcome);
|
||||
break;
|
||||
case HNBAP_HNBAP_PDU_PR_NOTHING: /* No components present */
|
||||
default:
|
||||
LOGP(DHNBAP, LOGL_ERROR, "Unexpected HNBAP message received\n");
|
||||
rc = -ENOSPC;
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
/* (C) 2015 by Daniel Willmann <dwillmann@sysmocom.de>
|
||||
* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/sctp.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/netif/stream.h>
|
||||
#include <osmocom/netif/sctp.h>
|
||||
|
||||
#include <osmocom/hnodeb/iuh.h>
|
||||
#include <osmocom/hnodeb/hnbap.h>
|
||||
#include <osmocom/hnodeb/rua.h>
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/hnb_shutdown_fsm.h>
|
||||
|
||||
static int get_logevel_by_sn_type(int sn_type)
|
||||
{
|
||||
switch (sn_type) {
|
||||
case SCTP_ADAPTATION_INDICATION:
|
||||
case SCTP_PEER_ADDR_CHANGE:
|
||||
#ifdef SCTP_AUTHENTICATION_INDICATION
|
||||
case SCTP_AUTHENTICATION_INDICATION:
|
||||
#endif
|
||||
#ifdef SCTP_SENDER_DRY_EVENT
|
||||
case SCTP_SENDER_DRY_EVENT:
|
||||
#endif
|
||||
return LOGL_INFO;
|
||||
case SCTP_ASSOC_CHANGE:
|
||||
return LOGL_NOTICE;
|
||||
case SCTP_SHUTDOWN_EVENT:
|
||||
case SCTP_PARTIAL_DELIVERY_EVENT:
|
||||
return LOGL_NOTICE;
|
||||
case SCTP_SEND_FAILED:
|
||||
case SCTP_REMOTE_ERROR:
|
||||
return LOGL_ERROR;
|
||||
default:
|
||||
return LOGL_NOTICE;
|
||||
}
|
||||
}
|
||||
|
||||
static void log_sctp_notification(union sctp_notification *notif)
|
||||
{
|
||||
int log_level;
|
||||
|
||||
LOGP(DSCTP, LOGL_INFO, "Iuh SCTP NOTIFICATION %u flags=0x%0x\n",
|
||||
notif->sn_header.sn_type, notif->sn_header.sn_flags);
|
||||
|
||||
log_level = get_logevel_by_sn_type(notif->sn_header.sn_type);
|
||||
|
||||
switch (notif->sn_header.sn_type) {
|
||||
case SCTP_ASSOC_CHANGE:
|
||||
LOGP(DSCTP, log_level, "Iuh SCTP_ASSOC_CHANGE: %s\n",
|
||||
osmo_sctp_assoc_chg_str(notif->sn_assoc_change.sac_state));
|
||||
break;
|
||||
default:
|
||||
LOGP(DSCTP, log_level, "Iuh %s\n",
|
||||
osmo_sctp_sn_type_str(notif->sn_header.sn_type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int hnb_iuh_read_cb(struct osmo_stream_cli *conn)
|
||||
{
|
||||
struct osmo_fd *fd = osmo_stream_cli_get_ofd(conn);
|
||||
struct hnb *hnb = osmo_stream_cli_get_data(conn);
|
||||
struct sctp_sndrcvinfo sinfo;
|
||||
struct msgb *msg = msgb_alloc(IUH_MSGB_SIZE, "Iuh rx");
|
||||
int flags = 0;
|
||||
int rc;
|
||||
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = sctp_recvmsg(fd->fd, msgb_data(msg), msgb_tailroom(msg),
|
||||
NULL, NULL, &sinfo, &flags);
|
||||
if (rc < 0) {
|
||||
LOGP(DSCTP, LOGL_ERROR, "Error during sctp_recvmsg()\n");
|
||||
osmo_stream_cli_close(conn);
|
||||
hnb_shutdown(hnb, "sctp_recvmsg() error", false);
|
||||
goto free_ret;
|
||||
} else if (rc == 0) {
|
||||
LOGP(DSCTP, LOGL_INFO, "Connection to HNBGW closed\n");
|
||||
osmo_stream_cli_close(conn);
|
||||
hnb_shutdown(hnb, "Iuh HNBGW conn closed", false);
|
||||
rc = -1;
|
||||
goto free_ret;
|
||||
} else {
|
||||
msgb_put(msg, rc);
|
||||
}
|
||||
|
||||
if (flags & MSG_NOTIFICATION) {
|
||||
union sctp_notification *notif = (union sctp_notification *) msgb_data(msg);
|
||||
log_sctp_notification(notif);
|
||||
switch (notif->sn_header.sn_type) {
|
||||
case SCTP_SHUTDOWN_EVENT:
|
||||
osmo_fsm_inst_dispatch(hnb->shutdown_fi, HNB_SHUTDOWN_EV_START, NULL);
|
||||
hnb_shutdown(hnb, "Iuh HNBGW conn notification (SCTP_SHUTDOWN_EVENT)", false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
rc = 0;
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
sinfo.sinfo_ppid = ntohl(sinfo.sinfo_ppid);
|
||||
|
||||
switch (sinfo.sinfo_ppid) {
|
||||
case IUH_PPI_HNBAP:
|
||||
LOGP(DHNBAP, LOGL_INFO, "HNBAP message received\n");
|
||||
rc = hnb_hnbap_rx(hnb, msg);
|
||||
break;
|
||||
case IUH_PPI_RUA:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA message received\n");
|
||||
rc = hnb_rua_rx(hnb, msg);
|
||||
break;
|
||||
case IUH_PPI_SABP:
|
||||
case IUH_PPI_RNA:
|
||||
case IUH_PPI_PUA:
|
||||
LOGP(DSCTP, LOGL_ERROR, "Unimplemented SCTP PPID=%u received\n",
|
||||
sinfo.sinfo_ppid);
|
||||
rc = 0;
|
||||
break;
|
||||
default:
|
||||
LOGP(DSCTP, LOGL_ERROR, "Unknown SCTP PPID=%u received\n",
|
||||
sinfo.sinfo_ppid);
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
free_ret:
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hnb_iuh_connect_cb(struct osmo_stream_cli *conn)
|
||||
{
|
||||
LOGP(DSCTP, LOGL_NOTICE, "Iuh connected to HNBGW\n");
|
||||
struct hnb *hnb = osmo_stream_cli_get_data(conn);
|
||||
|
||||
hnb_send_register_req(hnb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hnb_iuh_alloc(struct hnb *hnb)
|
||||
{
|
||||
struct osmo_stream_cli *cli;
|
||||
|
||||
hnb->iuh.local_addr = talloc_strdup(hnb, "0.0.0.0");
|
||||
hnb->iuh.local_port = 0;
|
||||
hnb->iuh.remote_addr = talloc_strdup(hnb, "127.0.0.1");
|
||||
hnb->iuh.remote_port = IUH_DEFAULT_SCTP_PORT;
|
||||
|
||||
cli = osmo_stream_cli_create(hnb);
|
||||
OSMO_ASSERT(cli);
|
||||
hnb->iuh.client = cli;
|
||||
osmo_stream_cli_set_name(cli, "Iuh");
|
||||
osmo_stream_cli_set_nodelay(cli, true);
|
||||
osmo_stream_cli_set_proto(cli, IPPROTO_SCTP);
|
||||
osmo_stream_cli_set_reconnect_timeout(cli, 5);
|
||||
osmo_stream_cli_set_connect_cb(cli, hnb_iuh_connect_cb);
|
||||
osmo_stream_cli_set_read_cb(cli, hnb_iuh_read_cb);
|
||||
osmo_stream_cli_set_data(cli, hnb);
|
||||
}
|
||||
|
||||
void hnb_iuh_free(struct hnb *hnb)
|
||||
{
|
||||
if (!hnb->iuh.client)
|
||||
return;
|
||||
osmo_stream_cli_destroy(hnb->iuh.client);
|
||||
hnb->iuh.client = NULL;
|
||||
}
|
||||
|
||||
int hnb_iuh_connect(struct hnb *hnb)
|
||||
{
|
||||
int rc;
|
||||
|
||||
LOGP(DSCTP, LOGL_INFO, "Iuh Connect: %s[:%u] => %s[:%u]\n",
|
||||
hnb->iuh.local_addr, hnb->iuh.local_port, hnb->iuh.remote_addr, hnb->iuh.remote_port);
|
||||
|
||||
osmo_stream_cli_set_addrs(hnb->iuh.client, (const char **)&hnb->iuh.remote_addr, 1);
|
||||
osmo_stream_cli_set_port(hnb->iuh.client, hnb->iuh.remote_port);
|
||||
osmo_stream_cli_set_local_addrs(hnb->iuh.client, (const char **)&hnb->iuh.local_addr, 1);
|
||||
osmo_stream_cli_set_local_port(hnb->iuh.client, hnb->iuh.local_port);
|
||||
rc = osmo_stream_cli_open(hnb->iuh.client);
|
||||
if (rc < 0) {
|
||||
LOGP(DSCTP, LOGL_ERROR, "Unable to open stream client for Iuh %s[:%u] => %s[:%u]\n",
|
||||
hnb->iuh.local_addr, hnb->iuh.local_port, hnb->iuh.remote_addr, hnb->iuh.remote_port);
|
||||
/* we don't return error in here because osmo_stream_cli_open()
|
||||
will continue to retry (due to timeout being explicitly set with
|
||||
osmo_stream_cli_set_reconnect_timeout() above) to connect so the error is transient */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hnb_iuh_send(struct hnb *hnb, struct msgb *msg)
|
||||
{
|
||||
osmo_stream_cli_send(hnb->iuh.client, msg);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <inttypes.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/prim.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/llsk.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
#include <osmocom/hnodeb/hnb_shutdown_fsm.h>
|
||||
|
||||
int ll_addr_type2af(enum u_addr_type t)
|
||||
{
|
||||
switch (t) {
|
||||
case HNB_PRIM_ADDR_TYPE_IPV4:
|
||||
return AF_INET;
|
||||
case HNB_PRIM_ADDR_TYPE_IPV6:
|
||||
return AF_INET6;
|
||||
default:
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx unknown address type %u\n", (unsigned)t);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int ll_addr2osa(enum u_addr_type t, const union u_addr *uaddr, uint16_t port, struct osmo_sockaddr *osa)
|
||||
{
|
||||
int af = ll_addr_type2af(t);
|
||||
|
||||
osa->u.sa.sa_family = af;
|
||||
|
||||
switch (af) {
|
||||
case AF_INET6:
|
||||
memcpy(&osa->u.sin6.sin6_addr, &uaddr->v6, sizeof(osa->u.sin6.sin6_addr));
|
||||
osa->u.sin6.sin6_port = htons(port);
|
||||
break;
|
||||
case AF_INET:
|
||||
memcpy(&osa->u.sin.sin_addr, &uaddr->v4, sizeof(osa->u.sin.sin_addr));
|
||||
osa->u.sin.sin_port = htons(port);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum u_addr_type osa2_ll_addr(const struct osmo_sockaddr *osa, union u_addr *uaddr, uint16_t *port)
|
||||
{
|
||||
switch (osa->u.sa.sa_family) {
|
||||
case AF_INET6:
|
||||
memcpy(&uaddr->v6, &osa->u.sin6.sin6_addr, sizeof(osa->u.sin6.sin6_addr));
|
||||
if (port)
|
||||
*port = ntohs(osa->u.sin6.sin6_port);
|
||||
return HNB_PRIM_ADDR_TYPE_IPV6;
|
||||
case AF_INET:
|
||||
memcpy(&uaddr->v4, &osa->u.sin.sin_addr, sizeof(osa->u.sin.sin_addr));
|
||||
if (port)
|
||||
*port = ntohs(osa->u.sin.sin_port);
|
||||
return HNB_PRIM_ADDR_TYPE_IPV4;
|
||||
default:
|
||||
return HNB_PRIM_ADDR_TYPE_UNSPEC;
|
||||
}
|
||||
}
|
||||
|
||||
static int llsk_opened_cb(struct osmo_prim_srv *srv)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)osmo_prim_srv_get_priv(srv);
|
||||
osmo_prim_srv_set_name(srv, "llsk");
|
||||
|
||||
if (hnb->llsk.srv) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "New connection opened while one is already active, dropping it\n");
|
||||
osmo_prim_srv_close(srv);
|
||||
return 0;
|
||||
}
|
||||
LOGP(DLLSK, LOGL_NOTICE, "LLSK conn is UP\n");
|
||||
hnb->llsk.srv = srv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int llsk_closed_cb(struct osmo_prim_srv *srv)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)osmo_prim_srv_get_priv(srv);
|
||||
|
||||
if (!hnb->llsk.srv) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "closed_cb received but we have no active llsk conn!\n");
|
||||
return 0;
|
||||
}
|
||||
/* If a later conn different than active one is dropped (because we closed it): */
|
||||
if (hnb->llsk.srv != srv)
|
||||
return 0;
|
||||
LOGP(DLLSK, LOGL_NOTICE, "LLSK conn is DOWN\n");
|
||||
|
||||
hnb->llsk.srv = NULL;
|
||||
hnb->llsk.valid_sapi_mask = 0x0;
|
||||
osmo_timer_del(&hnb->llsk.defer_configure_ind_timer);
|
||||
|
||||
/* We actively close the llsk conn during hnb_shutdown, no need to
|
||||
* re-enter shutdown procedure thin that case: */
|
||||
if (!hnb_shutdown_in_progress(hnb))
|
||||
hnb_shutdown(hnb, "LLSK conn dropped", false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool hnb_llsk_connected(const struct hnb *hnb)
|
||||
{
|
||||
return !!hnb->llsk.srv;
|
||||
}
|
||||
|
||||
bool hnb_llsk_can_be_configured(struct hnb *hnb)
|
||||
{
|
||||
if (!hnb->registered)
|
||||
return false;
|
||||
if (!hnb->llsk.srv)
|
||||
return false;
|
||||
|
||||
if (hnb->llsk.valid_sapi_mask & (1 << HNB_PRIM_SAPI_IUH) &&
|
||||
hnb->llsk.valid_sapi_mask & (1 << HNB_PRIM_SAPI_AUDIO) &&
|
||||
hnb->llsk.valid_sapi_mask & (1 << HNB_PRIM_SAPI_GTP))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void hnb_llsk_close_conn(const struct hnb *hnb)
|
||||
{
|
||||
if (!hnb_llsk_connected(hnb))
|
||||
return;
|
||||
osmo_prim_srv_close(hnb->llsk.srv);
|
||||
/* pointer NULLed in llsk_closed_cb() */
|
||||
}
|
||||
|
||||
static void llsk_defer_configure_ind_timer_cb(void *data)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)data;
|
||||
llsk_iuh_tx_configure_ind(hnb);
|
||||
}
|
||||
|
||||
static int llsk_rx_sapi_version_cb(struct osmo_prim_srv *prim_srv, uint32_t sapi, uint16_t rem_version)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)osmo_prim_srv_get_priv(prim_srv);
|
||||
if (sapi > sizeof(hnb->llsk.valid_sapi_mask)*8 - 1)
|
||||
return -1;
|
||||
|
||||
switch (sapi) {
|
||||
case HNB_PRIM_SAPI_IUH:
|
||||
if (rem_version < LLSK_SAPI_IUH_VERSION_MIN)
|
||||
return -1;
|
||||
if (rem_version > LLSK_SAPI_IUH_VERSION_MAX)
|
||||
return LLSK_SAPI_IUH_VERSION_MAX;
|
||||
hnb->llsk.sapi_version_iuh = rem_version;
|
||||
break;
|
||||
case HNB_PRIM_SAPI_GTP:
|
||||
if (rem_version < LLSK_SAPI_GTP_VERSION_MIN)
|
||||
return -1;
|
||||
if (rem_version > LLSK_SAPI_GTP_VERSION_MAX)
|
||||
return LLSK_SAPI_GTP_VERSION_MAX;
|
||||
hnb->llsk.sapi_version_gtp = rem_version;
|
||||
break;
|
||||
case HNB_PRIM_SAPI_AUDIO:
|
||||
if (rem_version < LLSK_SAPI_AUDIO_VERSION_MIN)
|
||||
return -1;
|
||||
if (rem_version > LLSK_SAPI_AUDIO_VERSION_MAX)
|
||||
return LLSK_SAPI_AUDIO_VERSION_MAX;
|
||||
if (llsk_audio_sapi_version_confirmed(rem_version) < 0)
|
||||
return -1;
|
||||
hnb->llsk.sapi_version_audio = rem_version;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
hnb->llsk.valid_sapi_mask |= (1 << sapi);
|
||||
|
||||
/* Defer CONFIGURE.req after we have confirmed the versions */
|
||||
if (hnb_llsk_can_be_configured(hnb))
|
||||
osmo_timer_schedule(&hnb->llsk.defer_configure_ind_timer, 0, 0);
|
||||
|
||||
return rem_version;
|
||||
}
|
||||
|
||||
static int llsk_rx_cb(struct osmo_prim_srv *srv, struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)osmo_prim_srv_get_priv(srv);
|
||||
LOGP(DLLSK, LOGL_DEBUG, "llsk_rx_cb() SAP=%u (%u bytes): %s\n", oph->sap,
|
||||
msgb_length(oph->msg), osmo_hexdump(msgb_data(oph->msg), msgb_length(oph->msg)));
|
||||
|
||||
switch (oph->sap) {
|
||||
case HNB_PRIM_SAPI_IUH:
|
||||
return llsk_rx_iuh(hnb, oph);
|
||||
case HNB_PRIM_SAPI_GTP:
|
||||
return llsk_rx_gtp(hnb, oph);
|
||||
case HNB_PRIM_SAPI_AUDIO:
|
||||
return llsk_rx_audio(hnb, oph);
|
||||
default:
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx msg for unknown SAPI %u (len=%u)\n",
|
||||
oph->sap, msgb_length(oph->msg));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int hnb_llsk_alloc(struct hnb *hnb)
|
||||
{
|
||||
hnb->llsk.link = osmo_prim_srv_link_alloc(hnb);
|
||||
osmo_prim_srv_link_set_priv(hnb->llsk.link, hnb);
|
||||
osmo_prim_srv_link_set_name(hnb->llsk.link, "llsk-link");
|
||||
osmo_prim_srv_link_set_log_category(hnb->llsk.link, DLLSK);
|
||||
osmo_prim_srv_link_set_addr(hnb->llsk.link, HNB_PRIM_UD_SOCK_DEFAULT);
|
||||
osmo_prim_srv_link_set_opened_conn_cb(hnb->llsk.link, llsk_opened_cb);
|
||||
osmo_prim_srv_link_set_closed_conn_cb(hnb->llsk.link, llsk_closed_cb);
|
||||
osmo_prim_srv_link_set_rx_sapi_version_cb(hnb->llsk.link, llsk_rx_sapi_version_cb);
|
||||
osmo_prim_srv_link_set_rx_cb(hnb->llsk.link, llsk_rx_cb);
|
||||
osmo_timer_setup(&hnb->llsk.defer_configure_ind_timer, llsk_defer_configure_ind_timer_cb, hnb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hnb_llsk_free(struct hnb *hnb)
|
||||
{
|
||||
osmo_timer_del(&hnb->llsk.defer_configure_ind_timer);
|
||||
osmo_prim_srv_link_free(hnb->llsk.link);
|
||||
hnb->llsk.link = NULL;
|
||||
}
|
||||
|
||||
int hnb_llsk_start_listen(struct hnb *hnb)
|
||||
{
|
||||
return osmo_prim_srv_link_open(g_hnb->llsk.link);
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <inttypes.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/trau/osmo_ortp.h>
|
||||
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/llsk.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
#include <osmocom/hnodeb/rtp.h>
|
||||
|
||||
static size_t llsk_audio_prim_size_tbl[4][_HNB_AUDIO_PRIM_MAX] = {
|
||||
[PRIM_OP_REQUEST] = {
|
||||
[HNB_AUDIO_PRIM_CONN_ESTABLISH] = sizeof(struct hnb_audio_conn_establish_req_param),
|
||||
[HNB_AUDIO_PRIM_CONN_RELEASE] = sizeof(struct hnb_audio_conn_release_req_param),
|
||||
[HNB_AUDIO_PRIM_CONN_DATA] = sizeof(struct hnb_audio_conn_data_req_param),
|
||||
},
|
||||
[PRIM_OP_RESPONSE] = {},
|
||||
[PRIM_OP_INDICATION] = {
|
||||
[HNB_AUDIO_PRIM_CONN_DATA] = sizeof(struct hnb_audio_conn_data_ind_param),
|
||||
},
|
||||
[PRIM_OP_CONFIRM] = {
|
||||
[HNB_AUDIO_PRIM_CONN_ESTABLISH] = sizeof(struct hnb_audio_conn_establish_cnf_param),
|
||||
},
|
||||
};
|
||||
static inline size_t llsk_audio_prim_size(enum hnb_audio_prim_type ptype, enum osmo_prim_operation op)
|
||||
{
|
||||
size_t val = llsk_audio_prim_size_tbl[op][ptype];
|
||||
if (val == 0) {
|
||||
LOGP(DLLSK, LOGL_FATAL, "Expected prim_size != 0 for ptype=%u op=%u\n", ptype, op);
|
||||
osmo_panic("Expected prim_size != 0 for ptype=%u op=%u\n", ptype, op);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
const struct value_string hnb_audio_prim_type_names[] = {
|
||||
OSMO_VALUE_STRING(HNB_AUDIO_PRIM_CONN_ESTABLISH),
|
||||
OSMO_VALUE_STRING(HNB_AUDIO_PRIM_CONN_RELEASE),
|
||||
OSMO_VALUE_STRING(HNB_AUDIO_PRIM_CONN_DATA),
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
int llsk_audio_sapi_version_confirmed(uint16_t sapi_version)
|
||||
{
|
||||
/* Update primitive size expectancies based on SAPI version: */
|
||||
switch (sapi_version) {
|
||||
case 0:
|
||||
llsk_audio_prim_size_tbl[PRIM_OP_REQUEST][HNB_AUDIO_PRIM_CONN_ESTABLISH] =
|
||||
sizeof(struct hnb_audio_conn_establish_req_param_v0);
|
||||
break;
|
||||
case 1:
|
||||
llsk_audio_prim_size_tbl[PRIM_OP_REQUEST][HNB_AUDIO_PRIM_CONN_ESTABLISH] =
|
||||
sizeof(struct hnb_audio_conn_establish_req_param_v1);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hnb_audio_prim *hnb_audio_prim_alloc(enum hnb_audio_prim_type ptype, enum osmo_prim_operation op, size_t extra_len)
|
||||
{
|
||||
struct osmo_prim_hdr *oph;
|
||||
size_t len = llsk_audio_prim_size(ptype, op);
|
||||
|
||||
oph = osmo_prim_msgb_alloc(HNB_PRIM_SAPI_AUDIO, ptype, op, sizeof(*oph) + len + extra_len);
|
||||
if (!oph)
|
||||
return NULL;
|
||||
msgb_put(oph->msg, len);
|
||||
|
||||
return (struct hnb_audio_prim *)oph;
|
||||
}
|
||||
|
||||
static struct hnb_audio_prim *hnb_audio_makeprim_conn_establish_cnf(uint32_t context_id, uint32_t audio_conn_id,
|
||||
uint8_t error_code, uint16_t local_rtp_port,
|
||||
uint8_t local_rtp_address_type,
|
||||
const union u_addr *local_rtp_addr)
|
||||
{
|
||||
struct hnb_audio_prim *audio_prim;
|
||||
|
||||
audio_prim = hnb_audio_prim_alloc(HNB_AUDIO_PRIM_CONN_ESTABLISH, PRIM_OP_CONFIRM, 0);
|
||||
audio_prim->u.conn_establish_cnf.context_id = context_id;
|
||||
audio_prim->u.conn_establish_cnf.audio_conn_id = audio_conn_id;
|
||||
audio_prim->u.conn_establish_cnf.local_rtp_port = local_rtp_port;
|
||||
audio_prim->u.conn_establish_cnf.error_code = error_code;
|
||||
audio_prim->u.conn_establish_cnf.local_rtp_address_type = local_rtp_address_type;
|
||||
if (local_rtp_addr)
|
||||
audio_prim->u.conn_establish_cnf.local_rtp_addr = *local_rtp_addr;
|
||||
|
||||
return audio_prim;
|
||||
}
|
||||
|
||||
static struct hnb_audio_prim *hnb_audio_makeprim_conn_data_ind(uint32_t audio_conn_id,
|
||||
uint8_t frame_nr, uint8_t fqc, uint8_t rfci,
|
||||
const uint8_t *data, uint32_t data_len)
|
||||
{
|
||||
struct hnb_audio_prim *audio_prim;
|
||||
|
||||
audio_prim = hnb_audio_prim_alloc(HNB_AUDIO_PRIM_CONN_DATA, PRIM_OP_INDICATION, data_len);
|
||||
audio_prim->u.conn_data_ind.audio_conn_id = audio_conn_id;
|
||||
audio_prim->u.conn_data_ind.frame_nr = frame_nr;
|
||||
audio_prim->u.conn_data_ind.fqc = fqc;
|
||||
audio_prim->u.conn_data_ind.rfci = rfci;
|
||||
audio_prim->u.conn_data_ind.data_len = data_len;
|
||||
if (data_len) {
|
||||
msgb_put(audio_prim->hdr.msg, data_len);
|
||||
memcpy(audio_prim->u.conn_data_ind.data, data, data_len);
|
||||
}
|
||||
|
||||
return audio_prim;
|
||||
}
|
||||
|
||||
int llsk_audio_tx_conn_data_ind(struct rtp_conn *conn, uint8_t frame_nr, uint8_t fqc, uint8_t rfci,
|
||||
const uint8_t *payload, uint32_t len)
|
||||
{
|
||||
struct hnb_audio_prim *audio_prim;
|
||||
int rc;
|
||||
|
||||
LOGUE(conn->ue, DLLSK, LOGL_DEBUG, "Tx AUDIO-CONN_DATA.ind conn_id=%u fn=%u fqc=%u rfci=%u data_len=%u\n",
|
||||
conn->id, frame_nr, fqc, rfci, len);
|
||||
audio_prim = hnb_audio_makeprim_conn_data_ind(conn->id, frame_nr, fqc, rfci, payload, len);
|
||||
if ((rc = osmo_prim_srv_send(conn->ue->hnb->llsk.srv, audio_prim->hdr.msg)) < 0)
|
||||
LOGUE(conn->ue, DLLSK, LOGL_ERROR, "Failed sending AUDIO-CONN_DATA.ind\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int _send_conn_establish_cnf_failed(struct hnb *hnb, uint32_t context_id, uint8_t error_code)
|
||||
{
|
||||
struct hnb_audio_prim *audio_prim;
|
||||
int rc;
|
||||
LOGP(DLLSK, LOGL_ERROR, "Tx AUDIO-CONN_ESTABLISH.cnf: ctx=%u error_code=%u\n",
|
||||
context_id, error_code);
|
||||
audio_prim = hnb_audio_makeprim_conn_establish_cnf(context_id, 0, error_code, 0, HNB_PRIM_ADDR_TYPE_UNSPEC, NULL);
|
||||
if ((rc = osmo_prim_srv_send(hnb->llsk.srv, audio_prim->hdr.msg)) < 0) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Failed sending AUDIO-CONN_ESTABLISH.cnf context_id=%u error_code=%u\n",
|
||||
context_id, error_code);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int llsk_rx_audio_conn_establish_req(struct hnb *hnb, struct hnb_audio_conn_establish_req_param *ce_req)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
int rc = 0;
|
||||
struct hnb_audio_prim *audio_prim;
|
||||
int af;
|
||||
char rem_addrstr[INET6_ADDRSTRLEN+32];
|
||||
struct osmo_sockaddr rem_osa = {0};
|
||||
union u_addr loc_uaddr = {0};
|
||||
uint16_t loc_port = 0;
|
||||
struct rtp_conn *conn = NULL;
|
||||
struct hnb_audio_conn_establish_req_param_v0 *v0 = &ce_req->v0;
|
||||
|
||||
rc = ll_addr2osa(v0->remote_rtp_address_type, &v0->remote_rtp_addr, v0->remote_rtp_port, &rem_osa);
|
||||
if (rc < 0) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_ESTABLISH.req: ctx=%u with unexpected address type %u\n",
|
||||
v0->context_id, v0->remote_rtp_address_type);
|
||||
return _send_conn_establish_cnf_failed(hnb, v0->context_id, 1);
|
||||
}
|
||||
osmo_sockaddr_to_str_buf(rem_addrstr, sizeof(rem_addrstr), &rem_osa);
|
||||
|
||||
LOGP(DLLSK, LOGL_INFO, "Rx AUDIO-CONN_ESTABLISH.req ctx=%u rem_addr=%s\n",
|
||||
v0->context_id, rem_addrstr);
|
||||
|
||||
if ((af = ll_addr_type2af(v0->remote_rtp_address_type)) < 0) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_ESTABLISH.req: ctx=%u with unexpected address type %u\n",
|
||||
v0->context_id, v0->remote_rtp_address_type);
|
||||
return _send_conn_establish_cnf_failed(hnb, v0->context_id, 1);
|
||||
}
|
||||
|
||||
ue = hnb_find_ue_by_id(hnb, v0->context_id);
|
||||
if (!ue) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_ESTABLISH.req: UE not found! ctx=%u rem_addr=%s\n",
|
||||
v0->context_id, rem_addrstr);
|
||||
return _send_conn_establish_cnf_failed(hnb, v0->context_id, 2);
|
||||
}
|
||||
if (!ue->conn_cs.active) {
|
||||
LOGUE(ue, DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_ESTABLISH.req: CS chan not active! rem_addr=%s\n",
|
||||
rem_addrstr);
|
||||
return _send_conn_establish_cnf_failed(hnb, v0->context_id, 3);
|
||||
}
|
||||
|
||||
/* Create the socket: */
|
||||
conn = rtp_conn_alloc(ue);
|
||||
if ((rc = rtp_conn_setup(conn, hnb->iuh.local_addr, &rem_osa, ce_req)) < 0) {
|
||||
LOGUE(ue, DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_ESTABLISH.req: Failed to set up audio socket rem_addr=%s\n",
|
||||
rem_addrstr);
|
||||
return _send_conn_establish_cnf_failed(hnb, v0->context_id, 4);
|
||||
}
|
||||
|
||||
/* Convert resulting local address back to LLSK format: */
|
||||
if (osa2_ll_addr(&conn->loc_addr, &loc_uaddr, &loc_port) != v0->remote_rtp_address_type) {
|
||||
LOGUE(ue, DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_ESTABLISH.req: Failed to provide proper local address rem_addr=%s\n",
|
||||
rem_addrstr);
|
||||
rc = _send_conn_establish_cnf_failed(hnb, v0->context_id, 4);
|
||||
goto release_sock;
|
||||
}
|
||||
|
||||
/* Submit successful confirmation */
|
||||
LOGUE(ue, DLLSK, LOGL_INFO, "Tx AUDIO-CONN_ESTABLISH.cnf: error_code=0 rem_addr=%s loc_addr=%s\n",
|
||||
rem_addrstr, osmo_sockaddr_to_str(&conn->loc_addr));
|
||||
audio_prim = hnb_audio_makeprim_conn_establish_cnf(v0->context_id, conn->id, 0, loc_port,
|
||||
v0->remote_rtp_address_type, &loc_uaddr);
|
||||
if ((rc = osmo_prim_srv_send(hnb->llsk.srv, audio_prim->hdr.msg)) < 0) {
|
||||
LOGUE(ue, DLLSK, LOGL_ERROR, "Failed sending AUDIO-CONN_ESTABLISH.cnf error_code=0\n");
|
||||
goto release_sock;
|
||||
}
|
||||
|
||||
return rc;
|
||||
release_sock:
|
||||
rtp_conn_free(conn);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int llsk_rx_audio_conn_release_req(struct hnb *hnb, struct hnb_audio_conn_release_req_param *rel_req)
|
||||
{
|
||||
struct rtp_conn *conn;
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Rx AUDIO-CONN_RELEASE.req id=%u\n", rel_req->audio_conn_id);
|
||||
|
||||
conn = hnb_find_rtp_conn_by_id(hnb, rel_req->audio_conn_id);
|
||||
if (!conn) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_RELEASE.req: RTP conn not found! id=%u\n",
|
||||
rel_req->audio_conn_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Release RTP socket: */
|
||||
rtp_conn_free(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int llsk_rx_audio_conn_data_req(struct hnb *hnb, struct hnb_audio_conn_data_req_param *data_req)
|
||||
{
|
||||
struct rtp_conn *conn;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Rx AUDIO-CONN_DATA.req id=%u data_len=%u\n",
|
||||
data_req->audio_conn_id, data_req->data_len);
|
||||
|
||||
conn = hnb_find_rtp_conn_by_id(hnb, data_req->audio_conn_id);
|
||||
if (!conn) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx AUDIO-CONN_DATA.req: RTP conn not found! id=%u data_len=%u\n",
|
||||
data_req->audio_conn_id, data_req->data_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Transmit data_req->data through RTP/Iu-UP socket */
|
||||
rc = rtp_conn_tx_data(conn, data_req->frame_nr, data_req->fqc, data_req->rfci, data_req->data, data_req->data_len);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int llsk_rx_audio(struct hnb *hnb, struct osmo_prim_hdr *oph)
|
||||
{
|
||||
size_t prim_size = llsk_audio_prim_size(oph->primitive, oph->operation);
|
||||
|
||||
if (msgb_length(oph->msg) < prim_size) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx AUDIO-%s.%s with length %u < %zu\n",
|
||||
get_value_string(hnb_audio_prim_type_names, oph->primitive),
|
||||
get_value_string(osmo_prim_op_names, oph->operation),
|
||||
msgb_length(oph->msg), prim_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (oph->operation) {
|
||||
case PRIM_OP_REQUEST:
|
||||
switch (oph->primitive) {
|
||||
case HNB_AUDIO_PRIM_CONN_ESTABLISH:
|
||||
return llsk_rx_audio_conn_establish_req(hnb, (struct hnb_audio_conn_establish_req_param *)msgb_data(oph->msg));
|
||||
case HNB_AUDIO_PRIM_CONN_RELEASE:
|
||||
return llsk_rx_audio_conn_release_req(hnb, (struct hnb_audio_conn_release_req_param *)msgb_data(oph->msg));
|
||||
case HNB_AUDIO_PRIM_CONN_DATA:
|
||||
return llsk_rx_audio_conn_data_req(hnb, (struct hnb_audio_conn_data_req_param *)msgb_data(oph->msg));
|
||||
default:
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx llsk-audio unknown primitive %u (len=%u)\n",
|
||||
oph->primitive, msgb_length(oph->msg));
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case PRIM_OP_RESPONSE:
|
||||
case PRIM_OP_INDICATION:
|
||||
case PRIM_OP_CONFIRM:
|
||||
default:
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx llsk-audio unexpected primitive operation %s::%s (len=%u)\n",
|
||||
get_value_string(hnb_audio_prim_type_names, oph->primitive),
|
||||
get_value_string(osmo_prim_op_names, oph->operation),
|
||||
msgb_length(oph->msg));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <inttypes.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/llsk.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
#include <osmocom/hnodeb/gtp.h>
|
||||
|
||||
static size_t llsk_gtp_prim_size_tbl[4][_HNB_GTP_PRIM_MAX] = {
|
||||
[PRIM_OP_REQUEST] = {
|
||||
[HNB_GTP_PRIM_CONN_ESTABLISH] = sizeof(struct hnb_gtp_conn_establish_req_param),
|
||||
[HNB_GTP_PRIM_CONN_RELEASE] = sizeof(struct hnb_gtp_conn_release_req_param),
|
||||
[HNB_GTP_PRIM_CONN_DATA] = sizeof(struct hnb_gtp_conn_data_req_param),
|
||||
},
|
||||
[PRIM_OP_RESPONSE] = {},
|
||||
[PRIM_OP_INDICATION] = {
|
||||
[HNB_GTP_PRIM_CONN_DATA] = sizeof(struct hnb_gtp_conn_data_ind_param),
|
||||
},
|
||||
[PRIM_OP_CONFIRM] = {
|
||||
[HNB_GTP_PRIM_CONN_ESTABLISH] = sizeof(struct hnb_gtp_conn_establish_cnf_param),
|
||||
},
|
||||
};
|
||||
static inline size_t llsk_gtp_prim_size(enum hnb_gtp_prim_type ptype, enum osmo_prim_operation op)
|
||||
{
|
||||
size_t val = llsk_gtp_prim_size_tbl[op][ptype];
|
||||
if (val == 0) {
|
||||
LOGP(DLLSK, LOGL_FATAL, "Expected prim_size != 0 for ptype=%u op=%u\n", ptype, op);
|
||||
osmo_panic("Expected prim_size != 0 for ptype=%u op=%u\n", ptype, op);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
const struct value_string hnb_gtp_prim_type_names[] = {
|
||||
OSMO_VALUE_STRING(HNB_GTP_PRIM_CONN_ESTABLISH),
|
||||
OSMO_VALUE_STRING(HNB_GTP_PRIM_CONN_RELEASE),
|
||||
OSMO_VALUE_STRING(HNB_GTP_PRIM_CONN_DATA),
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static struct hnb_gtp_prim *hnb_gtp_prim_alloc(enum hnb_gtp_prim_type ptype, enum osmo_prim_operation op, size_t extra_len)
|
||||
{
|
||||
struct osmo_prim_hdr *oph;
|
||||
size_t len = llsk_gtp_prim_size(ptype, op);
|
||||
|
||||
oph = osmo_prim_msgb_alloc(HNB_PRIM_SAPI_GTP, ptype, op, sizeof(*oph) + len + extra_len);
|
||||
if (!oph)
|
||||
return NULL;
|
||||
msgb_put(oph->msg, len);
|
||||
|
||||
return (struct hnb_gtp_prim *)oph;
|
||||
}
|
||||
|
||||
static struct hnb_gtp_prim *hnb_gtp_makeprim_conn_establish_cnf(uint32_t context_id, uint32_t gtp_conn_id,
|
||||
uint8_t error_code, uint32_t local_tei,
|
||||
uint8_t local_gtpu_address_type,
|
||||
const union u_addr *local_gtpu_addr)
|
||||
{
|
||||
struct hnb_gtp_prim *gtp_prim;
|
||||
|
||||
gtp_prim = hnb_gtp_prim_alloc(HNB_GTP_PRIM_CONN_ESTABLISH, PRIM_OP_CONFIRM, 0);
|
||||
gtp_prim->u.conn_establish_cnf.context_id = context_id;
|
||||
gtp_prim->u.conn_establish_cnf.gtp_conn_id = gtp_conn_id;
|
||||
gtp_prim->u.conn_establish_cnf.local_tei = local_tei;
|
||||
gtp_prim->u.conn_establish_cnf.error_code = error_code;
|
||||
gtp_prim->u.conn_establish_cnf.local_gtpu_address_type = local_gtpu_address_type;
|
||||
if (local_gtpu_addr)
|
||||
gtp_prim->u.conn_establish_cnf.local_gtpu_addr = *local_gtpu_addr;
|
||||
|
||||
return gtp_prim;
|
||||
}
|
||||
|
||||
struct hnb_gtp_prim *hnb_gtp_makeprim_conn_data_ind(uint32_t gtp_conn_id, const uint8_t *data, uint32_t data_len)
|
||||
{
|
||||
struct hnb_gtp_prim *gtp_prim;
|
||||
|
||||
gtp_prim = hnb_gtp_prim_alloc(HNB_GTP_PRIM_CONN_DATA, PRIM_OP_INDICATION, data_len);
|
||||
gtp_prim->u.conn_data_ind.gtp_conn_id = gtp_conn_id;
|
||||
gtp_prim->u.conn_data_ind.data_len = data_len;
|
||||
if (data_len) {
|
||||
msgb_put(gtp_prim->hdr.msg, data_len);
|
||||
memcpy(gtp_prim->u.conn_data_ind.data, data, data_len);
|
||||
}
|
||||
|
||||
return gtp_prim;
|
||||
}
|
||||
|
||||
static int _send_conn_establish_cnf_failed(struct hnb *hnb, uint32_t context_id, uint8_t error_code)
|
||||
{
|
||||
struct hnb_gtp_prim *gtp_prim;
|
||||
int rc;
|
||||
LOGP(DLLSK, LOGL_ERROR, "Tx GTP-CONN_ESTABLISH.cnf: ctx=%u error_code=%u\n",
|
||||
context_id, error_code);
|
||||
gtp_prim = hnb_gtp_makeprim_conn_establish_cnf(context_id, 0, error_code, 0, HNB_PRIM_ADDR_TYPE_UNSPEC, NULL);
|
||||
if ((rc = osmo_prim_srv_send(hnb->llsk.srv, gtp_prim->hdr.msg)) < 0) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Failed sending GTP-CONN_ESTABLISH.cnf context_id=%u error_code=%u\n",
|
||||
context_id, error_code);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int llsk_rx_gtp_conn_establish_req(struct hnb *hnb, struct hnb_gtp_conn_establish_req_param *ce_req)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
int rc = 0;
|
||||
struct hnb_gtp_prim *gtp_prim;
|
||||
int af;
|
||||
char rem_addrstr[INET6_ADDRSTRLEN+32];
|
||||
struct osmo_sockaddr rem_osa = {0};
|
||||
union u_addr loc_uaddr = {0};
|
||||
struct gtp_conn *conn = NULL;
|
||||
|
||||
rc = ll_addr2osa(ce_req->remote_gtpu_address_type, &ce_req->remote_gtpu_addr, 2152, &rem_osa);
|
||||
if (rc < 0) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: ctx=%u with unexpected address type %u\n",
|
||||
ce_req->context_id, ce_req->remote_gtpu_address_type);
|
||||
return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 1);
|
||||
}
|
||||
osmo_sockaddr_to_str_buf(rem_addrstr, sizeof(rem_addrstr), &rem_osa);
|
||||
|
||||
LOGP(DLLSK, LOGL_INFO, "Rx GTP-CONN_ESTABLISH.req ctx=%u rem_tei=%u rem_addr=%s\n",
|
||||
ce_req->context_id, ce_req->remote_tei, rem_addrstr);
|
||||
|
||||
if ((af = ll_addr_type2af(ce_req->remote_gtpu_address_type)) < 0) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: ctx=%u with unexpected address type %u\n",
|
||||
ce_req->context_id, ce_req->remote_gtpu_address_type);
|
||||
return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 1);
|
||||
}
|
||||
|
||||
ue = hnb_find_ue_by_id(hnb, ce_req->context_id);
|
||||
if (!ue) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: UE not found! ctx=%u rem_addr=%s\n",
|
||||
ce_req->context_id, rem_addrstr);
|
||||
return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 2);
|
||||
}
|
||||
if (!ue->conn_ps.active) {
|
||||
LOGUE(ue, DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: PS chan not active! rem_addr=%s\n",
|
||||
rem_addrstr);
|
||||
return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 3);
|
||||
}
|
||||
|
||||
/* Create the socket: */
|
||||
conn = gtp_conn_alloc(ue);
|
||||
if (!conn)
|
||||
return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 4);
|
||||
|
||||
if ((rc = gtp_conn_setup(conn, &rem_osa, ce_req->remote_tei)) < 0) {
|
||||
LOGUE(ue, DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: Failed to set up gtp socket rem_tei=%u rem_addr=%s\n",
|
||||
ce_req->remote_tei, rem_addrstr);
|
||||
return _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 4);
|
||||
}
|
||||
|
||||
/* Convert resulting local address back to LLSK format: */
|
||||
if (osa2_ll_addr(&conn->loc_addr, &loc_uaddr, NULL) != ce_req->remote_gtpu_address_type) {
|
||||
LOGUE(ue, DLLSK, LOGL_ERROR, "Rx GTP-CONN_ESTABLISH.req: Failed to provide proper local address rem_addr=%s\n",
|
||||
rem_addrstr);
|
||||
rc = _send_conn_establish_cnf_failed(hnb, ce_req->context_id, 4);
|
||||
goto release_sock;
|
||||
}
|
||||
|
||||
/* Submit successful confirmation */
|
||||
LOGUE(ue, DLLSK, LOGL_INFO, "Tx GTP-CONN_ESTABLISH.cnf: error_code=0 rem_addr=%s rem_tei=%u loc_addr=%s local_tei=%u\n",
|
||||
rem_addrstr, ce_req->remote_tei, osmo_sockaddr_to_str(&conn->loc_addr), conn->loc_tei);
|
||||
gtp_prim = hnb_gtp_makeprim_conn_establish_cnf(ce_req->context_id, conn->id, 0, conn->loc_tei,
|
||||
ce_req->remote_gtpu_address_type, &loc_uaddr);
|
||||
if ((rc = osmo_prim_srv_send(hnb->llsk.srv, gtp_prim->hdr.msg)) < 0) {
|
||||
LOGUE(ue, DLLSK, LOGL_ERROR, "Failed sending GTP-CONN_ESTABLISH.cnf error_code=0\n");
|
||||
goto release_sock;
|
||||
}
|
||||
return rc;
|
||||
release_sock:
|
||||
gtp_conn_free(conn);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int llsk_rx_gtp_conn_release_req(struct hnb *hnb, struct hnb_gtp_conn_release_req_param *rel_req)
|
||||
{
|
||||
struct gtp_conn *conn;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Rx GTP-CONN_RELEASE.req id=%u\n", rel_req->gtp_conn_id);
|
||||
|
||||
conn = hnb_find_gtp_conn_by_id(hnb, rel_req->gtp_conn_id);
|
||||
if (!conn) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx GTP-CONN_RELEASE.req: GTP conn not found! id=%u\n",
|
||||
rel_req->gtp_conn_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* release GTP pdp ctx: */
|
||||
gtp_conn_free(conn);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int llsk_rx_gtp_conn_data_req(struct hnb *hnb, struct hnb_gtp_conn_data_req_param *data_req)
|
||||
{
|
||||
struct gtp_conn *conn;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Rx GTP-CONN_DATA.req id=%u data_len=%u\n",
|
||||
data_req->gtp_conn_id, data_req->data_len);
|
||||
|
||||
conn = hnb_find_gtp_conn_by_id(hnb, data_req->gtp_conn_id);
|
||||
if (!conn) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx GTP-CONN_DATA.req: GTP conn not found! id=%u data_len=%u\n",
|
||||
data_req->gtp_conn_id, data_req->data_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = gtp_conn_tx(conn, data_req->data, data_req->data_len);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int llsk_rx_gtp(struct hnb *hnb, struct osmo_prim_hdr *oph)
|
||||
{
|
||||
size_t prim_size = llsk_gtp_prim_size(oph->primitive, oph->operation);
|
||||
|
||||
if (msgb_length(oph->msg) < prim_size) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx GTP-%s.%s with length %u < %zu\n",
|
||||
get_value_string(hnb_gtp_prim_type_names, oph->primitive),
|
||||
get_value_string(osmo_prim_op_names, oph->operation),
|
||||
msgb_length(oph->msg), prim_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (oph->operation) {
|
||||
case PRIM_OP_REQUEST:
|
||||
switch (oph->primitive) {
|
||||
case HNB_GTP_PRIM_CONN_ESTABLISH:
|
||||
return llsk_rx_gtp_conn_establish_req(hnb, (struct hnb_gtp_conn_establish_req_param *)msgb_data(oph->msg));
|
||||
case HNB_GTP_PRIM_CONN_RELEASE:
|
||||
return llsk_rx_gtp_conn_release_req(hnb, (struct hnb_gtp_conn_release_req_param *)msgb_data(oph->msg));
|
||||
case HNB_GTP_PRIM_CONN_DATA:
|
||||
return llsk_rx_gtp_conn_data_req(hnb, (struct hnb_gtp_conn_data_req_param *)msgb_data(oph->msg));
|
||||
default:
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx llsk-gtp unknown primitive %u (len=%u)\n",
|
||||
oph->primitive, msgb_length(oph->msg));
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case PRIM_OP_RESPONSE:
|
||||
case PRIM_OP_INDICATION:
|
||||
case PRIM_OP_CONFIRM:
|
||||
default:
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx llsk-gtp unexpected primitive operation %s::%s (len=%u)\n",
|
||||
get_value_string(hnb_gtp_prim_type_names, oph->primitive),
|
||||
get_value_string(osmo_prim_op_names, oph->operation),
|
||||
msgb_length(oph->msg));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,347 @@
|
|||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/rua/rua_msg_factory.h>
|
||||
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/llsk.h>
|
||||
#include <osmocom/hnodeb/hnb_prim.h>
|
||||
#include <osmocom/hnodeb/iuh.h>
|
||||
#include <osmocom/hnodeb/ranap.h>
|
||||
|
||||
static size_t llsk_iuh_prim_size_tbl[4][_HNB_IUH_PRIM_MAX] = {
|
||||
[PRIM_OP_REQUEST] = {
|
||||
[HNB_IUH_PRIM_CONN_ESTABLISH] = sizeof(struct hnb_iuh_conn_establish_req_param),
|
||||
[HNB_IUH_PRIM_CONN_RELEASE] = sizeof(struct hnb_iuh_conn_release_req_param),
|
||||
[HNB_IUH_PRIM_CONN_DATA] = sizeof(struct hnb_iuh_conn_data_req_param),
|
||||
[HNB_IUH_PRIM_UNITDATA] = sizeof(struct hnb_iuh_unitdata_req_param),
|
||||
},
|
||||
[PRIM_OP_RESPONSE] = {},
|
||||
[PRIM_OP_INDICATION] = {
|
||||
[HNB_IUH_PRIM_CONFIGURE] = sizeof(struct hnb_iuh_configure_ind_param),
|
||||
[HNB_IUH_PRIM_CONN_DATA] = sizeof(struct hnb_iuh_conn_data_ind_param),
|
||||
[HNB_IUH_PRIM_UNITDATA] = sizeof(struct hnb_iuh_unitdata_ind_param),
|
||||
},
|
||||
[PRIM_OP_CONFIRM] = {
|
||||
[HNB_IUH_PRIM_CONN_ESTABLISH] = sizeof(struct hnb_iuh_conn_establish_cnf_param),
|
||||
},
|
||||
};
|
||||
static inline size_t llsk_iuh_prim_size(enum hnb_iuh_prim_type ptype, enum osmo_prim_operation op)
|
||||
{
|
||||
size_t val = llsk_iuh_prim_size_tbl[op][ptype];
|
||||
if (val == 0) {
|
||||
LOGP(DLLSK, LOGL_FATAL, "Expected prim_size != 0 for ptype=%u op=%u\n", ptype, op);
|
||||
osmo_panic("Expected prim_size != 0 for ptype=%u op=%u\n", ptype, op);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
const struct value_string hnb_iuh_prim_type_names[] = {
|
||||
OSMO_VALUE_STRING(HNB_IUH_PRIM_CONFIGURE),
|
||||
OSMO_VALUE_STRING(HNB_IUH_PRIM_CONN_ESTABLISH),
|
||||
OSMO_VALUE_STRING(HNB_IUH_PRIM_CONN_RELEASE),
|
||||
OSMO_VALUE_STRING(HNB_IUH_PRIM_CONN_DATA),
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
struct hnb_iuh_prim *hnb_iuh_prim_alloc(enum hnb_iuh_prim_type ptype, enum osmo_prim_operation op, size_t extra_len)
|
||||
{
|
||||
struct osmo_prim_hdr *oph;
|
||||
size_t len = llsk_iuh_prim_size(ptype, op);
|
||||
|
||||
oph = osmo_prim_msgb_alloc(HNB_PRIM_SAPI_IUH, ptype, op, sizeof(*oph) + len + extra_len);
|
||||
if (!oph)
|
||||
return NULL;
|
||||
msgb_put(oph->msg, len);
|
||||
|
||||
return (struct hnb_iuh_prim *)oph;
|
||||
}
|
||||
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_configure_ind(uint16_t mcc, uint16_t mnc,
|
||||
uint16_t cell_identity,
|
||||
uint16_t lac, uint8_t rac,
|
||||
uint16_t sac, uint16_t rnc_id)
|
||||
{
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
|
||||
iuh_prim = hnb_iuh_prim_alloc(HNB_IUH_PRIM_CONFIGURE, PRIM_OP_INDICATION, 0);
|
||||
iuh_prim->u.configure_ind.mcc = mcc;
|
||||
iuh_prim->u.configure_ind.mnc = mnc;
|
||||
iuh_prim->u.configure_ind.cell_identity = cell_identity;
|
||||
iuh_prim->u.configure_ind.lac = lac;
|
||||
iuh_prim->u.configure_ind.rac = rac;
|
||||
iuh_prim->u.configure_ind.sac = sac;
|
||||
iuh_prim->u.configure_ind.rnc_id = rnc_id;
|
||||
|
||||
return iuh_prim;
|
||||
}
|
||||
|
||||
int llsk_iuh_tx_configure_ind(struct hnb *hnb)
|
||||
{
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
int rc;
|
||||
|
||||
LOGP(DLLSK, LOGL_INFO, "Tx IUH-CONFIGURE.ind\n");
|
||||
/* We are already registered, so configure the lower layers right now */
|
||||
iuh_prim = hnb_iuh_makeprim_configure_ind(hnb->plmn.mcc, hnb->plmn.mnc,
|
||||
hnb->cell_identity, hnb->lac,
|
||||
hnb->rac, hnb->sac, hnb->rnc_id);
|
||||
if ((rc = osmo_prim_srv_send(hnb->llsk.srv, iuh_prim->hdr.msg)) < 0)
|
||||
LOGP(DLLSK, LOGL_ERROR, "Failed sending IUH-CONFIGURE.ind\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_conn_establish_cnf(uint32_t context_id, uint8_t domain,
|
||||
uint8_t cause)
|
||||
{
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
|
||||
iuh_prim = hnb_iuh_prim_alloc(HNB_IUH_PRIM_CONN_ESTABLISH, PRIM_OP_CONFIRM, 0);
|
||||
iuh_prim->u.conn_establish_cnf.context_id = context_id;
|
||||
iuh_prim->u.conn_establish_cnf.domain = domain;
|
||||
iuh_prim->u.conn_establish_cnf.cause = cause;
|
||||
|
||||
return iuh_prim;
|
||||
}
|
||||
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_conn_data_ind(uint32_t context_id,
|
||||
uint8_t domain,
|
||||
const uint8_t *data,
|
||||
uint32_t data_len)
|
||||
{
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
|
||||
iuh_prim = hnb_iuh_prim_alloc(HNB_IUH_PRIM_CONN_DATA, PRIM_OP_INDICATION, data_len);
|
||||
iuh_prim->u.conn_data_ind.context_id = context_id;
|
||||
iuh_prim->u.conn_data_ind.domain = domain;
|
||||
iuh_prim->u.conn_data_ind.data_len = data_len;
|
||||
if (data_len) {
|
||||
msgb_put(iuh_prim->hdr.msg, data_len);
|
||||
memcpy(iuh_prim->u.conn_data_ind.data, data, data_len);
|
||||
}
|
||||
|
||||
return iuh_prim;
|
||||
}
|
||||
|
||||
struct hnb_iuh_prim *hnb_iuh_makeprim_unitdata_ind(const uint8_t *data, uint32_t data_len)
|
||||
{
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
|
||||
iuh_prim = hnb_iuh_prim_alloc(HNB_IUH_PRIM_UNITDATA, PRIM_OP_INDICATION, data_len);
|
||||
iuh_prim->u.unitdata_ind.data_len = data_len;
|
||||
if (data_len) {
|
||||
msgb_put(iuh_prim->hdr.msg, data_len);
|
||||
memcpy(iuh_prim->u.unitdata_ind.data, data, data_len);
|
||||
}
|
||||
|
||||
return iuh_prim;
|
||||
}
|
||||
|
||||
static int llsk_rx_iuh_conn_establish_req(struct hnb *hnb, struct hnb_iuh_conn_establish_req_param *ce_req)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DLLSK, LOGL_INFO, "Rx IUH-CONN_ESTABLISH.req ctx=%u is_ps=%u est_cause=%u data_len=%u\n",
|
||||
ce_req->context_id, ce_req->domain, ce_req->est_cause, ce_req->data_len);
|
||||
|
||||
if (!hnb->registered) {
|
||||
LOGP(DLLSK, LOGL_NOTICE, "Ignoring Rx IUH-CONN_ESTABLISH.req: HNB not registered to HNBGW!\n");
|
||||
/* TODO: NACK it to lower layers */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ue = hnb_find_ue_by_id(hnb, ce_req->context_id);
|
||||
if (!ue) {
|
||||
ue = hnb_ue_alloc(hnb, ce_req->context_id);
|
||||
if (ce_req->domain) {
|
||||
ue->conn_ps.active = true;
|
||||
ue->conn_ps.conn_est_cnf_pending = true; /* Set to false once we receive first DT from HNBGW and send CONN_EST.cnf */
|
||||
|
||||
} else {
|
||||
ue->conn_cs.active = true;
|
||||
ue->conn_cs.conn_est_cnf_pending = true; /* Set to false once we receive first DT from HNBGW and send CONN_EST.cnf */
|
||||
}
|
||||
}
|
||||
if (ce_req->data_len) {
|
||||
struct msgb *rua_msg;
|
||||
struct msgb *ranap_msg = hnb_ranap_msgb_alloc();
|
||||
LOGP(DRUA, LOGL_DEBUG, "Tx RUA CONNECT ctx=%u is_ps=%u data_len=%u\n",
|
||||
ce_req->context_id, ce_req->domain, ce_req->data_len);
|
||||
memcpy(msgb_put(ranap_msg, ce_req->data_len), ce_req->data, ce_req->data_len);
|
||||
rua_msg = rua_new_conn(ce_req->domain, ce_req->context_id, ranap_msg);
|
||||
hnb_iuh_send(hnb, rua_msg);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int llsk_rx_iuh_conn_release_req(struct hnb *hnb, struct hnb_iuh_conn_release_req_param *rel_req)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
struct msgb *rua_msg;
|
||||
struct msgb *ranap_msg;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Rx IUH-CONN_RELEASE.req ctx=%u is_ps=%u data_len=%u\n",
|
||||
rel_req->context_id, rel_req->domain, rel_req->data_len);
|
||||
|
||||
if (!hnb->registered) {
|
||||
LOGP(DLLSK, LOGL_NOTICE, "Rx IUH-CONN_RELEASE.req: Ignoring, HNB not registered to HNBGW!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ue = hnb_find_ue_by_id(hnb, rel_req->context_id);
|
||||
if (!ue) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx IUH-CONN_RELEASE.req: conn unknown! ctx=%u is_ps=%u data_len=%u\n",
|
||||
rel_req->context_id, rel_req->domain, rel_req->data_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
hnb_ue_reset_chan(ue, !!rel_req->domain);
|
||||
if (!ue->conn_cs.active && !ue->conn_ps.active) {
|
||||
hnb_ue_free(ue);
|
||||
ue = NULL;
|
||||
}
|
||||
|
||||
LOGP(DRUA, LOGL_DEBUG, "Tx RUA DISC ctx=%u is_ps=%u data_len=%u\n",
|
||||
rel_req->context_id, rel_req->domain, rel_req->data_len);
|
||||
ranap_msg = hnb_ranap_msgb_alloc();
|
||||
if (rel_req->data_len)
|
||||
memcpy(msgb_put(ranap_msg, rel_req->data_len), rel_req->data, rel_req->data_len);
|
||||
|
||||
rua_msg = rua_new_disc(rel_req->domain, rel_req->context_id, ranap_msg);
|
||||
hnb_iuh_send(hnb, rua_msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int llsk_rx_iuh_conn_data_req(struct hnb *hnb, struct hnb_iuh_conn_data_req_param *data_req)
|
||||
{
|
||||
struct hnb_ue *ue;
|
||||
struct msgb *rua_msg;
|
||||
struct msgb *ranap_msg;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Rx IUH-CONN_DATA.req ctx=%u is_ps=%u data_len=%u\n",
|
||||
data_req->context_id, data_req->domain, data_req->data_len);
|
||||
|
||||
if (!hnb->registered) {
|
||||
LOGP(DLLSK, LOGL_NOTICE, "Rx IUH-CONN_DATA.req: Ignoring, HNB not registered to HNBGW!\n");
|
||||
/* TODO: NACK it to lower layers */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ue = hnb_find_ue_by_id(hnb, data_req->context_id);
|
||||
if (!ue) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx IUH-CONN_DATA.req: conn unknown! ctx=%u is_ps=%u data_len=%u\n",
|
||||
data_req->context_id, data_req->domain, data_req->data_len);
|
||||
/* TODO: NACK it to lower layers */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOGP(DRUA, LOGL_DEBUG, "Tx RUA DT ctx=%u is_ps=%u data_len=%u\n",
|
||||
data_req->context_id, data_req->domain, data_req->data_len);
|
||||
ranap_msg = hnb_ranap_msgb_alloc();
|
||||
if (data_req->data_len)
|
||||
memcpy(msgb_put(ranap_msg, data_req->data_len), data_req->data, data_req->data_len);
|
||||
|
||||
rua_msg = rua_new_dt(data_req->domain, data_req->context_id, ranap_msg);
|
||||
hnb_iuh_send(hnb, rua_msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int llsk_rx_iuh_unitdata_req(struct hnb *hnb, struct hnb_iuh_unitdata_req_param *ud_req)
|
||||
{
|
||||
struct msgb *rua_msg;
|
||||
struct msgb *ranap_msg;
|
||||
int rc = 0;
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Rx IUH-UNITDATA.req data_len=%u\n", ud_req->data_len);
|
||||
|
||||
if (!hnb->registered) {
|
||||
LOGP(DLLSK, LOGL_NOTICE, "Rx IUH-UNITDATA.req: Ignoring, HNB not registered to HNBGW!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOGP(DRUA, LOGL_DEBUG, "Tx RUA UDT data_len=%u\n", ud_req->data_len);
|
||||
|
||||
ranap_msg = hnb_ranap_msgb_alloc();
|
||||
if (ud_req->data_len)
|
||||
memcpy(msgb_put(ranap_msg, ud_req->data_len), ud_req->data, ud_req->data_len);
|
||||
|
||||
rua_msg = rua_new_udt(ranap_msg);
|
||||
hnb_iuh_send(hnb, rua_msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int llsk_rx_iuh(struct hnb *hnb, struct osmo_prim_hdr *oph)
|
||||
{
|
||||
size_t prim_size = llsk_iuh_prim_size(oph->primitive, oph->operation);
|
||||
|
||||
if (msgb_length(oph->msg) < prim_size) {
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx IUH-%s.%s with length %u < %zu\n",
|
||||
get_value_string(hnb_iuh_prim_type_names, oph->primitive),
|
||||
get_value_string(osmo_prim_op_names, oph->operation),
|
||||
msgb_length(oph->msg), prim_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (oph->operation) {
|
||||
case PRIM_OP_REQUEST:
|
||||
switch (oph->primitive) {
|
||||
case HNB_IUH_PRIM_CONN_ESTABLISH:
|
||||
return llsk_rx_iuh_conn_establish_req(hnb, (struct hnb_iuh_conn_establish_req_param *)msgb_data(oph->msg));
|
||||
case HNB_IUH_PRIM_CONN_RELEASE:
|
||||
return llsk_rx_iuh_conn_release_req(hnb, (struct hnb_iuh_conn_release_req_param *)msgb_data(oph->msg));
|
||||
case HNB_IUH_PRIM_CONN_DATA:
|
||||
return llsk_rx_iuh_conn_data_req(hnb, (struct hnb_iuh_conn_data_req_param *)msgb_data(oph->msg));
|
||||
case HNB_IUH_PRIM_UNITDATA:
|
||||
return llsk_rx_iuh_unitdata_req(hnb, (struct hnb_iuh_unitdata_req_param *)msgb_data(oph->msg));
|
||||
default:
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx llsk-iuh unknown primitive %u (len=%u)\n",
|
||||
oph->primitive, msgb_length(oph->msg));
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case PRIM_OP_RESPONSE:
|
||||
case PRIM_OP_INDICATION:
|
||||
case PRIM_OP_CONFIRM:
|
||||
default:
|
||||
LOGP(DLLSK, LOGL_ERROR, "Rx llsk-iuh unexpected primitive operation %s::%s (len=%u)\n",
|
||||
get_value_string(hnb_iuh_prim_type_names, oph->primitive),
|
||||
get_value_string(osmo_prim_op_names, oph->operation),
|
||||
msgb_length(oph->msg));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
|
@ -41,6 +41,8 @@
|
|||
#include <osmocom/vty/cpu_sched_vty.h>
|
||||
#include <osmocom/vty/ports.h>
|
||||
|
||||
#include <osmocom/trau/osmo_ortp.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_common.h> /* ranap_set_log_area() */
|
||||
|
||||
#include <osmocom/hnodeb/hnbap.h>
|
||||
|
@ -48,6 +50,8 @@
|
|||
#include <osmocom/hnodeb/ranap.h>
|
||||
#include <osmocom/hnodeb/vty.h>
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
#include <osmocom/hnodeb/iuh.h>
|
||||
#include <osmocom/hnodeb/gtp.h>
|
||||
|
||||
static const char * const osmohnodeb_copyright =
|
||||
"OsmoHNodeB - Osmocom 3G Home NodeB implementation\r\n"
|
||||
|
@ -223,6 +227,7 @@ static void signal_handler(int signum)
|
|||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc;
|
||||
void *tall_rtp_ctx;
|
||||
|
||||
/* Track the use of talloc NULL memory contexts */
|
||||
talloc_enable_null_tracking();
|
||||
|
@ -245,6 +250,11 @@ int main(int argc, char **argv)
|
|||
osmo_talloc_vty_add_cmds();
|
||||
osmo_cpu_sched_vty_init(tall_hnb_ctx);
|
||||
|
||||
/* allocate a talloc pool for ORTP to ensure it doesn't have to go back
|
||||
* to the libc malloc all the time */
|
||||
tall_rtp_ctx = talloc_pool(tall_hnb_ctx, 262144);
|
||||
osmo_rtp_init(tall_rtp_ctx);
|
||||
|
||||
g_hnb = hnb_alloc(tall_hnb_ctx);
|
||||
hnb_vty_init();
|
||||
|
||||
|
@ -258,13 +268,26 @@ int main(int argc, char **argv)
|
|||
exit(1);
|
||||
}
|
||||
|
||||
rc = telnet_init_dynif(tall_hnb_ctx, g_hnb, vty_get_bind_addr(), OSMO_VTY_PORT_HNODEB);
|
||||
rc = telnet_init_default(tall_hnb_ctx, g_hnb, OSMO_VTY_PORT_HNODEB);
|
||||
if (rc < 0) {
|
||||
perror("Error binding VTY port");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rc = hnb_connect(g_hnb);
|
||||
/* Start listening on lower layer unix domain socket: */
|
||||
rc = hnb_llsk_start_listen(g_hnb);
|
||||
if (rc < 0) {
|
||||
perror("Error opening lower layer socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rc = hnb_gtp_bind(g_hnb);
|
||||
if (rc < 0) {
|
||||
perror("Error listening on GTP port");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
rc = hnb_iuh_connect(g_hnb);
|
||||
if (rc < 0) {
|
||||
perror("Error connecting to Iuh port");
|
||||
exit(1);
|
||||
|
@ -289,6 +312,8 @@ int main(int argc, char **argv)
|
|||
osmo_select_main_ctx(0);
|
||||
}
|
||||
|
||||
hnb_gtp_unbind(g_hnb);
|
||||
|
||||
log_fini();
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,268 +0,0 @@
|
|||
/* (C) 2015 by Daniel Willmann <dwillmann@sysmocom.de>
|
||||
* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
#include <osmocom/crypt/auth.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_msg_factory.h>
|
||||
|
||||
#include <osmocom/hnodeb/rua.h>
|
||||
#include <osmocom/hnodeb/ranap.h>
|
||||
#include <osmocom/hnodeb/nas.h>
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
|
||||
static struct msgb *gen_nas_id_resp()
|
||||
{
|
||||
uint8_t id_resp[] = {
|
||||
GSM48_PDISC_MM,
|
||||
GSM48_MT_MM_ID_RESP,
|
||||
/* IMEISV */
|
||||
0x09, /* len */
|
||||
0x03, /* first digit (0000) + even (0) + id IMEISV (011) */
|
||||
0x31, 0x91, 0x06, 0x00, 0x28, 0x47, 0x11, /* digits */
|
||||
0xf2, /* filler (1111) + last digit (0010) */
|
||||
};
|
||||
|
||||
return ranap_new_msg_dt(0, id_resp, sizeof(id_resp));
|
||||
}
|
||||
|
||||
static struct msgb *gen_nas_tmsi_realloc_compl()
|
||||
{
|
||||
uint8_t id_resp[] = {
|
||||
GSM48_PDISC_MM,
|
||||
GSM48_MT_MM_TMSI_REALL_COMPL,
|
||||
};
|
||||
|
||||
return ranap_new_msg_dt(0, id_resp, sizeof(id_resp));
|
||||
}
|
||||
|
||||
static struct msgb *gen_nas_auth_resp(uint8_t *sres)
|
||||
{
|
||||
uint8_t id_resp[] = {
|
||||
GSM48_PDISC_MM,
|
||||
0x80 | GSM48_MT_MM_AUTH_RESP, /* simulate sequence nr 2 */
|
||||
0x61, 0xb5, 0x69, 0xf5 /* hardcoded SRES */
|
||||
};
|
||||
|
||||
memcpy(id_resp + 2, sres, 4);
|
||||
|
||||
return ranap_new_msg_dt(0, id_resp, sizeof(id_resp));
|
||||
}
|
||||
|
||||
static struct tlv_parsed *parse_mm(struct gsm48_hdr *gh, int len)
|
||||
{
|
||||
static struct tlv_parsed tp;
|
||||
int parse_res;
|
||||
|
||||
len -= (const char *)&gh->data[0] - (const char *)gh;
|
||||
|
||||
OSMO_ASSERT(gsm48_hdr_pdisc(gh) == GSM48_PDISC_MM);
|
||||
|
||||
parse_res = tlv_parse(&tp, &gsm48_mm_att_tlvdef, &gh->data[0], len, 0, 0);
|
||||
if (parse_res <= 0) {
|
||||
uint8_t msg_type = gsm48_hdr_msg_type(gh);
|
||||
LOGP(DNAS, LOGL_ERROR, "Error parsing MM message 0x%hhx: %d\n", msg_type, parse_res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &tp;
|
||||
}
|
||||
|
||||
int hnb_nas_rx_lu_accept(struct gsm48_hdr *gh, int len, int *sent_tmsi)
|
||||
{
|
||||
LOGP(DNAS, LOGL_INFO, " :D Location Update Accept :D\n");
|
||||
struct gsm48_loc_area_id *lai;
|
||||
|
||||
lai = (struct gsm48_loc_area_id *)&gh->data[0];
|
||||
|
||||
struct osmo_location_area_id laid;
|
||||
gsm48_decode_lai2(lai, &laid);
|
||||
LOGP(DNAS, LOGL_INFO, "LU: mcc %s mnc %s lac %hd\n",
|
||||
osmo_mcc_name(laid.plmn.mcc), osmo_mnc_name(laid.plmn.mnc, laid.plmn.mnc_3_digits),
|
||||
laid.lac);
|
||||
|
||||
struct tlv_parsed tp;
|
||||
int parse_res;
|
||||
|
||||
len -= (const char *)&gh->data[0] - (const char *)gh;
|
||||
parse_res = tlv_parse(&tp, &gsm48_mm_att_tlvdef, &gh->data[0], len, 0, 0);
|
||||
if (parse_res <= 0) {
|
||||
LOGP(DNAS, LOGL_ERROR, "Error parsing Location Update Accept message: %d\n", parse_res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) {
|
||||
uint8_t type = TLVP_VAL(&tp, GSM48_IE_NAME_SHORT)[0] & 0x0f;
|
||||
if (type == GSM_MI_TYPE_TMSI)
|
||||
*sent_tmsi = 1;
|
||||
else *sent_tmsi = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hnb_nas_rx_mm_info(struct gsm48_hdr *gh, int len)
|
||||
{
|
||||
LOGP(DNAS, LOGL_INFO, " :) MM Info :)\n");
|
||||
struct tlv_parsed *tp = parse_mm(gh, len);
|
||||
if (!tp)
|
||||
return;
|
||||
|
||||
if (TLVP_PRESENT(tp, GSM48_IE_NAME_SHORT)) {
|
||||
char name[128] = {0};
|
||||
gsm_7bit_decode_n(name, 127,
|
||||
TLVP_VAL(tp, GSM48_IE_NAME_SHORT)+1,
|
||||
(TLVP_LEN(tp, GSM48_IE_NAME_SHORT)-1)*8/7);
|
||||
LOGP(DNAS, LOGL_INFO, "Info: Short Network Name: %s\n", name);
|
||||
}
|
||||
|
||||
if (TLVP_PRESENT(tp, GSM48_IE_NAME_LONG)) {
|
||||
char name[128] = {0};
|
||||
gsm_7bit_decode_n(name, 127,
|
||||
TLVP_VAL(tp, GSM48_IE_NAME_LONG)+1,
|
||||
(TLVP_LEN(tp, GSM48_IE_NAME_LONG)-1)*8/7);
|
||||
LOGP(DNAS, LOGL_INFO, "Info: Long Network Name: %s\n", name);
|
||||
}
|
||||
}
|
||||
|
||||
static int hnb_nas_rx_auth_req(struct hnb *hnb, struct gsm48_hdr *gh,
|
||||
int len)
|
||||
{
|
||||
struct gsm48_auth_req *ar;
|
||||
|
||||
len -= (const char *)&gh->data[0] - (const char *)gh;
|
||||
|
||||
if (len < sizeof(*ar)) {
|
||||
LOGP(DNAS, LOGL_ERROR, "GSM48 Auth Req does not fit.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DNAS, LOGL_INFO, " :) Authentication Request :)\n");
|
||||
|
||||
ar = (struct gsm48_auth_req*) &gh->data[0];
|
||||
int seq = ar->key_seq;
|
||||
|
||||
/* Generate SRES from *HARDCODED* Ki for Iuh testing */
|
||||
struct osmo_auth_vector vec;
|
||||
/* Ki 000102030405060708090a0b0c0d0e0f */
|
||||
struct osmo_sub_auth_data auth = {
|
||||
.type = OSMO_AUTH_TYPE_GSM,
|
||||
.algo = OSMO_AUTH_ALG_COMP128v1,
|
||||
.u.gsm.ki = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
||||
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
0x0e, 0x0f
|
||||
},
|
||||
};
|
||||
|
||||
memset(&vec, 0, sizeof(vec));
|
||||
osmo_auth_gen_vec(&vec, &auth, ar->rand);
|
||||
|
||||
LOGP(DNAS, LOGL_DEBUG, "seq %d rand %s",
|
||||
seq, osmo_hexdump(ar->rand, sizeof(ar->rand)));
|
||||
LOGP(DNAS, LOGL_DEBUG, " --> sres %s\n",
|
||||
osmo_hexdump(vec.sres, 4));
|
||||
|
||||
return hnb_tx_dt(hnb, gen_nas_auth_resp(vec.sres));
|
||||
}
|
||||
|
||||
static int hnb_nas_rx_mm(struct hnb *hnb, struct gsm48_hdr *gh, int len)
|
||||
{
|
||||
struct hnb_chan *chan;
|
||||
|
||||
chan = hnb->cs.chan;
|
||||
if (!chan) {
|
||||
LOGP(DNAS, LOGL_ERROR, "hnb_nas_rx_mm(): No CS channel established yet.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
OSMO_ASSERT(!chan->is_ps);
|
||||
|
||||
uint8_t msg_type = gsm48_hdr_msg_type(gh);
|
||||
int sent_tmsi;
|
||||
|
||||
switch (msg_type) {
|
||||
case GSM48_MT_MM_ID_REQ:
|
||||
return hnb_tx_dt(hnb, gen_nas_id_resp());
|
||||
|
||||
case GSM48_MT_MM_LOC_UPD_ACCEPT:
|
||||
if (hnb_nas_rx_lu_accept(gh, len, &sent_tmsi))
|
||||
return -1;
|
||||
if (sent_tmsi)
|
||||
return hnb_tx_dt(hnb, gen_nas_tmsi_realloc_compl());
|
||||
else
|
||||
return 0;
|
||||
|
||||
case GSM48_MT_MM_LOC_UPD_REJECT:
|
||||
LOGP(DNAS, LOGL_INFO, "Received Location Update Reject\n");
|
||||
return 0;
|
||||
|
||||
case GSM48_MT_MM_INFO:
|
||||
hnb_nas_rx_mm_info(gh, len);
|
||||
hnb_tx_iu_release_req(hnb);
|
||||
return 0;
|
||||
|
||||
case GSM48_MT_MM_AUTH_REQ:
|
||||
return hnb_nas_rx_auth_req(hnb, gh, len);
|
||||
|
||||
default:
|
||||
LOGP(DNAS, LOGL_INFO, "04.08 message type not handled by hnb-test: 0x%x\n",
|
||||
msg_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void hnb_nas_rx_dtap(struct hnb *hnb, void *data, int len)
|
||||
{
|
||||
int rc;
|
||||
LOGP(DNAS, LOGL_INFO, "got %d bytes: %s\n", len, osmo_hexdump(data, len));
|
||||
|
||||
// nas_pdu == '05 08 12' ==> IMEI Identity request
|
||||
// '05 04 0d' ==> LU reject
|
||||
|
||||
struct gsm48_hdr *gh = data;
|
||||
if (len < sizeof(*gh)) {
|
||||
LOGP(DNAS, LOGL_ERROR, "hnb_nas_rx_dtap(): NAS PDU is too short: %d. Ignoring.\n",
|
||||
len);
|
||||
return;
|
||||
}
|
||||
uint8_t pdisc = gsm48_hdr_pdisc(gh);
|
||||
|
||||
switch (pdisc) {
|
||||
case GSM48_PDISC_MM:
|
||||
rc = hnb_nas_rx_mm(hnb, gh, len);
|
||||
if (rc != 0)
|
||||
LOGP(DNAS, LOGL_ERROR, "Error receiving MM message: %d\n", rc);
|
||||
return;
|
||||
default:
|
||||
LOGP(DNAS, LOGL_NOTICE, "04.08 discriminator not handled by hnb-test: %d\n",
|
||||
pdisc);
|
||||
return;
|
||||
}
|
||||
}
|
|
@ -25,152 +25,10 @@
|
|||
#include <osmocom/ranap/iu_helpers.h>
|
||||
#include <osmocom/ranap/ranap_msg_factory.h>
|
||||
|
||||
#include <osmocom/hnodeb/nas.h>
|
||||
#include <osmocom/hnodeb/rua.h>
|
||||
#include <osmocom/hnodeb/ranap.h>
|
||||
|
||||
static const char *printstr(OCTET_STRING_t *s)
|
||||
struct msgb *hnb_ranap_msgb_alloc(void)
|
||||
{
|
||||
return osmo_hexdump((const unsigned char*)s->buf, s->size);
|
||||
}
|
||||
|
||||
#define PP(octet_string_t) \
|
||||
LOGP(DRANAP, LOGL_INFO, #octet_string_t " = %s\n",\
|
||||
printstr(&octet_string_t))
|
||||
|
||||
void hnb_rua_dt_handle_ranap(void *ctx, struct ranap_message_s *ranap_msg)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)ctx;
|
||||
int len;
|
||||
uint8_t *data;
|
||||
RANAP_PermittedIntegrityProtectionAlgorithms_t *algs;
|
||||
RANAP_IntegrityProtectionAlgorithm_t *first_alg;
|
||||
|
||||
LOGP(DRANAP, LOGL_INFO, "rx ranap_msg->procedureCode %d\n",
|
||||
ranap_msg->procedureCode);
|
||||
|
||||
switch (ranap_msg->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_DirectTransfer:
|
||||
LOGP(DRANAP, LOGL_INFO, "rx DirectTransfer: presence = %hx\n",
|
||||
ranap_msg->msg.directTransferIEs.presenceMask);
|
||||
PP(ranap_msg->msg.directTransferIEs.nas_pdu);
|
||||
|
||||
len = ranap_msg->msg.directTransferIEs.nas_pdu.size;
|
||||
data = ranap_msg->msg.directTransferIEs.nas_pdu.buf;
|
||||
|
||||
hnb_nas_rx_dtap(hnb, data, len);
|
||||
return;
|
||||
|
||||
case RANAP_ProcedureCode_id_SecurityModeControl:
|
||||
LOGP(DRANAP, LOGL_INFO, "rx SecurityModeControl: presence = %hx\n",
|
||||
ranap_msg->msg.securityModeCommandIEs.presenceMask);
|
||||
|
||||
/* Just pick the first available IP alg, don't care about
|
||||
* encryption (yet?) */
|
||||
algs = &ranap_msg->msg.securityModeCommandIEs.integrityProtectionInformation.permittedAlgorithms;
|
||||
if (algs->list.count < 1) {
|
||||
LOGP(DRANAP, LOGL_INFO, "Security Mode Command: No permitted algorithms.\n");
|
||||
return;
|
||||
}
|
||||
first_alg = *algs->list.array;
|
||||
|
||||
hnb_rx_secmode_cmd(hnb, *first_alg);
|
||||
return;
|
||||
|
||||
case RANAP_ProcedureCode_id_Iu_Release:
|
||||
hnb_rx_iu_release(hnb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void hnb_rua_cl_handle_ranap(void *ctx, struct ranap_message_s *ranap_msg)
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)ctx;
|
||||
char imsi[16];
|
||||
|
||||
LOGP(DRANAP, LOGL_INFO, "rx ranap_msg->procedureCode %d\n",
|
||||
ranap_msg->procedureCode);
|
||||
|
||||
switch (ranap_msg->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_Paging:
|
||||
if (ranap_msg->msg.pagingIEs.permanentNAS_UE_ID.present == RANAP_PermanentNAS_UE_ID_PR_iMSI) {
|
||||
ranap_bcd_decode(imsi, sizeof(imsi),
|
||||
ranap_msg->msg.pagingIEs.permanentNAS_UE_ID.choice.iMSI.buf,
|
||||
ranap_msg->msg.pagingIEs.permanentNAS_UE_ID.choice.iMSI.size);
|
||||
} else imsi[0] = '\0';
|
||||
|
||||
LOGP(DRANAP, LOGL_INFO, "rx Paging: presence=%hx domain=%ld IMSI=%s\n",
|
||||
ranap_msg->msg.pagingIEs.presenceMask,
|
||||
ranap_msg->msg.pagingIEs.cN_DomainIndicator,
|
||||
imsi
|
||||
);
|
||||
|
||||
hnb_rx_paging(hnb, imsi);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct msgb *gen_initue_lu(const struct hnb *hnb, int is_ps, uint32_t conn_id, const char *imsi)
|
||||
{
|
||||
uint8_t lu[] = { GSM48_PDISC_MM, GSM48_MT_MM_LOC_UPD_REQUEST,
|
||||
0x70, 0x62, 0xf2, 0x30, 0xff, 0xf3, 0x57,
|
||||
/* len, IMSI/type, IMSI-------------------------------- */
|
||||
0x08, 0x29, 0x26, 0x24, 0x10, 0x32, 0x54, 0x76, 0x98,
|
||||
0x33, 0x03, 0x57, 0x18 , 0xb2 };
|
||||
uint8_t plmn_id[3];
|
||||
osmo_plmn_to_bcd(plmn_id, &hnb->plmn);
|
||||
RANAP_GlobalRNC_ID_t rnc_id = {
|
||||
.rNC_ID = g_hnb->rnc_id,
|
||||
.pLMNidentity.buf = plmn_id,
|
||||
.pLMNidentity.size = sizeof(plmn_id),
|
||||
};
|
||||
|
||||
/* FIXME: patch imsi */
|
||||
/* Note: the Mobile Identitiy IE's IMSI data has the identity type and
|
||||
* an even/odd indicator bit encoded in the first octet. So the first
|
||||
* octet looks like this:
|
||||
*
|
||||
* 8 7 6 5 | 4 | 3 2 1
|
||||
* IMSI-digit | even/odd | type
|
||||
*
|
||||
* followed by the remaining IMSI digits.
|
||||
* If digit count is even (bit 4 == 0), that first high-nibble is 0xf.
|
||||
* (derived from Iu pcap Location Update Request msg and TS 25.413)
|
||||
*
|
||||
* TODO I'm only 90% sure about this
|
||||
*/
|
||||
|
||||
return ranap_new_msg_initial_ue(conn_id, is_ps, &rnc_id, lu, sizeof(lu));
|
||||
}
|
||||
|
||||
void hnb_tx_iu_release_req(struct hnb *hnb)
|
||||
{
|
||||
RANAP_Cause_t cause = {
|
||||
.present = RANAP_Cause_PR_radioNetwork,
|
||||
.choice.transmissionNetwork = RANAP_CauseRadioNetwork_release_due_to_UE_generated_signalling_connection_release,
|
||||
};
|
||||
hnb_tx_dt(hnb, ranap_new_msg_iu_rel_req(&cause));
|
||||
}
|
||||
|
||||
void hnb_tx_iu_release_compl(struct hnb *hnb)
|
||||
{
|
||||
hnb_tx_dt(hnb, ranap_new_msg_iu_rel_compl());
|
||||
}
|
||||
|
||||
void hnb_rx_secmode_cmd(struct hnb *hnb, long ip_alg)
|
||||
{
|
||||
LOGP(DRANAP, LOGL_INFO, " :) Security Mode Command :)\n");
|
||||
/* not caring about encryption yet, just pass 0 for No Encryption. */
|
||||
hnb_tx_dt(hnb, ranap_new_msg_sec_mod_compl(ip_alg, 0));
|
||||
}
|
||||
|
||||
void hnb_rx_iu_release(struct hnb *hnb)
|
||||
{
|
||||
hnb_tx_iu_release_compl(hnb);
|
||||
}
|
||||
|
||||
void hnb_rx_paging(struct hnb *hnb, const char *imsi)
|
||||
{
|
||||
LOGP(DRANAP, LOGL_INFO, " :) Paging Request for %s :)\n", imsi);
|
||||
/* TODO reply */
|
||||
return msgb_alloc(1000, "ranap_tx");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,363 @@
|
|||
/* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <osmocom/gsm/prim.h>
|
||||
#include <osmocom/gsm/iuup.h>
|
||||
#include <osmocom/trau/osmo_ortp.h>
|
||||
|
||||
#include <osmocom/hnodeb/rtp.h>
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
|
||||
#define HNB_IUUP_MSGB_SIZE 4096
|
||||
|
||||
static struct osmo_iuup_rnl_prim *llsk_audio_ce_to_iuup_rnl_cfg(struct rtp_conn *conn, const struct hnb_audio_conn_establish_req_param *ce_req)
|
||||
{
|
||||
struct osmo_iuup_rnl_prim *irp;
|
||||
struct osmo_iuup_rnl_config *cfg;
|
||||
unsigned int i;
|
||||
const struct hnb_audio_conn_establish_req_param_v0 *v0 = &ce_req->v0;
|
||||
const struct hnb *hnb = conn->ue->hnb;
|
||||
|
||||
irp = osmo_iuup_rnl_prim_alloc(conn, OSMO_IUUP_RNL_CONFIG, PRIM_OP_REQUEST, HNB_IUUP_MSGB_SIZE);
|
||||
cfg = &irp->u.config;
|
||||
cfg->transparent = !!v0->transparent;
|
||||
cfg->active = true;
|
||||
cfg->data_pdu_type = v0->data_pdu_type;
|
||||
cfg->supported_versions_mask = v0->supported_versions_mask;
|
||||
cfg->num_rfci = v0->num_rfci;
|
||||
cfg->num_subflows = v0->num_subflows;
|
||||
cfg->IPTIs_present = v0->IPTIs_present;
|
||||
OSMO_ASSERT(cfg->num_rfci <= ARRAY_SIZE(cfg->rfci));
|
||||
OSMO_ASSERT(cfg->num_subflows <= ARRAY_SIZE(cfg->rfci[0].subflow_sizes));
|
||||
for (i = 0; i < cfg->num_rfci; i++) {
|
||||
cfg->rfci[i].used = true;
|
||||
/* llsk_audio v0 doesn't provide info, assume RFC ID from position: */
|
||||
cfg->rfci[i].id = (hnb->llsk.sapi_version_audio > 0) ? ce_req->v1.rfci[i] : i;
|
||||
if (cfg->IPTIs_present)
|
||||
cfg->rfci[i].IPTI = v0->IPTIs[i];
|
||||
if (cfg->num_subflows > 0)
|
||||
memcpy(&cfg->rfci[i].subflow_sizes[0], &v0->subflow_sizes[i][0], cfg->num_subflows*sizeof(uint16_t));
|
||||
}
|
||||
|
||||
cfg->t_init = (struct osmo_iuup_rnl_config_timer){ .t_ms = IUUP_TIMER_INIT_T_DEFAULT, .n_max = IUUP_TIMER_INIT_N_DEFAULT };
|
||||
cfg->t_ta = (struct osmo_iuup_rnl_config_timer){ .t_ms = IUUP_TIMER_TA_T_DEFAULT, .n_max = IUUP_TIMER_TA_N_DEFAULT };
|
||||
cfg->t_rc = (struct osmo_iuup_rnl_config_timer){ .t_ms = IUUP_TIMER_RC_T_DEFAULT, .n_max = IUUP_TIMER_RC_N_DEFAULT };
|
||||
|
||||
return irp;
|
||||
}
|
||||
|
||||
static int _iuup_user_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
|
||||
{
|
||||
struct rtp_conn *conn = (struct rtp_conn *)ctx;
|
||||
struct osmo_iuup_rnl_prim *irp = (struct osmo_iuup_rnl_prim *)oph;
|
||||
struct msgb *msg = oph->msg;
|
||||
int rc;
|
||||
|
||||
switch (OSMO_PRIM_HDR(&irp->oph)) {
|
||||
case OSMO_PRIM(OSMO_IUUP_RNL_DATA, PRIM_OP_INDICATION):
|
||||
rc = llsk_audio_tx_conn_data_ind(conn, irp->u.data.frame_nr, irp->u.data.fqc,
|
||||
irp->u.data.rfci, msgb_l3(msg), msgb_l3len(msg));
|
||||
break;
|
||||
default:
|
||||
LOGUE(conn->ue, DRTP, LOGL_NOTICE, "Rx Unknown prim=%u op=%u from IuUP layer",
|
||||
irp->oph.primitive, irp->oph.operation);
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int _iuup_transport_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
|
||||
{
|
||||
struct rtp_conn *conn = (struct rtp_conn *)ctx;
|
||||
struct msgb *msg = oph->msg;
|
||||
int rc;
|
||||
|
||||
rc = osmo_rtp_send_frame_ext(conn->socket, msgb_l2(msg), msgb_l2len(msg),
|
||||
GSM_RTP_DURATION, false);
|
||||
if (rc < 0) {
|
||||
LOGUE(conn->ue, DLLSK, LOGL_ERROR,
|
||||
"Rx IuUP Transport UNITDATA.req: Failed sending RTP frame! id=%u data_len=%u\n",
|
||||
conn->id, msgb_l2len(msg));
|
||||
}
|
||||
|
||||
msgb_free(msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct rtp_conn *rtp_conn_alloc(struct hnb_ue *ue)
|
||||
{
|
||||
struct rtp_conn *conn;
|
||||
char iuup_id[64];
|
||||
|
||||
conn = talloc_zero(ue, struct rtp_conn);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
|
||||
conn->ue = ue;
|
||||
|
||||
snprintf(iuup_id, sizeof(iuup_id), "ue-%u", conn->ue->conn_id);
|
||||
conn->iui = osmo_iuup_instance_alloc(conn, iuup_id);
|
||||
osmo_iuup_instance_set_user_prim_cb(conn->iui, _iuup_user_prim_cb, conn);
|
||||
osmo_iuup_instance_set_transport_prim_cb(conn->iui, _iuup_transport_prim_cb, conn);
|
||||
|
||||
llist_add(&conn->list, &ue->conn_cs.conn_list);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
void rtp_conn_free(struct rtp_conn *conn)
|
||||
{
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
if (conn->socket) {
|
||||
osmo_rtp_socket_free(conn->socket);
|
||||
conn->socket = NULL;
|
||||
}
|
||||
if (conn->iui) {
|
||||
osmo_iuup_instance_free(conn->iui);
|
||||
conn->iui = NULL;
|
||||
}
|
||||
llist_del(&conn->list);
|
||||
talloc_free(conn);
|
||||
}
|
||||
|
||||
/* Mixture between osmo_rtp_get_bound_addr and osmo_rtp_get_bound_ip_port using osmo_sockaddr */
|
||||
/*static int rtp_get_bound_addr(struct osmo_rtp_socket *rs, struct osmo_sockaddr *osa)
|
||||
{
|
||||
int rc;
|
||||
socklen_t alen = sizeof(*osa);
|
||||
|
||||
rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&osa->u.sa, &alen);
|
||||
if (rc < 0) {
|
||||
LOGP(DRTP, LOGL_ERROR, "getsockname(%d) failed: %s\n",
|
||||
rs->rtp_bfd.fd, strerror(errno));
|
||||
return rc;
|
||||
}
|
||||
LOGP(DRTP, LOGL_DEBUG, "rtp socket: %s\n", osmo_sock_get_name2(rs->rtp_bfd.fd));
|
||||
return 0;
|
||||
}*/
|
||||
|
||||
/* osmo_rtp_socket_connect() is broken, OS#5356 */
|
||||
static int rtp_get_bound_addr(struct osmo_rtp_socket *rs, const struct osmo_sockaddr *rem_addr, struct osmo_sockaddr *osa)
|
||||
{
|
||||
int rc;
|
||||
uint16_t port;
|
||||
socklen_t alen = sizeof(*osa);
|
||||
|
||||
/* First, retrieve bound port using getsockname: */
|
||||
rc = getsockname(rs->rtp_bfd.fd, (struct sockaddr *)&osa->u.sa, &alen);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
switch (osa->u.sa.sa_family) {
|
||||
case AF_INET6:
|
||||
port = ntohs(osa->u.sin6.sin6_port);
|
||||
break;
|
||||
case AF_INET:
|
||||
port = ntohs(osa->u.sin.sin_port);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* osmo_rtp_socket_connect() is broken, OS#5356, so we didn't connect()
|
||||
* and hence local_addr may still be unresolved (0.0.0.0) in the socket.
|
||||
* let's get it from OS regular routing: */
|
||||
rc = osmo_sockaddr_local_ip(osa, rem_addr);
|
||||
if (rc < 0) {
|
||||
LOGP(DRTP, LOGL_ERROR, "osmo_sockaddr_local_ip(%d) failed: err=%d\n",
|
||||
rs->rtp_bfd.fd, -rc);
|
||||
return rc;
|
||||
}
|
||||
/* Copy back the correct port to the returned address: */
|
||||
switch (osa->u.sa.sa_family) {
|
||||
case AF_INET6:
|
||||
osa->u.sin6.sin6_port = htons(port);
|
||||
break;
|
||||
case AF_INET:
|
||||
osa->u.sin.sin_port = htons(port);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtp_bind(struct hnb *hnb, struct osmo_rtp_socket *rs, const char *ip)
|
||||
{
|
||||
int rc;
|
||||
unsigned int i;
|
||||
unsigned int tries;
|
||||
|
||||
tries = (hnb->rtp.port_range_end - hnb->rtp.port_range_start) / 2;
|
||||
for (i = 0; i < tries; i++) {
|
||||
uint16_t port;
|
||||
|
||||
if (hnb->rtp.port_range_next >= hnb->rtp.port_range_end)
|
||||
hnb->rtp.port_range_next = hnb->rtp.port_range_start;
|
||||
|
||||
port = hnb->rtp.port_range_next;
|
||||
rc = osmo_rtp_socket_bind(rs, ip, port);
|
||||
|
||||
hnb->rtp.port_range_next += 2;
|
||||
|
||||
if (rc != 0)
|
||||
continue;
|
||||
|
||||
if (hnb->rtp.ip_dscp != -1) {
|
||||
if (osmo_rtp_socket_set_dscp(rs, hnb->rtp.ip_dscp))
|
||||
LOGP(DRTP, LOGL_ERROR, "failed to set DSCP=%d: %s\n",
|
||||
hnb->rtp.ip_dscp, strerror(errno));
|
||||
}
|
||||
if (hnb->rtp.priority != -1) {
|
||||
if (osmo_rtp_socket_set_priority(rs, hnb->rtp.priority))
|
||||
LOGP(DRTP, LOGL_ERROR, "failed to set socket priority %d: %s\n",
|
||||
hnb->rtp.priority, strerror(errno));
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl,
|
||||
unsigned int rtp_pl_len, uint16_t seq_number,
|
||||
uint32_t timestamp, bool marker)
|
||||
{
|
||||
struct rtp_conn *conn = (struct rtp_conn *)rs->priv;
|
||||
struct osmo_iuup_tnl_prim *itp;
|
||||
int rc;
|
||||
|
||||
LOGUE(conn->ue, DRTP, LOGL_DEBUG, "Rx RTP seq=%u ts=%u M=%u pl=%p len=%u\n",
|
||||
seq_number, timestamp, marker, rtp_pl, rtp_pl_len);
|
||||
|
||||
itp = osmo_iuup_tnl_prim_alloc(conn, OSMO_IUUP_TNL_UNITDATA, PRIM_OP_INDICATION, HNB_IUUP_MSGB_SIZE);
|
||||
itp->oph.msg->l2h = msgb_put(itp->oph.msg, rtp_pl_len);
|
||||
memcpy(itp->oph.msg->l2h, rtp_pl, rtp_pl_len);
|
||||
rc = osmo_iuup_tnl_prim_up(conn->iui, itp);
|
||||
if (rc < 0)
|
||||
LOGUE(conn->ue, DRTP, LOGL_NOTICE,
|
||||
"Failed passing rx rtp up to IuUP layer: %d\n", rc);
|
||||
}
|
||||
|
||||
int rtp_conn_setup(struct rtp_conn *conn, const char *local_ipstr, const struct osmo_sockaddr *rem_addr,
|
||||
const struct hnb_audio_conn_establish_req_param *ce_req)
|
||||
{
|
||||
int rc;
|
||||
char cname[256+4];
|
||||
char name[32];
|
||||
struct osmo_rtp_socket *rs;
|
||||
char remote_ipstr[INET6_ADDRSTRLEN];
|
||||
uint16_t remote_port;
|
||||
struct osmo_iuup_rnl_prim *irp;
|
||||
struct hnb_ue *ue = conn->ue;
|
||||
struct hnb *hnb = ue->hnb;
|
||||
|
||||
if (osmo_sockaddr_to_str_and_uint(remote_ipstr, sizeof(remote_ipstr), &remote_port, &rem_addr->u.sa) == 0) {
|
||||
LOGUE(ue, DRTP, LOGL_ERROR, "Failed parsing remote address!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
conn->rem_addr = *rem_addr;
|
||||
|
||||
rs = conn->socket = osmo_rtp_socket_create(ue, 0);
|
||||
rc = osmo_rtp_socket_set_param(rs,
|
||||
hnb->rtp.jitter_adaptive ?
|
||||
OSMO_RTP_P_JIT_ADAP :
|
||||
OSMO_RTP_P_JITBUF,
|
||||
hnb->rtp.jitter_buf_ms);
|
||||
if (rc < 0) {
|
||||
LOGUE(ue, DRTP, LOGL_ERROR, "Failed to set RTP socket parameters: %s\n", strerror(-rc));
|
||||
goto free_ret;
|
||||
}
|
||||
/* TS 25.414 Section 5.1.3.3.1.6: A dynamic Payload Type (IETF RFC 1890
|
||||
* [23]) shall be used. Values in the Range between 96 and 127 shall be
|
||||
* used. The value shall be ignored in the receiving entity. */
|
||||
rc = osmo_rtp_socket_set_pt(rs, 96);
|
||||
if (rc < 0) {
|
||||
LOGUE(ue, DRTP, LOGL_ERROR, "Failed to set RTP socket Payload-Type 96\n");
|
||||
/* Continue, the other side is anyway ignoring it... */
|
||||
}
|
||||
rs->priv = conn;
|
||||
rs->rx_cb = &rtp_rx_cb;
|
||||
|
||||
rc = rtp_bind(hnb, rs, local_ipstr);
|
||||
if (rc < 0) {
|
||||
LOGUE(ue, DRTP, LOGL_ERROR, "Failed to bind RTP/RTCP sockets\n");
|
||||
goto free_ret;
|
||||
}
|
||||
conn->id = rc; /* We use local port as rtp conn ID */
|
||||
osmo_sockaddr_from_str_and_uint(&conn->loc_addr, local_ipstr, rc);
|
||||
|
||||
/* Ensure RTCP SDES contains some useful information */
|
||||
snprintf(cname, sizeof(cname), "hnb@%s", local_ipstr);
|
||||
snprintf(name, sizeof(name), "ue@%u-%u", conn->ue->conn_id, conn->id);
|
||||
osmo_rtp_set_source_desc(rs, cname, name, NULL, NULL, NULL,
|
||||
"OsmoHNodeB-" PACKAGE_VERSION, NULL);
|
||||
|
||||
rc = osmo_rtp_socket_connect(rs, remote_ipstr, remote_port);
|
||||
if (rc < 0) {
|
||||
LOGUE(ue, DRTP, LOGL_ERROR, "Failed to connect RTP/RTCP sockets\n");
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
if (osmo_sockaddr_is_any(&conn->loc_addr)) {
|
||||
/* osmo_rtp_socket_connect() is broken, OS#5356 */
|
||||
//rc = rtp_get_bound_addr(rs, &conn->loc_addr);
|
||||
rc = rtp_get_bound_addr(rs, rem_addr, &conn->loc_addr);
|
||||
if (rc < 0) {
|
||||
LOGUE(ue, DRTP, LOGL_ERROR, "Cannot obtain locally bound IP/port: %d\n", rc);
|
||||
goto free_ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now configure the IuUP layer: */
|
||||
irp = llsk_audio_ce_to_iuup_rnl_cfg(conn, ce_req);
|
||||
rc = osmo_iuup_rnl_prim_down(conn->iui, irp);
|
||||
if (rc < 0) {
|
||||
LOGUE(ue, DRTP, LOGL_ERROR, "Failed setting up IuUP layer: %d\n", rc);
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
return rc;
|
||||
free_ret:
|
||||
osmo_rtp_socket_free(conn->socket);
|
||||
conn->socket = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int rtp_conn_tx_data(struct rtp_conn *conn, uint8_t frame_nr, uint8_t fqc, uint8_t rfci, const uint8_t *data, unsigned int data_len)
|
||||
{
|
||||
struct osmo_iuup_rnl_prim *irp;
|
||||
|
||||
irp = osmo_iuup_rnl_prim_alloc(conn, OSMO_IUUP_RNL_DATA, PRIM_OP_REQUEST, HNB_IUUP_MSGB_SIZE);
|
||||
irp->u.data.rfci = rfci;
|
||||
irp->u.data.frame_nr = frame_nr;
|
||||
irp->u.data.fqc = fqc;
|
||||
irp->oph.msg->l3h = msgb_put(irp->oph.msg, data_len);
|
||||
memcpy(irp->oph.msg->l3h, data, data_len);
|
||||
return osmo_iuup_rnl_prim_down(conn->iui, irp);
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
#include <errno.h>
|
||||
|
||||
#include <asn1c/ANY.h>
|
||||
#include <asn1c/asn1helpers.h>
|
||||
|
||||
#include <osmocom/rua/rua_ies_defs.h>
|
||||
#include <osmocom/ranap/ranap_common_cn.h>
|
||||
|
@ -28,27 +29,26 @@
|
|||
|
||||
#include <osmocom/hnodeb/rua.h>
|
||||
#include <osmocom/hnodeb/ranap.h>
|
||||
#include <osmocom/hnodeb/iuh.h>
|
||||
#include <osmocom/hnodeb/hnodeb.h>
|
||||
|
||||
int hnb_tx_dt(struct hnb *hnb, struct msgb *txm)
|
||||
|
||||
struct msgb *hnb_rua_msgb_alloc(void)
|
||||
{
|
||||
struct hnb_chan *chan;
|
||||
struct msgb *rua;
|
||||
|
||||
chan = hnb->cs.chan;
|
||||
if (!chan) {
|
||||
LOGP(DRUA, LOGL_INFO, "hnb_nas_tx_tmsi_realloc_compl(): No CS channel established yet.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rua = rua_new_dt(chan->is_ps, chan->conn_id, txm);
|
||||
hnb_iuh_send(hnb, rua);
|
||||
return 0;
|
||||
return msgb_alloc(1000, "rua_tx");
|
||||
}
|
||||
|
||||
static void hnb_rua_dt_handle(struct hnb *hnb, ANY_t *in)
|
||||
{
|
||||
RUA_DirectTransferIEs_t ies;
|
||||
int rc;
|
||||
struct hnb_ue *ue;
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
uint32_t context_id;
|
||||
bool is_ps;
|
||||
uint8_t *ranap_buf;
|
||||
size_t ranap_buf_len;
|
||||
bool *conn_est_cnf_pending;
|
||||
|
||||
rc = rua_decode_directtransferies(&ies, in);
|
||||
if (rc < 0) {
|
||||
|
@ -56,8 +56,44 @@ static void hnb_rua_dt_handle(struct hnb *hnb, ANY_t *in)
|
|||
return;
|
||||
}
|
||||
|
||||
rc = ranap_cn_rx_co(hnb_rua_dt_handle_ranap, hnb, ies.ranaP_Message.buf, ies.ranaP_Message.size);
|
||||
context_id = asn1bitstr_to_u24(&ies.context_ID);
|
||||
is_ps = (ies.cN_DomainIndicator == RUA_CN_DomainIndicator_ps_domain);
|
||||
ranap_buf = ies.ranaP_Message.buf;
|
||||
ranap_buf_len = ies.ranaP_Message.size;
|
||||
|
||||
LOGP(DRUA, LOGL_DEBUG, "Rx RUA DT context_id=%u is_ps=%u ranap_len=%zu\n",
|
||||
context_id, is_ps, ranap_buf_len);
|
||||
|
||||
if (!(ue = hnb_find_ue_by_id(hnb, context_id))) {
|
||||
LOGP(DRUA, LOGL_ERROR, "Rx RUA DT: Failed finding ue context_id=%u is_ps=%u\n",
|
||||
context_id, is_ps);
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
conn_est_cnf_pending = is_ps ? &ue->conn_ps.conn_est_cnf_pending :
|
||||
&ue->conn_cs.conn_est_cnf_pending;
|
||||
if (*conn_est_cnf_pending) {
|
||||
*conn_est_cnf_pending = false;
|
||||
LOGP(DLLSK, LOGL_INFO, "Tx IUH-CONN_ESTABLISH.cnf context_id=%u is_ps=%u\n",
|
||||
context_id, is_ps);
|
||||
iuh_prim = hnb_iuh_makeprim_conn_establish_cnf(context_id, is_ps, 0);
|
||||
if ((rc = osmo_prim_srv_send(hnb->llsk.srv, iuh_prim->hdr.msg)) < 0) {
|
||||
LOGP(DRUA, LOGL_ERROR, "Failed sending IUH-CONN_ESTABLISH.cnf context_id=%u is_ps=%u\n",
|
||||
context_id, is_ps);
|
||||
goto free_ret;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Tx IUH-CONN_DATA.ind context_id=%u is_ps=%u ranap_len=%zu\n",
|
||||
context_id, is_ps, ranap_buf_len);
|
||||
iuh_prim = hnb_iuh_makeprim_conn_data_ind(context_id, is_ps, ranap_buf, ranap_buf_len);
|
||||
if ((rc = osmo_prim_srv_send(hnb->llsk.srv, iuh_prim->hdr.msg)) < 0) {
|
||||
LOGP(DRUA, LOGL_ERROR, "Failed sending IUH-CONN_DATA.ind context_id=%u is_ps=%u ranap_len=%zu\n",
|
||||
context_id, is_ps, ranap_buf_len);
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
free_ret:
|
||||
/* FIXME: what to do with the asn1c-allocated memory */
|
||||
rua_free_directtransferies(&ies);
|
||||
}
|
||||
|
@ -66,19 +102,87 @@ static void hnb_rua_cl_handle(struct hnb *hnb, ANY_t *in)
|
|||
{
|
||||
RUA_ConnectionlessTransferIEs_t ies;
|
||||
int rc;
|
||||
struct hnb_iuh_prim *iuh_prim;
|
||||
uint8_t *ranap_buf;
|
||||
size_t ranap_buf_len;
|
||||
|
||||
rc = rua_decode_connectionlesstransferies(&ies, in);
|
||||
if (rc < 0) {
|
||||
LOGP(DRUA, LOGL_INFO, "failed to decode RUA CL IEs\n");
|
||||
return;
|
||||
}
|
||||
ranap_buf = ies.ranaP_Message.buf;
|
||||
ranap_buf_len = ies.ranaP_Message.size;
|
||||
|
||||
rc = ranap_cn_rx_cl(hnb_rua_cl_handle_ranap, hnb, ies.ranaP_Message.buf, ies.ranaP_Message.size);
|
||||
LOGP(DRUA, LOGL_DEBUG, "Rx RUA UDT ranap_len=%zu\n", ranap_buf_len);
|
||||
if (!hnb->llsk.srv) {
|
||||
LOGP(DRUA, LOGL_NOTICE, "Discarding rx RUA UDT ranap_len=%zu due to lower layers not available\n",
|
||||
ranap_buf_len);
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
LOGP(DLLSK, LOGL_DEBUG, "Tx IUH-UNITDATA.ind ranap_len=%zu\n", ranap_buf_len);
|
||||
iuh_prim = hnb_iuh_makeprim_unitdata_ind(ranap_buf, ranap_buf_len);
|
||||
if ((rc = osmo_prim_srv_send(hnb->llsk.srv, iuh_prim->hdr.msg)) < 0) {
|
||||
LOGP(DRUA, LOGL_ERROR, "Failed sending IUH-CONN_DATA.ind ranap_len=%zu\n",
|
||||
ranap_buf_len);
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
free_ret:
|
||||
/* FIXME: what to do with the asn1c-allocated memory */
|
||||
rua_free_connectionlesstransferies(&ies);
|
||||
}
|
||||
|
||||
|
||||
static int hnb_rua_rx_initiating(struct hnb *hnb, RUA_InitiatingMessage_t *init)
|
||||
{
|
||||
switch (init->procedureCode) {
|
||||
case RUA_ProcedureCode_id_ConnectionlessTransfer:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx Initiating ConnectionlessTransfer\n");
|
||||
hnb_rua_cl_handle(hnb, &init->value);
|
||||
break;
|
||||
case RUA_ProcedureCode_id_DirectTransfer:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx Initiating DirectTransfer\n");
|
||||
hnb_rua_dt_handle(hnb, &init->value);
|
||||
break;
|
||||
default:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx unknown Initiating message\n");
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hnb_rua_rx_successful(struct hnb *hnb, RUA_SuccessfulOutcome_t *success)
|
||||
{
|
||||
switch (success->procedureCode) {
|
||||
case RUA_ProcedureCode_id_ConnectionlessTransfer:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx SuccessfulOutcome ConnectionlessTransfer\n");
|
||||
hnb_rua_cl_handle(hnb, &success->value);
|
||||
break;
|
||||
case RUA_ProcedureCode_id_Connect:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx SuccessfulOutcome Connect\n");
|
||||
break;
|
||||
case RUA_ProcedureCode_id_DirectTransfer:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx SuccessfulOutcome DirectTransfer\n");
|
||||
hnb_rua_dt_handle(hnb, &success->value);
|
||||
break;
|
||||
case RUA_ProcedureCode_id_Disconnect:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx SuccessfulOutcome Disconnect\n");
|
||||
break;
|
||||
case RUA_ProcedureCode_id_ErrorIndication:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx SuccessfulOutcome ErrorIndication\n");
|
||||
break;
|
||||
case RUA_ProcedureCode_id_privateMessage:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx SuccessfulOutcome privateMessage\n");
|
||||
break;
|
||||
default:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx unknown SuccessfulOutcome message\n");
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hnb_rua_rx(struct hnb *hnb, struct msgb *msg)
|
||||
{
|
||||
RUA_RUA_PDU_t _pdu, *pdu = &_pdu;
|
||||
|
@ -94,11 +198,9 @@ int hnb_rua_rx(struct hnb *hnb, struct msgb *msg)
|
|||
|
||||
switch (pdu->present) {
|
||||
case RUA_RUA_PDU_PR_successfulOutcome:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA_RUA_PDU_PR_successfulOutcome\n");
|
||||
break;
|
||||
return hnb_rua_rx_successful(hnb, &pdu->choice.successfulOutcome);
|
||||
case RUA_RUA_PDU_PR_initiatingMessage:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA_RUA_PDU_PR_initiatingMessage\n");
|
||||
break;
|
||||
return hnb_rua_rx_initiating(hnb, &pdu->choice.initiatingMessage);
|
||||
case RUA_RUA_PDU_PR_NOTHING:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA_RUA_PDU_PR_NOTHING\n");
|
||||
break;
|
||||
|
@ -110,31 +212,5 @@ int hnb_rua_rx(struct hnb *hnb, struct msgb *msg)
|
|||
break;
|
||||
}
|
||||
|
||||
switch (pdu->choice.successfulOutcome.procedureCode) {
|
||||
case RUA_ProcedureCode_id_ConnectionlessTransfer:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx Connectionless Transfer\n");
|
||||
hnb_rua_cl_handle(hnb, &pdu->choice.successfulOutcome.value);
|
||||
break;
|
||||
case RUA_ProcedureCode_id_Connect:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx Connect\n");
|
||||
break;
|
||||
case RUA_ProcedureCode_id_DirectTransfer:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx DirectTransfer\n");
|
||||
hnb_rua_dt_handle(hnb, &pdu->choice.successfulOutcome.value);
|
||||
break;
|
||||
case RUA_ProcedureCode_id_Disconnect:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx Disconnect\n");
|
||||
break;
|
||||
case RUA_ProcedureCode_id_ErrorIndication:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx ErrorIndication\n");
|
||||
break;
|
||||
case RUA_ProcedureCode_id_privateMessage:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx privateMessage\n");
|
||||
break;
|
||||
default:
|
||||
LOGP(DRUA, LOGL_INFO, "RUA rx unknown message\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/ranap_msg_factory.h>
|
||||
|
||||
#include <osmocom/hnodeb/iuh.h>
|
||||
#include <osmocom/hnodeb/hnbap.h>
|
||||
#include <osmocom/hnodeb/ranap.h>
|
||||
#include <osmocom/hnodeb/vty.h>
|
||||
|
@ -46,6 +47,14 @@ int hnb_vty_go_parent(struct vty *vty)
|
|||
vty->node = HNODEB_NODE;
|
||||
vty->index = g_hnb;
|
||||
break;
|
||||
case LLSK_NODE:
|
||||
vty->node = HNODEB_NODE;
|
||||
vty->index = g_hnb;
|
||||
break;
|
||||
case GTP_NODE:
|
||||
vty->node = HNODEB_NODE;
|
||||
vty->index = g_hnb;
|
||||
break;
|
||||
case HNODEB_NODE:
|
||||
vty->node = CONFIG_NODE;
|
||||
vty->index = g_hnb;
|
||||
|
@ -183,8 +192,8 @@ DEFUN_USRATTR(cfg_hnodeb_rac,
|
|||
DEFUN_USRATTR(cfg_hnodeb_sac,
|
||||
cfg_hnodeb_sac_cmd,
|
||||
0,
|
||||
"service_area_code <0-255>",
|
||||
"Set the Service Area Code (RAC) of this HnodeB\n" "SAC\n")
|
||||
"service_area_code <0-65535>",
|
||||
"Set the Service Area Code (SAC) of this HnodeB\n" "SAC\n")
|
||||
{
|
||||
struct hnb *hnb = (struct hnb *)vty->index;
|
||||
hnb->sac = atoi(argv[0]);
|
||||
|
@ -246,6 +255,63 @@ DEFUN(cfg_hnodeb_iuh_remote_port, cfg_hnodeb_iuh_remote_port_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static struct cmd_node llsk_node = {
|
||||
LLSK_NODE,
|
||||
"%s(config-ll-socket)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
#define LLSK_STR "Configure the Lower Layer Unix Domain Socket\n"
|
||||
|
||||
DEFUN(cfg_hnodeb_llsk,
|
||||
cfg_hnodeb_llsk_cmd,
|
||||
"ll-socket", LLSK_STR)
|
||||
{
|
||||
OSMO_ASSERT(g_hnb);
|
||||
vty->index = g_hnb;
|
||||
vty->node = LLSK_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_hnodeb_llsk_path, cfg_hnodeb_llsk_path_cmd,
|
||||
"path PATH",
|
||||
"Configure the Lower Layer Unix Domain Socket path\n"
|
||||
"UNIX socket path\n")
|
||||
{
|
||||
osmo_prim_srv_link_set_addr(g_hnb->llsk.link, argv[0]);
|
||||
|
||||
/* FIXME: re-open the interface? */
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static struct cmd_node gtp_node = {
|
||||
GTP_NODE,
|
||||
"%s(config-gtp)# ",
|
||||
1,
|
||||
};
|
||||
|
||||
#define GTP_STR "Configure the GPRS Tunnelling Protocol parameters\n"
|
||||
|
||||
DEFUN(cfg_hnodeb_gtp,
|
||||
cfg_hnodeb_gtp_cmd,
|
||||
"gtp", GTP_STR)
|
||||
{
|
||||
OSMO_ASSERT(g_hnb);
|
||||
vty->index = g_hnb;
|
||||
vty->node = GTP_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_hnodeb_gtp_local_ip, cfg_hnodeb_gtp_local_ip_cmd,
|
||||
"local-ip " VTY_IPV4_CMD,
|
||||
"Configure the GTP-U bind address\n"
|
||||
"GTP-U local IPv4 address\n")
|
||||
{
|
||||
osmo_talloc_replace_string(g_hnb, &g_hnb->gtp.cfg_local_addr, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static int config_write_hnodeb(struct vty *vty)
|
||||
{
|
||||
|
@ -265,48 +331,22 @@ static int config_write_hnodeb(struct vty *vty)
|
|||
vty_out(vty, " local-port %u%s", g_hnb->iuh.local_port, VTY_NEWLINE);
|
||||
vty_out(vty, " remote-ip %s%s", g_hnb->iuh.remote_addr, VTY_NEWLINE);
|
||||
vty_out(vty, " remote-port %u%s", g_hnb->iuh.remote_port, VTY_NEWLINE);
|
||||
vty_out(vty, " ll-socket%s", VTY_NEWLINE);
|
||||
vty_out(vty, " path %s%s", osmo_prim_srv_link_get_addr(g_hnb->llsk.link), VTY_NEWLINE);
|
||||
vty_out(vty, " gtp%s", VTY_NEWLINE);
|
||||
vty_out(vty, " local-ip %s%s", g_hnb->gtp.cfg_local_addr, VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static struct cmd_node chan_node = {
|
||||
CHAN_NODE,
|
||||
"%s(chan)> ",
|
||||
1,
|
||||
};
|
||||
|
||||
#define HNBAP_STR "HNBAP related commands\n"
|
||||
#define HNB_STR "HomeNodeB commands\n"
|
||||
#define UE_STR "User Equipment commands\n"
|
||||
#define RANAP_STR "RANAP related commands\n"
|
||||
#define CSPS_STR "Circuit Switched\n" "Packet Switched\n"
|
||||
|
||||
DEFUN(hnb_register, hnb_register_cmd,
|
||||
"hnbap hnb register", HNBAP_STR HNB_STR "Send HNB-REGISTER REQUEST")
|
||||
{
|
||||
hnb_send_register_req(g_hnb);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(hnb_deregister, hnb_deregister_cmd,
|
||||
"hnbap hnb deregister", HNBAP_STR HNB_STR "Send HNB-DEREGISTER REQUEST")
|
||||
{
|
||||
hnb_send_deregister_req(g_hnb);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(ue_register, ue_register_cmd,
|
||||
"hnbap ue register IMSI", HNBAP_STR UE_STR "Send UE-REGISTER REQUEST")
|
||||
{
|
||||
hnb_ue_register_tx(g_hnb, argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(asn_dbg, asn_dbg_cmd,
|
||||
"asn-debug (1|0)", "Enable or disable libasn1c debugging")
|
||||
"asn-debug (1|0)",
|
||||
"Enable or disable libasn1c debugging\n"
|
||||
"Enable libasn1c debugging\n"
|
||||
"Disable libasn1c debugging\n")
|
||||
{
|
||||
asn_debug = atoi(argv[0]);
|
||||
|
||||
|
@ -335,40 +375,6 @@ DEFUN(ranap_reset, ranap_reset_cmd,
|
|||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(chan, chan_cmd,
|
||||
"channel (cs|ps) lu imsi IMSI",
|
||||
"Open a new Signalling Connection\n"
|
||||
"To Circuit-Switched CN\n"
|
||||
"To Packet-Switched CN\n"
|
||||
"Performing a Location Update\n"
|
||||
)
|
||||
{
|
||||
struct hnb_chan *chan;
|
||||
struct msgb *msg, *rua;
|
||||
static uint16_t conn_id = 42;
|
||||
|
||||
chan = talloc_zero(tall_hnb_ctx, struct hnb_chan);
|
||||
if (!strcmp(argv[0], "ps"))
|
||||
chan->is_ps = 1;
|
||||
chan->imsi = talloc_strdup(chan, argv[1]);
|
||||
chan->conn_id = conn_id;
|
||||
conn_id++;
|
||||
|
||||
msg = gen_initue_lu(g_hnb, chan->is_ps, chan->conn_id, chan->imsi);
|
||||
rua = rua_new_conn(chan->is_ps, chan->conn_id, msg);
|
||||
|
||||
hnb_iuh_send(g_hnb, rua);
|
||||
|
||||
vty->index = chan;
|
||||
vty->node = CHAN_NODE;
|
||||
|
||||
if (!chan->is_ps)
|
||||
g_hnb->cs.chan = chan;
|
||||
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
void hnb_vty_init(void)
|
||||
{
|
||||
install_element(CONFIG_NODE, &cfg_hnodeb_cmd);
|
||||
|
@ -386,13 +392,13 @@ void hnb_vty_init(void)
|
|||
install_element(IUH_NODE, &cfg_hnodeb_iuh_local_port_cmd);
|
||||
install_element(IUH_NODE, &cfg_hnodeb_iuh_remote_ip_cmd);
|
||||
install_element(IUH_NODE, &cfg_hnodeb_iuh_remote_port_cmd);
|
||||
install_element(HNODEB_NODE, &cfg_hnodeb_llsk_cmd);
|
||||
install_node(&llsk_node, NULL);
|
||||
install_element(LLSK_NODE, &cfg_hnodeb_llsk_path_cmd);
|
||||
install_element(HNODEB_NODE, &cfg_hnodeb_gtp_cmd);
|
||||
install_node(>p_node, NULL);
|
||||
install_element(GTP_NODE, &cfg_hnodeb_gtp_local_ip_cmd);
|
||||
|
||||
install_element_ve(&asn_dbg_cmd);
|
||||
install_element_ve(&hnb_register_cmd);
|
||||
install_element_ve(&hnb_deregister_cmd);
|
||||
install_element_ve(&ue_register_cmd);
|
||||
install_element_ve(&ranap_reset_cmd);
|
||||
install_element_ve(&chan_cmd);
|
||||
|
||||
install_node(&chan_node, NULL);
|
||||
}
|
||||
|
|
|
@ -34,15 +34,14 @@ DISTCLEANFILES = \
|
|||
$(NULL)
|
||||
|
||||
if ENABLE_EXT_TESTS
|
||||
python-tests: $(BUILT_SOURCES)
|
||||
echo ""
|
||||
# TODO: Enable once we have a VTY/CTRL interface:
|
||||
# $(MAKE) vty-test
|
||||
# osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
# osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
python-tests: $(top_builddir)/src/osmo-hnodeb/osmo-hnodeb
|
||||
$(MAKE) vty-test
|
||||
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
|
||||
# TODO: CTRL interface is not implemented
|
||||
# $(srcdir)/ctrl_test_runner.py -w $(abs_top_builddir) -v
|
||||
else
|
||||
python-tests: $(BUILT_SOURCES)
|
||||
python-tests:
|
||||
echo "Not running python-based tests (determined at configure-time)"
|
||||
endif
|
||||
|
||||
|
@ -52,7 +51,7 @@ VTY_TEST ?= *.vty
|
|||
# To update the VTY script from current application behavior,
|
||||
# pass -u to vty_script_runner.py by doing:
|
||||
# make vty-test U=-u
|
||||
vty-test:
|
||||
vty-test: $(top_builddir)/src/osmo-hnodeb/osmo-hnodeb
|
||||
osmo_verify_transcript_vty.py -v \
|
||||
-n OsmoHNodeB -p 4273 \
|
||||
-r "$(top_builddir)/src/osmo-hnodeb/osmo-hnodeb -c $(top_srcdir)/doc/examples/osmo-hnodeb/osmo-hnodeb.cfg" \
|
||||
|
|
|
@ -154,22 +154,14 @@ class TestCtrlHNB(TestCtrlBase):
|
|||
"doc/examples/osmo-hnodeb/osmo-hnodeb.cfg"]
|
||||
|
||||
def ctrl_app(self):
|
||||
return (4249, "./src/osmo-hnodeb/osmo-hnodeb", "OsmoHNodeB", "hnb")
|
||||
return (4274, "./src/osmo-hnodeb/osmo-hnodeb", "OsmoHNodeB", "hnb")
|
||||
|
||||
def testCtrlErrs(self):
|
||||
r = self.do_get('invalid')
|
||||
self.assertEqual(r['mtype'], 'ERROR')
|
||||
self.assertEqual(r['error'], 'Command not found')
|
||||
|
||||
r = self.do_set('rf_locked', '999')
|
||||
self.assertEqual(r['mtype'], 'ERROR')
|
||||
self.assertEqual(r['error'], 'Value failed verification.')
|
||||
|
||||
r = self.do_get('bts')
|
||||
self.assertEqual(r['mtype'], 'ERROR')
|
||||
self.assertEqual(r['error'], 'Error while parsing the index.')
|
||||
|
||||
r = self.do_get('bts.999')
|
||||
r = self.do_get('hnodeb.999')
|
||||
self.assertEqual(r['mtype'], 'ERROR')
|
||||
self.assertEqual(r['error'], 'Error while resolving object')
|
||||
|
||||
|
|
Loading…
Reference in New Issue