Compare commits

...

58 Commits

Author SHA1 Message Date
Oliver Smith 1db9b60023 contrib: remove rpm spec file
Related: https://osmocom.org/news/255
Related: OS#6446
Change-Id: Idd67d52ca736c4e145387ea8d4030f9cf4b9596d
2024-05-08 14:40:59 +02:00
Oliver Smith d15ef114cc .deb/.rpm: various fixes related to non-root
* Explicitly chown /var/lib/osmocom to osmocom:osmocom, instead of
  relying on systemd to do it when the service starts up. This does not
  work with the systemd versions in debian 10 and almalinux 8.
* deb: Use "useradd" instead of the interactive "adduser" perl script
  from Debian. This makes it consistent with how we do it in rpm, and
  avoids the dependency on "adduser".
* deb: Consistently use tabs through the file, instead of mixing tabs
  and spaces.
* deb: Remove support for the "dpkg-statoverride --list" logic. This
  seems to be a rather obscure feature to override permissions for
  certain files or directories, for which it does not seem to be a good
  idea to make the postinst script less maintainable. Something similar
  can be achieved by using your own Osmocom config file in a different
  path with different permissions.

Related: OS#4107
Change-Id: I6dd0205fb65d4ad5a79821c111865e67fb293a73
2024-04-26 15:08:45 +02:00
Max 460b553971 .deb/.rpm: add osmocom user during package install
Create osmocom user & group during package installation.
Fix the configuration dir/files permission to match.

Related: OS#4107
Tweaked-By: Oliver Smith <osmith@sysmocom.de>
Change-Id: Ic24b43bde8d4af0641365f276106eb3cd016be2c
2024-04-24 11:52:29 +02:00
Pau Espin a9b33a40af Close LLSK during shutdown procedure
This way we notify the lower layers that state must be reset, eg. when
Iuh conn drops.

Related: OS#6826
Change-Id: I282f3e75d31b3e481136aade34b30074300ab631
2024-03-12 18:48:26 +01:00
Pau Espin b04885005b Free UE contexts when starting shutdown procedure
They are not valid anymore since the Iuh conn is closed.
If kept alive, then if GTP/RTP traffic comes from CN while shutting down
the payloads may be forwarded to LLSK which may have been closed (eg.
because shutdown procedure was started precisely due to death LLSK
because other end closed it).

Related: OS#6826
Change-Id: I51aa449e7fd5b1049fcaaeef3f1378423a838c05
2024-03-12 18:47:35 +01:00
Pau Espin ab8db8fabd llsk: Add APIs to interact with llsk->link
This also clarifies the type of available uses/features.

Change-Id: I7723467d34670f89710f55c0c6731e2e38560f35
2024-03-12 18:47:35 +01:00
Pau Espin d3cd9e7afa cosmetic: Fix typo in comment
Change-Id: Ie13952075be29201751d5b7884ca572a285ca9dd
2024-03-12 17:42:56 +01:00
Pau Espin 8ff216bf02 Name llsk prim_srv{_link}
This allows easily identifying the prim_srv{_link} in logs.

Depends: libosmo-netif.git Change-Id Iba683e4d65e0aba81e13bdf1b9d5a9065b1fc89c
Change-Id: Idae27bc9a2a223710edb9fca905ad5ae924dde80
2024-03-12 12:25:55 +01:00
Pau Espin 9f2a584fb6 iuh: Name stream_cli connection
This makes it easier to figure out the conn log lines.

Change-Id: I1503b322f8ff053088688b3088a70801d10b3d88
2024-03-12 12:03:16 +01:00
Andreas Eversberg 4bc90a6be7 Use uniform log format for default config files
Related: OS#6272
Change-Id: I239229c0f23f70a75155ca8790b51a7c39acca21
2023-12-01 12:47:33 +01:00
Harald Welte fed4bf196a Use 'iuh/local-ip' as local IP for RTP sockets
Don't use the wildcard IPv4 address for RTP sockets, but instead use
the address explicitly configured by the user for the Iuh interface.

Closes: SYS#6657
Change-Id: I90e2cbb1765d4d2db5a19f64f0ff09cdc18b7911
Depends: libosmocore.git Change-Id I6b5c0bf8ca97e6358d992fb2ff45ffd53ba15197
2023-11-21 19:53:02 +01:00
Vadim Yanitskiy b303e296b7 tests: enable commented-out 'python-tests' target
Keep the last line running the CTRL tests commented out as the
CTRL interface is not implemented yet.  Add a TODO.

Change-Id: I76e24184aecafa8460d1da9419a424d9990730c5
Related: OS#5989
2023-11-03 14:47:59 +07:00
Vadim Yanitskiy 11b379d95a tests/ctrl_test_runner.py: fix wrong CTRL port
Change-Id: I88432b77dae923df373055a657a77838268a32e1
Related: OS#5989
2023-11-03 14:47:57 +07:00
Vadim Yanitskiy be5af799c3 vty: fix incomplete docs for 'asn-debug' command
Change-Id: I7174e1833be206a62afd95ddc27e9d912a7efcfc
Related: OS#5989
2023-10-30 15:38:55 +07:00
Pau Espin 4f7362272f Bump version: 0.1.1.8-6968-dirty → 0.1.2
Change-Id: Ib6cba818c589f7c280827311de7e4e5568714f2a
2023-09-12 17:22:11 +02:00
Pau Espin 69682c8deb rua: Fix null ptr access in hnb->llsk.srv
At startup, osmo-hnodeb connects the Iuh socket to the HNBGW, and start
the LLSK unix socket link_server waiting for lower layers to connect.
If for some reason the lower layers don't connect and an Iuh messages
arrives which was to be forwarded to LLSK, it would try to access it
without first checking if it was already available.
This patch adds a check to avoid a crash when accessing the null
pointer.

Change-Id: I32d8f91b2fc5401ace386e1085c248476228cd18
2023-07-25 11:27:59 +02:00
Oliver Smith 4d94531f6f systemd: depend on networking-online.target
Related: SYS#6400
Change-Id: Ib6c78c76c5f13b9482428ce653a61b03b2aca1d3
2023-05-26 14:10:45 +02:00
Vadim Yanitskiy b4a5d3d9a8 copyright: fix typo: sysmocom s/s.m.f.c./s.f.m.c./ GmbH
Change-Id: I9ad500784d1c99cc764b38729ac0d86ab8441379
2023-05-18 18:46:29 +07:00
Oliver Smith eee4534364 debian: set compat level to 10
Related: OS#5958
Change-Id: I5d26ab03aacf3b8ef8c1c4c669c12090fd0b7899
2023-04-25 16:48:23 +02:00
Vadim Yanitskiy a2aa6b19b2 tests: do not depend on undefined $(BUILT_SOURCES)
Change-Id: I43c78aac0d23274d2a9bb01021cde0371c231be2
2023-03-30 03:02:45 +07:00
Vadim Yanitskiy 54899f76b0 tests: make 'vty-test' target depend on the respective binary
Change-Id: I53ff9a1659a0e25a6c3a5bc987dbae82d0312259
2023-03-30 03:01:22 +07:00
Vadim Yanitskiy 9b356e1c5c build: remove $(COVERAGE_LDFLAGS) from osmo_hnodeb_LDADD
Change-Id: Id088775f444640df863c346b225c1bae61725249
2023-03-30 03:00:24 +07:00
arehbein ad5981352b Transition to use of 'telnet_init_default'
Related: OS#5809
Change-Id: Icc57c68337d55c6594c1c36e9bf41624d11dab0a
2023-02-25 17:48:58 +01:00
Pau Espin 69f91b1773 Bump version: 0.1.0.3-1bad-dirty → 0.1.1
Change-Id: I6c1170506c76b794fd4e762b64adf63a039053ca
2023-02-07 18:03:49 +01:00
Oliver Smith 1bad165c69 contrib/jenkins.sh: use --enable-werror
Fixes: OS#5290
Change-Id: Ic32464b9a52a5de4ee81fc3a4dbb5717b3b43e39
2022-11-15 12:40:36 +01:00
Max 9c2c2654ba Set working directory in systemd service file
By default systemd will execute service with root directory (or home directory for user instance) which might result in
attempts to create files in unexpected place. Let's set it to 'osmocom' subdir of state directory (/var/lib for system instance) instead.

Related: OS#4821
Change-Id: I67564759f43193f4739b0b194d5f5da92e3bc6ae
2022-08-30 19:51:47 +07:00
Vadim Yanitskiy f74f944784 configure.ac: do not require unused dlopen
Change-Id: Idd4a816babcfb2e2302fbd21cb48989659940d5c
2022-08-19 20:29:35 +07:00
Pau Espin 8ffe838a81 Bump version: 0.0.1.30-5263-dirty → 0.1.0
Change-Id: I3c3eff6d3cd473a307b36bc78cdd0654c840b36e
2022-06-29 12:37:29 +02:00
Michael Iedema 52636f32c1 typo: SAC needs 16bit range, not 8bit as copied from RAC entry
Change-Id: I756fbab449151d912cde48f1da01d3b645b7627c
2022-06-23 02:04:23 -07:00
Pau Espin 19a475bcf4 llsk: Fix previous audio sapi version passed instead of current
This bug showed up when running the whole TTCN3 hnodeb-test testsuite.
llsk_audio_sapi_version_confirmed() was setting the expected struct size
fo the version previously set.

Related: SYS#5516
Change-Id: Ic94881f46b20ba2817b56d88ada9da5d13b1e294
2022-06-21 12:39:01 +02:00
Harald Welte 71f758939a update git URLs (git -> https; gitea)
Change-Id: I64be70776dd7f3eb691c4dc8810a746c096a50ae
2022-06-18 12:12:44 +02:00
Pau Espin f320ac5841 audio: Introduce SAPI v1 with unordered RFCI support
This commit introduces support for the new llsk_audio SAPI v1.
This new version is almost the same as v0, with the exception that
primitive <REQUEST,HNB_AUDIO_PRIM_CONN_ESTABLISH> wins an extra field
appended at the end:
"""
uint8_t rfci[HNB_MAX_RFCIS]; /* values range 6 bits */
"""

This allows lowerlayers to provide an unordered array of RFCIs, which
was seen may happen under some conditions. For instance:
"""
rfci[HNB_MAX_RFCIS] = { 2, 3, 1};
[0] RFCI 2
[1] RFCI 3
[2] RFCI 1
"""

OsmoHNodeB still supports v0 if the lowerlayer asks for it, and will
continue having the exact behavior as before in this case (using
position in the array as RFCI). Hence, no compatibility breakage occurs
in this patch. New clients, on the other hand, can provide the extra
information by announcing V1 support during version negotiation at
startup.

Related: SYS#5516
Change-Id: I860d18b80c1041bf63a1570d435e0568c0f6b01b
2022-06-08 18:38:37 +02:00
Pau Espin 780def0114 llsk: Validate received SAPI versions at startup
Change-Id: I172abfee3bfadb383aa6bce6fe76306291c7cd7f
2022-06-08 18:06:09 +02:00
Pau Espin 0843c8ae6e hnb: move llsk fields inside substruct
More fields will be added, let's better organize them this way.

Change-Id: Idfe644a7c2eccd94b602816a86e38b56264141af
2022-06-08 18:03:38 +02:00
Pau Espin fc660017e8 llsk: Fix typo in log message
Change-Id: I3c8be85e56df23cd68e58f0112238647add3d97f
2022-06-08 18:03:35 +02:00
Pau Espin 3dffff3147 rtp: Update code to libosmogsm IuUP API changes
NOTICE: This commit implies an API change when using libosmogsm.
However, the previous API was never available in any libosmogsm release,
and only available in both libosmogsm and osmo-hnodeb master, so we are
only breaking compatibility between different master versions, which is
acceptable.

Related: SYS#5969
Change-Id: Ia26a945147d68511bb1750d51ed91909e48b4139
2022-05-25 18:55:09 +02:00
Pau Espin 8875abd3f9 Do not turn some compiler warnings into errors by default
We build with --enable-werror during development and in CI. If the code
is built with a different compiler that throws additional warnings, it
should not stop the build.

This patch also effectively removes dependency on autoconf-archive.

Related: OS#5289
Related: SYS#5789
Change-Id: Ie30d8024ee2ad48073dd8be2e02856b6ce1a6b0b
2022-01-11 18:30:57 +01:00
Pau Espin 70b65c55f6 gtp: Fix uninitialized var if no extension flags set in gtp header
The logic was plain wrong, bug introduced while typing initial code.

Closes: Coverity CID#243535
Change-Id: I497667edd0571fff91ab41c8b57bdcf277e5988d
2022-01-07 13:44:31 +01:00
Pau Espin 14cceb5dfb Get rid of libgtp for GTP-U
It doesn't make much sense to pull whole libgtp to implement only the
user plan side of GTP. Let's drop it and add a minimal GTP-U
implementation here.

Related: SYS#5516
Change-Id: I53ad4915aaed3bc7574036e963be10514e370fe2
2021-12-23 15:55:21 +01:00
Pau Espin e4f127f1a3 Add initial support for IuUP RTP based Transport Layer
Use the recently introduced IuUP support in libosmocore to send and receive
proper IuUP over RTP instead of regular RTP.
The LLSK AUDIO primitives are updated to provide required information
back and forth.

Depends: libosmocore.git Change-Id Ibe356fa7b1abaca0091e368db8478e79c09c6cb0
Related: SYS#5516
Change-Id: Ie137809e1f91dfd702853c52be97ca79e8604eab
2021-12-23 15:55:17 +01:00
Pau Espin 387b892b1d Add example default config file
This config file is also installed and used by the systemd service by
default. Local ip address of 127.0.0.3 is used by default to avoid
collisions with default configs of osmo-sgsn and osmo-ggsn.

Related: OS#5360
Change-Id: I02d628f48ca5adbc46e75a015c39a6fb5614a2d6
2021-12-20 11:57:57 +01:00
Pau Espin 272f296cd3 gtp: Fix possible null pointer dereference
Closes: Coverity CID#242771
Change-Id: I412082cc5fa93818d321210c34a2d22c038cb985
2021-12-17 13:45:36 +01:00
Pau Espin 5869201039 contrib/osmo-hnodeb.spec.in: Fix missing dep libosmotrau
Change-Id: Ia40b6a28caecdd94ce3c53172fe531b12a0a221f
2021-12-17 13:40:41 +01:00
Pau Espin 64d8fd6298 llsk_audio: Fix false positive about uninitialized var access
Let's make gcc in Ubuntu 21.10 happy.

Change-Id: If085b0ca0e1d3e7237696eb67cf6301ea84793e0
2021-12-17 13:37:32 +01:00
Pau Espin 3363db5c90 llsk_gtp: Early return if conn alloc fails
Change-Id: Iaf597b76ceb9cce3f27c42625f963f740c8f213e
2021-12-17 13:35:36 +01:00
Pau Espin 381c4cccc5 rtp: Use dynamic Payload Type 96
Change-Id: I6ca83c274200d8fc0fb3d8914e970b05df91f6ea
2021-12-16 13:01:13 +01:00
Pau Espin 7550ba3d05 Update AUDIO and GTP SAPs to support multiple conns per UE
This is needed for instance:
* AUDIO: video calls
* GTP: secondary pdp contexts

For better abstraction, now both AUDIO and GTP conns use unique ID
namespaces, and one ID is used to identify each one. Each conn relate in
turn to a UE since a context_id is passed during connection
establishment.

Related: SYS#5516
Change-Id: Ib3f60d5ba21defe5259c25e2034fc2217c4d93df
2021-12-16 13:01:12 +01:00
Pau Espin ecc76ef69a Fix several Coverity found issues
Closes: CID#242770
Closes: CID#242769
Closes: CID#242767
Change-Id: I6d73a970919889953539fbacc601d679d7ec4113
2021-12-16 12:58:51 +01:00
Pau Espin a7dc04ef26 First implementation of the LLSK gtp SAPI
This first implementation uses libgtp to manage the GTPv1-U socket and
pdp contexts, as a proof of concept to have something working for now.
At a later step, it is expected to drop libgtp and use some adhoc code
to handle GTP-U.

Related: SYS#5516
Change-Id: I5a6f5dfc4e508c92adb35210b4dc576d64353366
2021-12-14 15:47:53 +01:00
Pau Espin b3af382157 First implementation of the LLSK audio SAPI
This first version can send and receive regular RTP, but does not talk IuUP.
Support for IuUP will be added in a future patch. This way we can
already test the whole LLSK<->RTP path.

Change-Id: I9909a7c054ddaabb1bb63d7d06331cc79f642b5d
2021-12-14 12:43:19 +01:00
Pau Espin cd1baf0649 Initial implementation of LowerLayer UD Socket
The LLSK socket is an interface allowing thid-parties to implement and
hook their RLC/MAC/RRC stack to osmo-hnodbe, which takes care of
interconnection with the rest of the RAN and core network (Iuh, RTP,
GTP-U, etc.).

Related: SYS#5516
Change-Id: Icaabb2206d6f141d4fba47dedf71f8ec37e6257d
2021-12-14 12:43:15 +01:00
Pau Espin d8da64bb46 Drop NAS and RANAP code and VTY commands
The higher level bits were used in originally imported hnb-test code in
order to control interaction with the HNBGW over the VTY. This is no
longer needed in osmo-hnodeb, as NAS is in layers above the ones being
handled by an hNodeB.
Regarding RANAP, most of it is going to be handled by the LowerLayer UD
socket peer to be introduced in the future, so we can drop most of it,
as in osmo-hnodeb we'll basically be speaking RUA to the HNBGW and
passing RANAP buffers transparently.
We keep the .c/.h files since some small helpers functions will be
ntroduced later on when adding support for the mentioned LL socket.

Related: SYS#5516
Change-Id: I6aae54734c1c4607beede19169f16bafe35b3a61
2021-11-30 11:02:52 +01:00
Pau Espin 5689081325 hnbap: Improve rx path and implement HnbRegisterReject
The process is shut down if we receive such a message.

Change-Id: Id4656140b7f51b07860dcbeed449ed68c3a9f85a
2021-11-24 21:31:32 +01:00
Pau Espin 807e52a2da Implement initial shutdown + reconnect FSM
The shutdown_fsm doesn't do much right now, but it's included there in
order to allow easy clean up of resources upon Iuh reconnect in the
future, as well as reset the lower layers.

Change-Id: Id35d692218af366843186b0966b62f65bb9eb620
2021-11-24 21:23:45 +01:00
Pau Espin fd440b7a67 Fix typos and non-related test content
Artifacts from initial commits adding the project skeleton.

Change-Id: I00cce8fbfeef9688dd2fd235cb859c27bd864e8b
2021-11-24 21:22:42 +01:00
Pau Espin 65489ed7c6 iuh: Improve logging of SCTP notifications
Change-Id: I08842665effec6e70a3c7f6b93ea62c45549d69d
2021-11-24 21:22:42 +01:00
Pau Espin 9828dbed18 Introduce SCTP log category
Change-Id: I2c9cb54958807bd68c04a4c0d3a0f355dd641282
2021-11-24 21:22:42 +01:00
Pau Espin 4310b0d57a Move Iuh code to its own module
The Iuh code will be further extended next to properly support
reconnect, and hnb will also gain proper shutdown support soon.

Change-Id: I6e94210ab06a34b70c61bb074c58d7b0f4ee75de
2021-11-24 21:22:38 +01:00
45 changed files with 3661 additions and 865 deletions

View File

@ -19,7 +19,6 @@ SUBDIRS = \
BUILT_SOURCES = $(top_srcdir)/.version
EXTRA_DIST = \
.version \
contrib/osmo-hnodeb.spec.in \
debian \
git-version-gen \
osmoappdesc.py \

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

73
debian/changelog vendored
View File

@ -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 ]

2
debian/compat vendored
View File

@ -1 +1 @@
9
10

22
debian/control vendored
View File

@ -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

2
debian/copyright vendored
View File

@ -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>

30
debian/postinst vendored Executable file
View File

@ -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#

View File

@ -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

86
doc/hnodeb.msc Normal file
View File

@ -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"];
}

View File

@ -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)

View File

@ -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);

View File

@ -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));

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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) \

View File

@ -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",
},
};

345
src/osmo-hnodeb/gtp.c Normal file
View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

226
src/osmo-hnodeb/iuh.c Normal file
View File

@ -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;
}

246
src/osmo-hnodeb/llsk.c Normal file
View File

@ -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);
}

View File

@ -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;
}
}

279
src/osmo-hnodeb/llsk_gtp.c Normal file
View File

@ -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;
}
}

347
src/osmo-hnodeb/llsk_iuh.c Normal file
View File

@ -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;
}
}

View File

@ -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();
/**

View File

@ -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;
}
}

View File

@ -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");
}

363
src/osmo-hnodeb/rtp.c Normal file
View File

@ -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);
}

View File

@ -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;
}

View File

@ -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(&gtp_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);
}

View File

@ -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" \

View File

@ -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')