Compare commits

...

39 Commits

Author SHA1 Message Date
Pau Espin Pedrol
c500f92822 Bump version: 1.6.0.7-a253 → 1.6.1
Change-Id: I1cccf6c3851b05787dcd82026065d74af08ddc84
2024-11-08 16:10:50 +01:00
Pau Espin Pedrol
a253f668ff hnbgw: Fix wrong map object retrieved from hashtable
If several map objects (mapping between RUA and SCCP transports for a
given Iu connection) ended up in the same hashtable bucket, then the
first one was always returned by map_from_conn_id().

As a result, when such collisions happened (e.g. when a big number of
connections were inserted) a user could see a Iu message coming from SCCP
connection "A" end up being forwarded to an unrelated RUA connection
"B".

Related: SYS#6602
Fixes: f3caea850b
Change-Id: I107f461bf5bcb92262422c893b23d190659f6f72
(cherry picked from commit 1150705a90)
2024-11-08 16:10:07 +01:00
Pau Espin Pedrol
ca7c36d39e ps_rab_ass_fsm: Fix uninitialized ptr access
/git/osmo-hnbgw/include/osmocom/hnbgw/context_map.h:211:9: error: 'rab' may be used uninitialized [-Werror=maybe-uninitialized]
  211 |         _map_rua_dispatch(MAP, EVENT, MSGB, __FILE__, __LINE__)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/git/osmo-hnbgw/src/osmo-hnbgw/ps_rab_ass_fsm.c:358:9: note: in expansion of macro 'map_rua_dispatch'
  358 |         map_rua_dispatch(rab->map, MAP_RUA_EV_TX_DIRECT_TRANSFER, msg);
      |         ^~~~~~~~~~~~~~~~
/git/osmo-hnbgw/src/osmo-hnbgw/ps_rab_ass_fsm.c:297:24: note: 'rab' was declared here
  297 |         struct ps_rab *rab;
      |                        ^~~

Change-Id: Ifdcd4c369b35820c5c2beb695392d0b82d4ea34c
(cherry picked from commit 89504050ea)
2024-11-08 16:09:56 +01:00
Neels Janosch Hofmeyr
ee47106dc3 coverity CID#358069
Change-Id: Ib2ed76c1bdae96457f61c2e015846566f100b8d6
(cherry picked from commit 1eb2a759ad)
2024-11-08 16:09:39 +01:00
Neels Janosch Hofmeyr
19bd64c77f coverity CID#358070
Change-Id: I742e95fa97f41ca4efe77055c5f29f73e48826d6
(cherry picked from commit 280f9e6ca9)
2024-11-08 16:09:30 +01:00
Neels Janosch Hofmeyr
f901dc0f60 coverity CID#358072
Change-Id: I90b0c73530fa3ba8b0bf74728d5b215957ef7184
(cherry picked from commit 2fc8abeecf)
2024-11-08 16:09:24 +01:00
Neels Janosch Hofmeyr
8adeeb4609 coverity CID#358071
Change-Id: I8237bf7d4985e993bb10aaaa9370cde2ece3d812
(cherry picked from commit 71f3169140)
2024-11-08 16:09:15 +01:00
Neels Janosch Hofmeyr
1ccb1832f7 drop config.vty tests from make check
The intention of those tests was to test various osmo-hnbgw startup
scenarios. It seemed the fastest/simplest way to do it at the time, but
the output depends on another library, regularly causing test fallout.

Keep the tests around for reference. They can still be invoked manually
with 'make config-tests'.

Related: OS#6380
Change-Id: I9c1540904b13d25db1c7933a88f6cc4b028fdd2b
(cherry picked from commit 4468d8728e)
2024-11-08 16:08:52 +01:00
Oliver Smith
0081cd7b00 Bump version: 1.5.0.82-610a → 1.6.0
Change-Id: I61af4064f83792aedeb4b9ba1b45c7e29da3705e
2024-07-25 10:05:59 +02:00
Harald Welte
610aae4d65 kpi_ranap: Avoid null pointer de-ref during HNB shutdown
In the downlink path, we cannot assume map->hnb_ctx is always
non-NULL.  If the HNB has just disconnected, it might be NULL,
while we're still processing downlink messages from the CN which
were sent by it before it realized that HNB was gone.

Closes: SYS#7010
Change-Id: I9a304b9e0cbc18dbf7b699f4aae6b91ca0c16173
2024-07-19 09:34:58 +02:00
Neels Janosch Hofmeyr
3ef65760ff fix MGCP compat with osmo-mgw <= 1.12.2: CRCX in recvonly
Fix a recently introduced problem with MGCP to osmo-mgw:
Send the first CRCX in recvonly mode, not sendrecv. osmo-hnbgw always
sends an additional MDCX including sendrecv mode anyway.

osmo-mgw currently forbids sending an initial CRCX in connection mode
'sendrecv', with this error message:

  DLMGCP ERROR endpoint:rtpbridge/2@mgw CI:7F4C8EDD CRCX: selected connection mode type requires an opposite end! (mgcp_protocol.c:1090)

I am submitting an osmo-mgw patch to not fail there, but we want to and
can easily be compatible with current and earlier osmo-mgw:

Sending the initial CRCX in sendrecv was introduced in commit:

	"drop legacy hack: do not start MGW endp in loopback mode"
	da7d33e284
	I0eca75d7abf66f8b9fde9c68ec10d4265f64a189
	This patch has not been part of a release yet.

The intention of that commit was to get away from loopback mode. The
logical mode to pick instead indeed is sendrecv, but by that osmo-hnbgw
triggers above osmo-mgw error.

Related: SYS#6974 SYS#6907
Related: osmo-mgw Ic089485543c5c97a35c7ae24fe0f622bf57d1976
Change-Id: I004f96ae36774ceb33f177c9f58f820fefa3ca14
2024-06-24 03:28:55 +02:00
Neels Janosch Hofmeyr
00655d45dd dbg log: nft kpi: clarify nr of rate ctrs vs nr of hnbp
Related: SYS#6773
Change-Id: I84346d3151f3040967f39a3a6e6db2e29bc1e2ec
2024-06-15 02:53:14 +00:00
Neels Janosch Hofmeyr
15e552f232 drop list of HNBAP UE Context
Last year, I have fixed some more ue_context leak situations.
But since we don't really use ue_context for anything, we could also
just drop this completely.

On HNBAP UE Register, we collect the ue_contexts in a ue_list. But we
never do anything with this list, at all. Furthermore, users are
reporting the list of ue_context growing indefinitely.

Simply drop the ue_context listing. Simply acknowledge all HNBAP UE
Register and DeRegister requests without storing any context IDs.

Change-Id: Ida7eadf36abcf465ae40003725c49e8e321a28c9
2024-06-15 04:14:05 +02:00
Neels Janosch Hofmeyr
e29816eccc nft-kpi: remove X34 drifting: adjust delay by elapsed time
Related: SYS#6773
Change-Id: I04f572890c04a48bb19c59f613a492ef96624baa
2024-06-12 04:24:31 +02:00
Neels Janosch Hofmeyr
fc0e505330 nft-kpi: log errors of counter retrieval
Make sure errors of getting counters from nft are logged.

Some context: we'll try again each X34 period, hence this is only a
problem when the error persists.

For example, when the get-counters thread is faster than the maintenance
thread can even create the nft table initially, this error will show.

We could add a definite check whether the maintenance thread has created
the tables yet, and wait for that event. But this complexity is not
really needed: it is fine to just fail getting counters once or twice.

Related: SYS#6773
Change-Id: I84340482e4a5bfcac158a21c9378a9511fa5ea10
2024-06-12 04:24:31 +02:00
Neels Janosch Hofmeyr
3da951d5c3 improve HNBAP error logging
We have a situation where HNBAP is not answered by osmo-hnbgw, and the
log is all silent. Add logging to a lot more of the possible HNBAP
failure paths to find out what is wrong.

Related: SYS#6810
Change-Id: I17d2809f59087d32e7c11a3ada1d3fadf6f0b660
2024-06-11 00:40:42 +00:00
Neels Janosch Hofmeyr
2a4af66669 prevent use-after-free after FSM instance termination
- Set osmo_fsm_set_dealloc_ctx(OTC_SELECT) in osmo-hnbgw's main().
- Only dispatch RANAP when FSM instances aren't terminated.

This way we possibly pre-empt use-after-free crashes for deallocating
FSM "nests" for obscure corner cases.

Use-after-free is a general problem for FSM design. For this, we created
osmo_fsm_set_dealloc_ctx(): When an FSM is terminated, move it to a
separate talloc context, instead of being deallocated.

An actual use-after-free was observed as described in OS#6484, but that
needs a separate, orthogonal fix:

When the Iuh link is lost while the CN link is waiting for SCCP CC or
CREF -- the better solution is described in OS#6085: don't wait for CC
at all, just dispatch DISCONN to SCCP-SCOC.

So even though the code where a crash was observed will be removed, this
patch is a general safeguard against corner case crashes, improving
general stability.

Related: OS#6484
Change-Id: Ib41e1a996aaa03221e73643636140947ac8f99e2
2024-06-11 00:04:58 +00:00
Neels Janosch Hofmeyr
62da064eda hnb_persistent: introduce disconnected timeout
Add timer hnbgw X35: Clean up all hNodeB persistent state after this
time of the hNodeB being disconnected. Set to zero to never clear hNodeB
persistent state. (default is 60*60*24*27 = a week).

The idea is to bound memory usage at sites where hNodeB do not stay on
one fixed cell id, but use a different cell id after each Iuh
reconnection.

Related: SYS#6773
Related: osmo-ttcn3-hacks Ibec009203d38f65714561b7c28edbdbd8b34e704
Change-Id: Ic819d7cbc03fb39e98c204b70d016c5170dc6307
2024-06-06 07:26:44 +02:00
Neels Janosch Hofmeyr
938b5ac777 add LOG_HNBP() for hnb_persistent
Add macro for the pattern LOGP(DHNB, ..., "%s: ...", hnbp->id_str)
and use where applicable.

Change-Id: I23334124074491fb3b91e720b67c97181c16bc21
2024-06-06 07:19:05 +02:00
Neels Janosch Hofmeyr
a503bd4eb2 3-digit MNC: use osmo_plmn_id in struct umts_cell_id
Properly represent the mnc_3_digits flag in umts_cell_id, and preserve
the three digit indicator as received on the wire.

Before this patch, the indicator for a three digit MNC received on the
wire was discarded, and instead g_hnbgw->config.plmn.mnc_3_digits was
used to convert any PLMN to string, whether it had 3 digits or not.

== hnb_persistent_list:

The cell id is used as primary key in the list of hnb_persistent
instances. This patch prevents any collisions between 2-digit and
3-digit MNCs (however unlikely in practice this may be).

== nft_kpi.c:

Just like the cell ids in hnb_persistent, the ids' strings are used as
primary key in nftables rulesets in nft_kpi.c -- also prevent MNC
collisions there:

Properly transport the 3-digit property in conversions:
  struct umts_cell_id <-> string
Uncouple to_str conversion from the PLMN set in the hnbgw VTY cfg.

Related: OS#6457
Change-Id: Id9a91c80cd2745424a916aef4736993bb7cd8ba0
2024-06-06 07:19:05 +02:00
Neels Janosch Hofmeyr
da8a042ead add umts_cell_id_test.c
Prepare for adding proper mnc_3_digits support to struct umts_cell_id.

Show current behavior of the umts_cell_id <-> string conversions.

Show two expected errors in umts_cell_id_test.ok: the three-digit MNC
with leading zeros is lost (because the g_hnbgw->config.plmn has
mnc_3_digits == false).

The expected errors will be fixed in upcoming patch
Id9a91c80cd2745424a916aef4736993bb7cd8ba0

Related: SYS#6773
Change-Id: Ibbb61a2c53a11dea794f451d3074bc9ba50862fe
2024-06-06 07:19:05 +02:00
Neels Janosch Hofmeyr
b3b2e2b20d umts_cell_id: add umts_cell_id_to_str_buf()
Prepare for umts_cell_id_test.c: allow testing the to_str conversion
without using talloc.

Change-Id: I3cc0169593c73c2e658637e4116ddd578f83df6d
2024-06-06 07:19:05 +02:00
Neels Janosch Hofmeyr
9da60749ca rename to umts_cell_id_to_str()
A subsequent patch adds umts_cell_id_to_str_buf(), and before the old
foo_name() pattern spreads further, I'd rather rename it.

Rationale:
- There is a umts_cell_id_from_str() function.
- The foo_name() is an old pattern, we prefer foo_to_str() now.
- Contained within osmo-hnbgw, no API problems.

Change-Id: I3124d1f5e634bc895ec347cb1a9816789fd9ab69
2024-06-06 07:19:05 +02:00
Neels Janosch Hofmeyr
02d1d51966 use osmo_jhash for the hnb_persistent hashtable
Use hashing like the linux kernel.

Related: SYS#6773
Depends: I0c9652bbc9e2a18b1200e7d63bb6f64ded7d75fa
Change-Id: I5441db4293dc6b57a1c606ef830656fa9fa01943
2024-06-06 07:19:05 +02:00
Neels Janosch Hofmeyr
9a7e520a95 fix stat_item leak in hnb_persistent_free()
Add missing stat_item free in hnb_persistent_free().

We recently fixed a rate_ctr leak in
I14e050bfb91b993f194e3800eacdc0d10f2b1a4e, but missed the also leaking
stat_item.

Particularly relevant with upcoming patch
Ic819d7cbc03fb39e98c204b70d016c5170dc6307 -- testing that patch revealed
the leak.

Related: osmo-ttcn3-hacks Ibec009203d38f65714561b7c28edbdbd8b34e704
Change-Id: I7326c53d595dce7b442eced89ff8f4b972bd2a82
2024-06-06 07:18:49 +02:00
Neels Janosch Hofmeyr
20b710bfc2 add hnb_persistent hashtable: optimize lookup by cell id
Mainly the new nft counters do a lot of hnb_persistent lookups by cell
id.

Add a hashtable to optimize looking up hnb_persistent instances. So far
we iterate the linear list of hnb_persistent for each and every counter
returned from nft_kpi.c.

Also improves lookups for HNBAP HNB* operations (rare).

A follow-up patch uses a better hash function from libosmocore
(jhash.h).

Related: SYS#6773
Change-Id: Iecb81eba28263ecf90a09c108995f6fb6f5f81f2
2024-05-28 01:32:09 +02:00
Neels Janosch Hofmeyr
64a2debb9a per-HNB GTP-U traffic counters via nft
Add optional feature: retrieve GTP-U traffic counters per hNodeB (not
per individual subscriber!) using nftables, to provide new rate_ctr
stats.

This is a "workaround" to get performance indicators per hNodeB, without
needing a UPF that supports URR.

When an hNodeB registers, set up nftables rules to count GTP-U packets
(UDP port 2152) to and from that hNodeB's address -- we are assuming
that it is the same address that Iuh is connecting from.

From the per-hNodeB packet and byte counters from nftables, also derive
a "UE bytes" counter, which is counting only the GTP-U payload. Assume
IP header of 20 bytes; UDP and GTP-U headers are 8 bytes each:

  ue_bytes = total_bytes - packets * (20 + 8 + 8)

Query these periodically, as configurable by new timer X34. Default is
one second of wait time between querying counters (excluding the time it
takes to retrieve and update the counters).

Add compile-time switch --enable-nftables, to build with/without
external dependency libnftables. Default is without, as before.

Add jenkins axis NFTABLES to switch --enable-nftables.

Add cfg file option 'hnbgw' / 'nft-kpi' to enable use of nftables.
This requires osmo-hnbgw to be run with cap_net_admin.

The VTY config commands are always visible -- simplifies VTY testing.
Refuse to start osmo-hnbgw when the user is requesting nft-kpi in the
config but when built without --enable-nftables.

Do nft commands in 2 separate threads. Run the same request queue
implementation twice, with two thread workers to handle them:
- one thread receives all requests to init the nft table, add and remove
  hNodeB counters, and start and stop counting for a specific hNodeB.
- Another thread handles all retrieval and parsing of counters from nft.

The main() thread hence never blocks for nftables commands, and services
the responses from nft when they are ready, via an osmo_it_q registered
in the main() select loop.

Persistently keep an nftables named counter for each seen hNodeB cell id
in the nftables ruleset, for the lifetime of a hnb_persistent instance
that holds the target rate_ctrs.

Add the rules to feed into these persistent counters to the ruleset when
the particular cell attaches and detaches via HNBAP HNB (De-)Register.

On hnb_persistent_free(), remove all items relating to this cell id from
nftables, including the persistent named counters.

Loosely related: upcoming patches will implement
- a hashtable for faster cell id lookup (important for updating
  counters)
  Iecb81eba28263ecf90a09c108995f6fb6f5f81f2
- proper MNC-3-digit support in cell ids (better have a 100% correct
  primary key).
  Id9a91c80cd2745424a916aef4736993bb7cd8ba0
- idle timeout for disconnected hnbp, so we are sure stale state does
  not build up for eternity.
  Ic819d7cbc03fb39e98c204b70d016c5170dc6307

Related: SYS#6773
Related: OS#6425
Change-Id: Ib2f0a9252715ea4b2fe9c367aa65f771357768ca
2024-05-23 17:07:08 +02:00
Neels Janosch Hofmeyr
e4bd96a841 fix rate_ctr leak in hnb_persistent_free()
Change-Id: I14e050bfb91b993f194e3800eacdc0d10f2b1a4e
2024-05-23 04:17:36 +02:00
Oliver Smith
eccff1abe8 contrib/jenkins: set --enable-werror
Fixes: OS#6460
Change-Id: I6d85b077f57a86b0bad29cb868f1f4f848506f46
2024-05-22 08:44:06 +02:00
Neels Janosch Hofmeyr
a121f82f33 fixup: compilation error: unused var in map_rua_init_action()
The variable was left unused by recent patch
I3e1ad7a2aa71674a22a27c31512600f2de139032 aka
a5974d7906

Change-Id: I871bc43f6f47d4b78fbf88826615f2dbb8e1f807
2024-05-21 17:57:38 +02:00
Harald Welte
a5974d7906 KPI: Add initial set of DTAP message type rate counters
From an operational perspective, it may be interesting to know how many
LU/RAU/Attach attempts, rejects and accepts are happening in a given
hNodeB.  Let's add some common infrastructure for DTAP related
statistics as well as some initial counters.

Related: SYS#6885
Depends: osmo-iuh.git Change-Id I7dea74102da8b610ff2a310c5814f5c89f08e7a6
Change-Id: I3e1ad7a2aa71674a22a27c31512600f2de139032
2024-05-15 15:37:39 +00:00
Oliver Smith
0d2d966c15 debian/postinst: add checks, be verbose
Do not attempt to change permissions/ownership if the package gets
upgraded from a version higher than the next release.

Do not fail if the user deleted the config file.

Be verbose when changing permissions.

Related: OS#4107
Change-Id: I1bcbe414fd18101e4d875a16539deab7baf9cb5f
2024-05-14 15:21:05 +02:00
Oliver Smith
de99af3aaa 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
ba81785b71 .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-05-08 12:03:05 +00:00
Neels Janosch Hofmeyr
da7d33e284 drop legacy hack: do not start MGW endp in loopback mode
We used to tell osmo-mgw to create an IuUP endpoint in loopback mode, in
order to hack it into responding to an IuUP Initialization. The loopback
mode here in osmo-hnbgw is a leftover from that hack. Drop it.

Change-Id: I0eca75d7abf66f8b9fde9c68ec10d4265f64a189
2024-05-06 18:59:04 +00:00
Max
7449635520 .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: Ife9433291ae03392ae114ebda418bce8cc93fe3b
2024-04-24 11:52:19 +02:00
Pau Espin Pedrol
8fd95f3e74 hnbap: Avoid calling duplicate getpeername() for each registered HNB
Change-Id: I980de31d1296c3b956146a461609bec76ed3d430
2024-04-16 14:42:37 +02:00
Harald Welte
cd58308915 counters: Distinguish between normal and abnormal release cause
It is interesting to know if a release was normal (as expected/requested
by the NAS layer, typically indicating a user-requested call end) or
abnormal (radio failure, pre-emption or whatever other event that the
user did not expect).

Related: SYS#6773
Change-Id: Idd2f845b7db448064b693ac1efdc8db006a47a11
2024-04-09 08:12:03 +00:00
Harald Welte
e3cc5ddf1d HNBAP: Support IMSI identity type in hnbgw_tx_ue_register_rej()
Change-Id: I2e00968cbf686f78f5c9655e899963f2b84dd78b
2024-03-29 12:00:06 +01:00
34 changed files with 2196 additions and 349 deletions

View File

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

View File

@@ -1,10 +1,9 @@
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
# In short:
# according to https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release
# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
# LIBVERSION=c:r:a
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:0.
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:a.
# If any interfaces have been added since the last public release: c:r:a + 1.
# If any interfaces have been removed or changed since the last public release: c:r:0.
#library what description / commit summary line
osmo-iuh >1.5.0 proper decoding of X.213 IPv4 address len=7
#library what description / commit summary line

View File

@@ -49,27 +49,37 @@ AC_SEARCH_LIBS([sctp_recvmsg], [sctp], [
LIBS=$old_LIBS
PKG_CHECK_MODULES(LIBASN1C, libasn1c >= 0.9.30)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0)
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.8.0)
PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.12.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.10.0)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.5.0)
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMORUA, libosmo-rua >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOHNBAP, libosmo-hnbap >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOMGCPCLIENT, libosmo-mgcp-client >= 1.13.0)
# Enable PFCP support for GTP tunnel mapping via UPF
AC_ARG_ENABLE([pfcp], [AS_HELP_STRING([--enable-pfcp], [Build with PFCP support, for GTP tunnel mapping via UPF])],
[osmo_ac_pfcp="$enableval"],[osmo_ac_pfcp="no"])
if test "x$osmo_ac_pfcp" = "xyes" ; then
PKG_CHECK_MODULES(LIBOSMOPFCP, libosmo-pfcp >= 0.3.0)
PKG_CHECK_MODULES(LIBOSMOPFCP, libosmo-pfcp >= 0.4.0)
AC_DEFINE(ENABLE_PFCP, 1, [Define to build with PFCP support])
fi
AM_CONDITIONAL(ENABLE_PFCP, test "x$osmo_ac_pfcp" = "xyes")
AC_SUBST(osmo_ac_pfcp)
# Enable libnftables support for traffic counters using nft
AC_ARG_ENABLE([nftables], [AS_HELP_STRING([--enable-nftables], [Build with libnftables support, for traffic counters using nft])],
[osmo_ac_nftables="$enableval"],[osmo_ac_nftables="no"])
if test "x$osmo_ac_nftables" = "xyes" ; then
PKG_CHECK_MODULES(LIBNFTABLES, libnftables >= 1.0.2)
AC_DEFINE(ENABLE_NFTABLES, 1, [Define to build with libnftables support])
fi
AM_CONDITIONAL(ENABLE_NFTABLES, test "x$osmo_ac_nftables" = "xyes")
AC_SUBST(osmo_ac_nftables)
dnl checks for header files
AC_HEADER_STDC
@@ -230,11 +240,11 @@ AC_OUTPUT(
tests/Makefile
tests/atlocal
tests/ranap_rab_ass/Makefile
tests/umts_cell_id/Makefile
doc/Makefile
doc/examples/Makefile
doc/manuals/Makefile
doc/charts/Makefile
contrib/Makefile
contrib/systemd/Makefile
contrib/osmo-hnbgw.spec
Makefile)

View File

@@ -4,9 +4,11 @@
# environment variables:
# * PFCP: configure PFCP support if set to "1" (default)
# * WITH_MANUALS: build manual PDFs if set to "1"
# * NFTABLES: configure nftables support if set to "1" (default)
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
#
PFCP=${PFCP:-1}
NFTABLES=${NFTABLES:-1}
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
@@ -45,6 +47,9 @@ if [ "$PFCP" = "1" ]; then
osmo-build-dep.sh libosmo-pfcp
CONFIG="$CONFIG --enable-pfcp"
fi
if [ "$NFTABLES" = "1" ]; then
CONFIG="$CONFIG --enable-nftables"
fi
if [ "$WITH_MANUALS" = "1" ]; then
CONFIG="$CONFIG --enable-manuals"
fi
@@ -59,7 +64,7 @@ set -x
cd "$base"
autoreconf --install --force
./configure --enable-sanitize --enable-external-tests $CONFIG
./configure --enable-sanitize --enable-external-tests --enable-werror $CONFIG
$MAKE $PARALLEL_MAKE
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
|| cat-testlogs.sh

View File

@@ -1,102 +0,0 @@
#
# spec file for package osmo-hnbgw
#
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
## Disable LTO for now since it breaks compilation of the tests
## https://osmocom.org/issues/4113
%define _lto_cflags %{nil}
Name: osmo-hnbgw
Version: @VERSION@
Release: 0
Summary: OsmoHNBGW: Osmocom's Base Station Controller for 2G CS mobile networks
License: AGPL-3.0-or-later AND GPL-2.0-or-later
Group: Hardware/Mobile
URL: https://osmocom.org/projects/osmohnbgw
Source: %{name}-%{version}.tar.xz
BuildRequires: automake >= 1.9
BuildRequires: libtool >= 2
BuildRequires: lksctp-tools-devel
BuildRequires: pkgconfig >= 0.20
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
BuildRequires: pkgconfig(libcrypto) >= 0.9.5
BuildRequires: pkgconfig(libosmo-mgcp-client) >= 1.12.0
BuildRequires: pkgconfig(libosmo-netif) >= 1.4.0
BuildRequires: pkgconfig(libosmo-sigtran) >= 1.8.0
BuildRequires: pkgconfig(libosmoabis) >= 1.5.0
BuildRequires: pkgconfig(libosmotrau) >= 1.5.0
BuildRequires: pkgconfig(libosmocore) >= 1.9.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.9.0
BuildRequires: pkgconfig(libosmogb) >= 1.9.0
BuildRequires: pkgconfig(libosmogsm) >= 1.9.0
BuildRequires: pkgconfig(libosmovty) >= 1.9.0
BuildRequires: pkgconfig(libosmo-hnbap) >= 1.5.0
BuildRequires: pkgconfig(libosmo-ranap) >= 1.5.0
BuildRequires: pkgconfig(libosmo-rua) >= 1.5.0
BuildRequires: pkgconfig(libosmo-pfcp) >= 0.3.0
BuildRequires: pkgconfig(talloc)
BuildRequires: pkgconfig(libasn1c) >= 0.9.30
%{?systemd_requires}
%description
OsmoHNBGW: Osmocom's Home NodeB for 3G mobile networks.
%prep
%setup -q
%build
echo "%{version}" >.tarball-version
autoreconf -fi
%configure \
--docdir=%{_docdir}/%{name} \
--with-systemdsystemunitdir=%{_unitdir} \
--enable-pfcp
make %{?_smp_mflags}
%install
%make_install
%if 0%{?suse_version}
%preun
%service_del_preun %{name}.service
%postun
%service_del_postun %{name}.service
%pre
%service_add_pre %{name}.service
%post
%service_add_post %{name}.service
%endif
%check
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%files
%license COPYING
%doc AUTHORS README.md
%{_bindir}/osmo-hnbgw
%dir %{_docdir}/%{name}/examples
%dir %{_docdir}/%{name}/examples/osmo-hnbgw
%{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw.cfg
%{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw-cs7.cfg
%{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw-pfcp.cfg
%{_docdir}/%{name}/examples/osmo-hnbgw/osmo-hnbgw-cnpool.cfg
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-hnbgw.cfg
%{_unitdir}/%{name}.service
%changelog

View File

@@ -9,6 +9,8 @@ Restart=always
LimitNOFILE=65536
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-hnbgw -c /etc/osmocom/osmo-hnbgw.cfg
RestartSec=2

113
debian/changelog vendored
View File

@@ -1,3 +1,116 @@
osmo-hnbgw (1.6.1) unstable; urgency=medium
[ Neels Janosch Hofmeyr ]
* drop config.vty tests from make check
* coverity CID#358071
* coverity CID#358072
* coverity CID#358070
* coverity CID#358069
[ Pau Espin Pedrol ]
* ps_rab_ass_fsm: Fix uninitialized ptr access
* hnbgw: Fix wrong map object retrieved from hashtable
-- Pau Espin Pedrol <pespin@sysmocom.de> Fri, 08 Nov 2024 16:10:50 +0100
osmo-hnbgw (1.6.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
* hnbgw_cn: Remove assert hit due to wrong assumption
* Increase default X31 val from 5 to 15 seconds
* context_map_sccp: Fix assert hit due to missing ev handling
* tests/ranap_rab_ass: Test RAB-Ass.req with X.213 IPv4 address len=7
* mgw_fsm: Assume IuUP HNB IP addr = Iuh during MGCP CRCX on RAN side
* mgw_fsm: Modify RAB on HNB if IuUP local IP addr at MGW changes during MDCX
* hnbap: Avoid calling duplicate getpeername() for each registered HNB
[ Neels Janosch Hofmeyr ]
* X31: fix vty doc
* allow (second) CS RAB Assignment Request without RTP info
* systemd,manual: set LimitNOFILE=65536
* rua: validate correct RUA ctx state per RUA message type
* rua: move from ERROR to DEBUG log: stray RUA Disconnect
* pfcp: fix modification of wrong FAR ID
* tweak vty doc: "UDP port"
* add tests/pfcp_cfg.vty.with_pfcp
* pfcp: fix missing vty_write of pfcp local-port
* pfcp: implement sending Network Instance IEs
* fix null deref on hnb_context_release
* drop legacy hack: do not start MGW endp in loopback mode
* fixup: compilation error: unused var in map_rua_init_action()
* fix rate_ctr leak in hnb_persistent_free()
* per-HNB GTP-U traffic counters via nft
* add hnb_persistent hashtable: optimize lookup by cell id
* fix stat_item leak in hnb_persistent_free()
* use osmo_jhash for the hnb_persistent hashtable
* rename to umts_cell_id_to_str()
* umts_cell_id: add umts_cell_id_to_str_buf()
* add umts_cell_id_test.c
* 3-digit MNC: use osmo_plmn_id in struct umts_cell_id
* add LOG_HNBP() for hnb_persistent
* hnb_persistent: introduce disconnected timeout
* prevent use-after-free after FSM instance termination
* improve HNBAP error logging
* nft-kpi: log errors of counter retrieval
* nft-kpi: remove X34 drifting: adjust delay by elapsed time
* drop list of HNBAP UE Context
* dbg log: nft kpi: clarify nr of rate ctrs vs nr of hnbp
* fix MGCP compat with osmo-mgw <= 1.12.2: CRCX in recvonly
[ Andreas Eversberg ]
* Use uniform log format for default config files
[ Harald Welte ]
* Display RANAP state during 'show cnlink'
* Fix license headers: Should have been AGPLv3+, not GPLv2+
* [cosmetic] re-order hnbgw.c to group code in major blocks
* umts_cell_id_name: Use 3-digit MCC and 2/3-digit MNC based on VTY config
* osmo_hnbgw_main: Install our usual SIGUSR1/SIGUSR2/SIGABRT handlers
* Introduce umts_cell_id_from_str() as inverse of umts_cell_id_name()
* Introduce concept of per-HNB persistent data structure
* Set persistent->ctx pointer on HNB REGISTER REQ
* New per-hnb rate_ctr and stat_item groups; track uptime
* Various new per-hnb RANAP and RUA counters
* stats: Introduce CNLINK counter macros
* stats: Introduce CNPOOL counter macro
* stats: Introduce basic counters for RANAP unit-data from CN links
* cosmetic: Fix comment in mgw_fsm.c: One RAB per context_map, not hnb!
* cosmetic: talk about cs_rab_ass_* when working in CS domain
* cosmetic: Rename hnbgw_rx_ranap and friends to *_rx_ranap_udt_ul
* rename hnbgw_peek_l3 to hnbgw_peek_l3_ul as it's uplink only
* don't forward paging requests to HNB's not yet registered
* vty: Print the uptime during 'show hnb' output
* Introduce counter for per-hnb cumulative active CS RAB duration
* stats: Add per-hnb paging:{cs,ps}:attempted counter
* cosmetic: align downlink RANAP unitdata function names with uplink
* mgw_fsm: Add some OSMO_ASSERT() to ensure only CS maps passed
* RAB activation/modification/release statistics
* context_map_{rua,sccp}: Re-order to always process KPIs
* generalize hnbgw_tx_ue_register_rej_tmsi() to hnbgw_tx_ue_register_rej()
* HNBAP: Use proper cause values during HNB-REGISTER-REQ processing
* HNBAP: Send HNB-REGISTER-REJ on ASN.1 decoding error
* HNBAP: Always respond to UE-REGISTER-REQUEST
* HNBAP: Make sure to respond with correct "reject"
* HNBAP: Transmit ErrorIndication in more situations
* HNBAP: use GSM23003_IMSI_MAX_DIGITS instead of magic number
* HNBAP: Support IMSI identity type in hnbgw_tx_ue_register_rej()
* counters: Distinguish between normal and abnormal release cause
* KPI: Add initial set of DTAP message type rate counters
* kpi_ranap: Avoid null pointer de-ref during HNB shutdown
[ Oliver Smith ]
* gitignore: add *.la, hnbgw_vty_reference.xml
* debian: fix having no manuals in osmo-hnbgw-doc
* .deb/.rpm: various fixes related to non-root
* contrib: remove rpm spec file
* debian/postinst: add checks, be verbose
* contrib/jenkins: set --enable-werror
[ Max ]
* .deb/.rpm: add osmocom user during package install
-- Oliver Smith <osmith@sysmocom.de> Thu, 25 Jul 2024 10:05:58 +0200
osmo-hnbgw (1.5.0) unstable; urgency=medium
[ Neels Janosch Hofmeyr ]

21
debian/control vendored
View File

@@ -13,16 +13,17 @@ Build-Depends: debhelper (>= 10),
libtalloc-dev,
libasn1c-dev (>= 0.9.30),
libsctp-dev,
libosmocore-dev (>= 1.9.0),
libosmo-sigtran-dev (>= 1.8.0),
libosmo-abis-dev (>= 1.5.0),
libosmo-netif-dev (>= 1.4.0),
libosmo-mgcp-client-dev (>= 1.12.0),
libosmo-hnbap-dev (>= 1.5.0),
libosmo-ranap-dev (>= 1.5.0),
libosmo-rua-dev (>= 1.5.0),
libosmo-pfcp-dev (>= 0.3.0),
osmo-gsm-manuals-dev (>= 1.5.0)
libosmocore-dev (>= 1.10.0),
libosmo-sigtran-dev (>= 1.9.0),
libosmo-abis-dev (>= 1.6.0),
libosmo-netif-dev (>= 1.5.0),
libosmo-mgcp-client-dev (>= 1.13.0),
libosmo-hnbap-dev (>= 1.6.0),
libosmo-ranap-dev (>= 1.6.0),
libosmo-rua-dev (>= 1.6.0),
libosmo-pfcp-dev (>= 0.4.0),
libnftables-dev,
osmo-gsm-manuals-dev (>= 1.6.0)
Standards-Version: 3.9.8
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-hnbgw

38
debian/postinst vendored Executable file
View File

@@ -0,0 +1,38 @@
#!/bin/sh -e
case "$1" in
configure)
# Create the osmocom group and user (if it doesn't exist yet)
if ! getent group osmocom >/dev/null; then
groupadd --system osmocom
fi
if ! getent passwd osmocom >/dev/null; then
useradd \
--system \
--gid osmocom \
--home-dir /var/lib/osmocom \
--shell /sbin/nologin \
--comment "Open Source Mobile Communications" \
osmocom
fi
# Fix permissions of previous (root-owned) install (OS#4107)
if dpkg --compare-versions "$2" le "1.6.0"; then
if [ -e /etc/osmocom/osmo-hnbgw.cfg ]; then
chown -v osmocom:osmocom /etc/osmocom/osmo-hnbgw.cfg
chmod -v 0660 /etc/osmocom/osmo-hnbgw.cfg
fi
if [ -d /etc/osmocom ]; then
chown -v root:osmocom /etc/osmocom
chmod -v 2775 /etc/osmocom
fi
mkdir -p /var/lib/osmocom
chown -R -v osmocom:osmocom /var/lib/osmocom
fi
;;
esac
# dh_installdeb(1) will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#

15
debian/rules vendored
View File

@@ -44,11 +44,18 @@
%:
dh $@ --with autoreconf
# debmake generated override targets
CONFIGURE_FLAGS += --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
CONFIGURE_FLAGS += --enable-pfcp
# libnftables is too old in Debian 10 (OS#6425)
override_dh_auto_configure:
dh_auto_configure -- $(CONFIGURE_FLAGS)
CONFIGURE_FLAGS=" \
--enable-manuals \
--enable-pfcp \
--with-systemdsystemunitdir=/lib/systemd/system \
"; \
if pkg-config --exists libnftables --atleast-version=1.0.2; then \
CONFIGURE_FLAGS="$$CONFIGURE_FLAGS --enable-nftables"; \
fi; \
dh_auto_configure -- $$CONFIGURE_FLAGS
#
# Do not install libtool archive, python .pyc .pyo
#override_dh_install:

View File

@@ -3,6 +3,7 @@ noinst_HEADERS = \
context_map.h hnbgw.h hnbgw_cn.h \
hnbgw_hnbap.h hnbgw_rua.h hnbgw_ranap.h \
kpi.h \
nft_kpi.h \
ranap_rab_ass.h mgw_fsm.h tdefs.h \
hnbgw_pfcp.h \
ps_rab_ass_fsm.h \

View File

@@ -6,6 +6,7 @@
#include <osmocom/core/write_queue.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/gsm/gsm23003.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/sigtran/osmo_ss7.h>
@@ -18,6 +19,8 @@
#include <osmocom/mgcp_client/mgcp_client.h>
#include <osmocom/mgcp_client/mgcp_client_pool.h>
#include <osmocom/hnbgw/nft_kpi.h>
#define STORE_UPTIME_INTERVAL 10 /* seconds */
#define HNB_STORE_RAB_DURATIONS_INTERVAL 1 /* seconds */
@@ -29,6 +32,7 @@ enum {
DMGW,
DHNB,
DCN,
DNFT,
};
extern const struct log_info hnbgw_log_info;
@@ -37,6 +41,12 @@ extern struct vty_app_info hnbgw_vty_info;
#define LOGHNB(HNB_CTX, ss, lvl, fmt, args ...) \
LOGP(ss, lvl, "(%s) " fmt, hnb_context_name(HNB_CTX), ## args)
#define LOG_HNBP(HNBP, lvl, fmt, args...) \
LOGP(DHNB, lvl, "(%s) " fmt, \
(HNBP) ? \
(((HNBP)->id_str && *(HNBP)->id_str) ? (HNBP)->id_str : "no-cell-id") \
: "null", ## args)
#define DOMAIN_CS RANAP_CN_DomainIndicator_cs_domain
#define DOMAIN_PS RANAP_CN_DomainIndicator_ps_domain
@@ -100,6 +110,8 @@ enum hnb_rate_ctr {
HNB_CTR_RANAP_PS_RAB_REL_REQ,
HNB_CTR_RANAP_CS_RAB_REL_REQ,
HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL,
HNB_CTR_RANAP_CS_RAB_REL_REQ_ABNORMAL,
HNB_CTR_RANAP_PS_RAB_REL_CNF,
HNB_CTR_RANAP_CS_RAB_REL_CNF,
@@ -109,6 +121,8 @@ enum hnb_rate_ctr {
HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT,
HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT,
HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL,
HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT_ABNORMAL,
HNB_CTR_RUA_ERR_IND,
@@ -132,6 +146,25 @@ enum hnb_rate_ctr {
HNB_CTR_CS_PAGING_ATTEMPTED,
HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL,
HNB_CTR_DTAP_CS_LU_REQ,
HNB_CTR_DTAP_CS_LU_ACC,
HNB_CTR_DTAP_CS_LU_REJ,
HNB_CTR_DTAP_PS_ATT_REQ,
HNB_CTR_DTAP_PS_ATT_ACK,
HNB_CTR_DTAP_PS_ATT_REJ,
HNB_CTR_DTAP_PS_RAU_REQ,
HNB_CTR_DTAP_PS_RAU_ACK,
HNB_CTR_DTAP_PS_RAU_REJ,
HNB_CTR_GTPU_PACKETS_UL,
HNB_CTR_GTPU_TOTAL_BYTES_UL,
HNB_CTR_GTPU_UE_BYTES_UL,
HNB_CTR_GTPU_PACKETS_DL,
HNB_CTR_GTPU_TOTAL_BYTES_DL,
HNB_CTR_GTPU_UE_BYTES_DL,
};
enum hnb_stat {
@@ -139,22 +172,22 @@ enum hnb_stat {
};
struct umts_cell_id {
uint16_t mcc; /*!< Mobile Country Code (0-999) */
uint16_t mnc; /*!< Mobile Network Code (0-999) */
struct osmo_plmn_id plmn; /*!< Mobile Country Code and Mobile Network Code (000-00 to 999-999) */
uint16_t lac; /*!< Locaton Area Code (1-65534) */
uint16_t rac; /*!< Routing Area Code (0-255) */
uint16_t sac; /*!< Service Area Code */
uint32_t cid; /*!< Cell ID */
};
const char *umts_cell_id_name(const struct umts_cell_id *ucid);
int umts_cell_id_to_str_buf(char *buf, size_t buflen, const struct umts_cell_id *ucid);
char *umts_cell_id_to_str_c(void *ctx, const struct umts_cell_id *ucid);
const char *umts_cell_id_to_str(const struct umts_cell_id *ucid);
int umts_cell_id_from_str(struct umts_cell_id *ucid, const char *instr);
uint32_t umts_cell_id_hash(const struct umts_cell_id *ucid);
/*! are both given umts_cell_id euqal? */
static inline bool umts_cell_id_equal(const struct umts_cell_id *a, const struct umts_cell_id *b)
{
if (a->mcc != b->mcc)
return false;
if (a->mnc != b->mnc)
if (osmo_plmn_cmp(&a->plmn, &b->plmn))
return false;
if (a->lac != b->lac)
return false;
@@ -340,6 +373,8 @@ struct hnb_context {
struct hnb_persistent {
/*! Entry in HNBGW-global list of hnb_persistent */
struct llist_head list;
/*! Entry in hash table g_hnbgw->hnb_persistent_by_id. */
struct hlist_node node_by_id;
/*! back-pointer to hnb_context. Can be NULL if no context at this point */
struct hnb_context *ctx;
@@ -353,6 +388,32 @@ struct hnb_persistent {
struct rate_ctr_group *ctrs;
struct osmo_stat_item_group *statg;
/* State that the main thread needs in order to know what was requested from the nft worker threads and what
* still needs to be requested. */
struct {
/* Whether a persistent named counter was added in nftables for this cell id. */
bool persistent_counter_added;
/* The last hNodeB GTP-U address we asked the nft maintenance thread to set up.
* osmo_sockaddr_str_is_nonzero(addr_remote) == false when no rules were added yet, and when
* we asked the nft maintenance thread to remove the rules for this hNodeB because it has
* disconnected. */
struct osmo_sockaddr_str addr_remote;
/* the nft handles needed to clean up the UL and DL rules when the hNodeB disconnects,
* and the last seen counter value gotten from nft. */
struct {
struct nft_kpi_handle h;
struct nft_kpi_val v;
} ul;
struct {
struct nft_kpi_handle h;
struct nft_kpi_val v;
} dl;
} nft_kpi;
struct osmo_timer_list disconnected_timeout;
};
struct ue_context {
@@ -391,16 +452,24 @@ struct hnbgw {
char *core;
} netinst;
} pfcp;
struct {
bool enable;
/* The table name as used in nftables for the ruleset owned by this process. It is "osmo-hnbgw"
* by default. */
char *table_name;
} nft_kpi;
} config;
/*! SCTP listen socket for incoming connections */
struct osmo_stream_srv_link *iuh;
/* list of struct hnb_context */
struct llist_head hnb_list;
/* list of struct hnb_persistent */
struct llist_head hnb_persistent_list;
/* optimized lookup for hnb_persistent, by cell id string */
DECLARE_HASHTABLE(hnb_persistent_by_id, 5);
struct osmo_timer_list store_uptime_timer;
/* list of struct ue_context */
struct llist_head ue_list;
/* next availble UE Context ID */
uint32_t next_ue_ctx_id;
struct ctrl_handle *ctrl;
@@ -424,6 +493,12 @@ struct hnbgw {
} pfcp;
struct osmo_timer_list hnb_store_rab_durations_timer;
struct {
bool active;
struct osmo_timer_list get_counters_timer;
struct timespec next_timer;
} nft_kpi;
};
extern struct hnbgw *g_hnbgw;
@@ -439,18 +514,13 @@ int hnbgw_mgw_setup(void);
struct hnb_context *hnb_context_by_identity_info(const char *identity_info);
const char *hnb_context_name(struct hnb_context *ctx);
struct ue_context *ue_context_by_id(uint32_t id);
struct ue_context *ue_context_by_imsi(const char *imsi);
struct ue_context *ue_context_by_tmsi(uint32_t tmsi);
struct ue_context *ue_context_alloc(struct hnb_context *hnb, const char *imsi,
uint32_t tmsi);
void ue_context_free(struct ue_context *ue);
void hnb_context_release(struct hnb_context *ctx);
void hnb_context_release_ue_state(struct hnb_context *ctx);
struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id);
struct hnb_persistent *hnb_persistent_find_by_id(const struct umts_cell_id *id);
void hnb_persistent_registered(struct hnb_persistent *hnbp);
void hnb_persistent_deregistered(struct hnb_persistent *hnbp);
void hnb_persistent_free(struct hnb_persistent *hnbp);
void hnbgw_vty_init(void);
@@ -471,3 +541,5 @@ struct msgb *hnbgw_ranap_msg_alloc(const char *name);
int hnbgw_peek_l3_ul(struct hnbgw_context_map *map, struct msgb *ranap_msg);
unsigned long long hnb_get_updowntime(const struct hnb_context *ctx);
uint32_t get_next_ue_ctx_id(void);

View File

@@ -6,3 +6,6 @@
void kpi_ranap_process_ul(struct hnbgw_context_map *map, ranap_message *ranap);
void kpi_ranap_process_dl(struct hnbgw_context_map *map, ranap_message *ranap);
void kpi_dtap_process_ul(struct hnbgw_context_map *map, const uint8_t *buf, unsigned int len, uint8_t sapi);
void kpi_dtap_process_dl(struct hnbgw_context_map *map, const uint8_t *buf, unsigned int len, uint8_t sapi);

View File

@@ -0,0 +1,25 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
struct hnb_persistent;
/* A "handle" that nftables returns for chains and rules -- a plain number. Deleting an unnamed rule can only be done by
* such a handle. */
struct nft_kpi_handle {
bool handle_present;
int64_t handle;
};
/* One GTP-U packet and byte counter cache, i.e. for one UL/DL direction of one hNodeB. */
struct nft_kpi_val {
uint64_t packets;
uint64_t total_bytes;
uint64_t ue_bytes;
};
void nft_kpi_init(const char *table_name);
void nft_kpi_hnb_persistent_add(struct hnb_persistent *hnbp);
void nft_kpi_hnb_persistent_remove(struct hnb_persistent *hnbp);
int nft_kpi_hnb_start(struct hnb_persistent *hnbp, const struct osmo_sockaddr_str *gtpu_remote);
void nft_kpi_hnb_stop(struct hnb_persistent *hnbp);

View File

@@ -20,6 +20,7 @@ AM_CFLAGS = \
$(LIBOSMORANAP_CFLAGS) \
$(LIBOSMOHNBAP_CFLAGS) \
$(LIBOSMOMGCPCLIENT_CFLAGS) \
$(LIBNFTABLES_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
@@ -44,8 +45,10 @@ libhnbgw_la_SOURCES = \
cnlink.c \
ranap_rab_ass.c \
mgw_fsm.c \
kpi_dtap.c \
kpi_ranap.c \
tdefs.c \
nft_kpi.c \
$(NULL)
libhnbgw_la_LIBADD = \
@@ -62,6 +65,7 @@ libhnbgw_la_LIBADD = \
$(LIBOSMOHNBAP_LIBS) \
$(LIBSCTP_LIBS) \
$(LIBOSMOMGCPCLIENT_LIBS) \
$(LIBNFTABLES_LIBS) \
$(NULL)
if ENABLE_PFCP

View File

@@ -158,6 +158,11 @@ ranap_message *hnbgw_decode_ranap_co(struct msgb *ranap_msg)
static int handle_rx_rua(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
{
struct hnbgw_context_map *map = fi->priv;
/* If the FSM instance has already terminated, don't dispatch anything. */
if (fi->proc.terminating)
return 0;
if (!msg_has_l2_data(ranap_msg))
return 0;
@@ -214,16 +219,14 @@ static int forward_ranap_to_rua(struct hnbgw_context_map *map, struct msgb *rana
static void map_rua_init_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct hnbgw_context_map *map = fi->priv;
struct msgb *ranap_msg = data;
switch (event) {
case MAP_RUA_EV_RX_CONNECT:
/* not needed for RAB assignment scanning, but for KPI scanning */
handle_rx_rua(fi, ranap_msg);
map_rua_fsm_state_chg(MAP_RUA_ST_CONNECTED);
/* The Connect will never be a RAB Assignment response, so no need for handle_rx_rua() (which decodes
* the RANAP message to detect a RAB Assignment response). Just forward to SCCP as is. */
map_sccp_dispatch(map, MAP_SCCP_EV_TX_DATA_REQUEST, ranap_msg);
return;
case MAP_RUA_EV_RX_DISCONNECT:

View File

@@ -198,6 +198,10 @@ static int handle_rx_sccp(struct osmo_fsm_inst *fi, struct msgb *ranap_msg)
struct hnbgw_context_map *map = fi->priv;
int rc;
/* If the FSM instance has already terminated, don't dispatch anything. */
if (fi->proc.terminating)
return 0;
/* When there was no message received along with the received event, then there is nothing to forward to RUA. */
if (!msg_has_l2_data(ranap_msg))
return 0;

View File

@@ -25,6 +25,7 @@
#include <osmocom/core/stats.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stat_item.h>
#include <osmocom/core/jhash.h>
#include <osmocom/vty/vty.h>
@@ -43,6 +44,7 @@
#include <osmocom/hnbgw/hnbgw_cn.h>
#include <osmocom/hnbgw/context_map.h>
#include <osmocom/hnbgw/mgw_fsm.h>
#include <osmocom/hnbgw/tdefs.h>
struct hnbgw *g_hnbgw = NULL;
@@ -92,92 +94,11 @@ static void hnbgw_store_hnb_rab_durations(void *data)
* UE Context
***********************************************************************/
struct ue_context *ue_context_by_id(uint32_t id)
uint32_t get_next_ue_ctx_id(void)
{
struct ue_context *ue;
llist_for_each_entry(ue, &g_hnbgw->ue_list, list) {
if (ue->context_id == id)
return ue;
}
return NULL;
return g_hnbgw->next_ue_ctx_id++;
}
struct ue_context *ue_context_by_imsi(const char *imsi)
{
struct ue_context *ue;
llist_for_each_entry(ue, &g_hnbgw->ue_list, list) {
if (!strcmp(ue->imsi, imsi))
return ue;
}
return NULL;
}
struct ue_context *ue_context_by_tmsi(uint32_t tmsi)
{
struct ue_context *ue;
llist_for_each_entry(ue, &g_hnbgw->ue_list, list) {
if (ue->tmsi == tmsi)
return ue;
}
return NULL;
}
static void ue_context_free_by_hnb(const struct hnb_context *hnb)
{
struct ue_context *ue, *tmp;
llist_for_each_entry_safe(ue, tmp, &g_hnbgw->ue_list, list) {
if (ue->hnb == hnb)
ue_context_free(ue);
}
}
static uint32_t get_next_ue_ctx_id(void)
{
uint32_t id;
do {
id = g_hnbgw->next_ue_ctx_id++;
} while (ue_context_by_id(id));
return id;
}
struct ue_context *ue_context_alloc(struct hnb_context *hnb, const char *imsi,
uint32_t tmsi)
{
struct ue_context *ue;
ue = talloc_zero(g_hnbgw, struct ue_context);
if (!ue)
return NULL;
ue->hnb = hnb;
if (imsi)
OSMO_STRLCPY_ARRAY(ue->imsi, imsi);
else
ue->imsi[0] = '\0';
ue->tmsi = tmsi;
ue->context_id = get_next_ue_ctx_id();
llist_add_tail(&ue->list, &g_hnbgw->ue_list);
LOGP(DHNBAP, LOGL_INFO, "created UE context: id 0x%x, imsi %s, tmsi 0x%x\n",
ue->context_id, imsi? imsi : "-", tmsi);
return ue;
}
void ue_context_free(struct ue_context *ue)
{
llist_del(&ue->list);
talloc_free(ue);
}
/***********************************************************************
* HNB Context
***********************************************************************/
@@ -231,31 +152,66 @@ static struct hnb_context *hnb_context_alloc(struct osmo_stream_srv_link *link,
return ctx;
}
const char *umts_cell_id_name(const struct umts_cell_id *ucid)
int umts_cell_id_to_str_buf(char *buf, size_t buflen, const struct umts_cell_id *ucid)
{
const char *fmtstr = "%03u-%02u-L%u-R%u-S%u-C%u";
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
OSMO_STRBUF_APPEND_NOLEN(sb, osmo_plmn_name_buf, &ucid->plmn);
OSMO_STRBUF_PRINTF(sb, "-L%u-R%u-S%u-C%u", ucid->lac, ucid->rac, ucid->sac, ucid->cid);
return sb.chars_needed;
}
if (g_hnbgw->config.plmn.mnc_3_digits)
fmtstr = "%03u-%03u-L%u-R%u-S%u-C%u";
char *umts_cell_id_to_str_c(void *ctx, const struct umts_cell_id *ucid)
{
OSMO_NAME_C_IMPL(ctx, 64, "ERROR", umts_cell_id_to_str_buf, ucid)
}
return talloc_asprintf(OTC_SELECT, fmtstr, ucid->mcc, ucid->mnc, ucid->lac, ucid->rac,
ucid->sac, ucid->cid);
const char *umts_cell_id_to_str(const struct umts_cell_id *ucid)
{
return umts_cell_id_to_str_c(OTC_SELECT, ucid);
}
/* Useful to index a hash table by struct umts_cell_id. */
uint32_t umts_cell_id_hash(const struct umts_cell_id *ucid)
{
return osmo_jhash(ucid, sizeof(*ucid), 0x423423);
}
/* parse a string representation of an umts_cell_id into its decoded representation */
int umts_cell_id_from_str(struct umts_cell_id *ucid, const char *instr)
{
int rc = sscanf(instr, "%hu-%hu-L%hu-R%hu-S%hu-C%u", &ucid->mcc, &ucid->mnc, &ucid->lac, &ucid->rac, &ucid->sac, &ucid->cid);
int rc;
char buf[4];
const char *pos = instr;
const char *end;
/* We want to use struct umts_cell_id as hashtable key. If it ever happens to contain any padding bytes, make
* sure everything is deterministically zero. */
memset(ucid, 0, sizeof(*ucid));
/* read MCC */
end = strchr(pos, '-');
if (!end || end <= pos || (end - pos) >= sizeof(buf))
return -EINVAL;
osmo_strlcpy(buf, pos, end - pos + 1);
if (osmo_mcc_from_str(buf, &ucid->plmn.mcc))
return -EINVAL;
pos = end + 1;
/* read MNC -- here the number of leading zeros matters. */
end = strchr(pos, '-');
if (!end || end == pos || (end - pos) >= sizeof(buf))
return -EINVAL;
osmo_strlcpy(buf, pos, end - pos + 1);
if (osmo_mnc_from_str(buf, &ucid->plmn.mnc, &ucid->plmn.mnc_3_digits))
return -EINVAL;
pos = end + 1;
/* parse the rest, where leading zeros do not matter */
rc = sscanf(pos, "L%hu-R%hu-S%hu-C%u", &ucid->lac, &ucid->rac, &ucid->sac, &ucid->cid);
if (rc < 0)
return -errno;
if (rc != 6)
return -EINVAL;
if (ucid->mcc > 999)
return -EINVAL;
if (ucid->mnc > 999)
if (rc != 4)
return -EINVAL;
if (ucid->lac == 0 || ucid->lac == 0xffff)
@@ -291,7 +247,7 @@ const char *hnb_context_name(struct hnb_context *ctx)
if (g_hnbgw->config.log_prefix_hnb_id)
result = talloc_asprintf(OTC_SELECT, "%s %s", result, ctx->identity_info);
else
result = talloc_asprintf(OTC_SELECT, "%s %s", result, umts_cell_id_name(&ctx->id));
result = talloc_asprintf(OTC_SELECT, "%s %s", result, umts_cell_id_to_str(&ctx->id));
return result;
}
@@ -304,7 +260,6 @@ void hnb_context_release_ue_state(struct hnb_context *ctx)
context_map_hnb_released(map);
/* hnbgw_context_map will remove itself from lists when it is ready. */
}
ue_context_free_by_hnb(ctx);
}
void hnb_context_release(struct hnb_context *ctx)
@@ -343,7 +298,7 @@ void hnb_context_release(struct hnb_context *ctx)
/* remove back reference from hnb_persistent to context */
if (ctx->persistent)
ctx->persistent->ctx = NULL;
hnb_persistent_deregistered(ctx->persistent);
talloc_free(ctx);
}
@@ -398,11 +353,14 @@ const struct rate_ctr_desc hnb_ctr_description[] = {
[HNB_CTR_RANAP_CS_RAB_MOD_FAIL] = {
"ranap:cs:rab_mod:fail", "CS RAB Modifications failed" },
[HNB_CTR_RANAP_PS_RAB_REL_REQ] = {
"ranap:ps:rab_rel:req", "PS RAB Release requested" },
"ranap:ps:rab_rel:req:normal", "PS RAB Release requested (by CN), normal" },
[HNB_CTR_RANAP_CS_RAB_REL_REQ] = {
"ranap:cs:rab_rel:req", "CS RAB Release requested" },
"ranap:cs:rab_rel:req:normal", "CS RAB Release requested (by CN), normal" },
[HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL] = {
"ranap:ps:rab_rel:req:abnormal", "PS RAB Release requested (by CN), abnormal" },
[HNB_CTR_RANAP_CS_RAB_REL_REQ_ABNORMAL] = {
"ranap:cs:rab_rel:req:abnormal", "CS RAB Release requested (by CN), abnormal" },
[HNB_CTR_RANAP_PS_RAB_REL_CNF] = {
"ranap:ps:rab_rel:cnf", "PS RAB Release confirmed" },
@@ -415,9 +373,13 @@ const struct rate_ctr_desc hnb_ctr_description[] = {
"ranap:cs:rab_rel:fail", "CS RAB Release failed" },
[HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT] = {
"ranap:ps:rab_rel:implicit", "PS RAB Release implicit (during Iu Release)" },
"ranap:ps:rab_rel:implicit:normal", "PS RAB Release implicit (during Iu Release), normal" },
[HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT] = {
"ranap:cs:rab_rel:implicit", "CS RAB Release implicit (during Iu Release)" },
"ranap:cs:rab_rel:implicit:normal", "CS RAB Release implicit (during Iu Release), normal" },
[HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL] = {
"ranap:ps:rab_rel:implicit:abnormal", "PS RAB Release implicit (during Iu Release), abnormal" },
[HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT_ABNORMAL] = {
"ranap:cs:rab_rel:implicit:abnormal", "CS RAB Release implicit (during Iu Release), abnormal" },
[HNB_CTR_RUA_ERR_IND] = {
"rua:error_ind", "Received RUA Error Indications" },
@@ -457,6 +419,44 @@ const struct rate_ctr_desc hnb_ctr_description[] = {
[HNB_CTR_RAB_ACTIVE_MILLISECONDS_TOTAL] = {
"rab:cs:active_milliseconds:total", "Cumulative number of milliseconds of CS RAB activity" },
[HNB_CTR_DTAP_CS_LU_REQ] = { "dtap:cs:location_update:req", "CS Location Update Requests" },
[HNB_CTR_DTAP_CS_LU_ACC] = { "dtap:cs:location_update:accept", "CS Location Update Accepts" },
[HNB_CTR_DTAP_CS_LU_REJ] = { "dtap:cs:location_update:reject", "CS Location Update Rejects" },
[HNB_CTR_DTAP_PS_ATT_REQ] = { "dtap:ps:attach:req", "PS Attach Requests" },
[HNB_CTR_DTAP_PS_ATT_ACK] = { "dtap:ps:attach:accept", "PS Attach Accepts" },
[HNB_CTR_DTAP_PS_ATT_REJ] = { "dtap:ps:attach:reject", "PS Attach Rejects" },
[HNB_CTR_DTAP_PS_RAU_REQ] = { "dtap:ps:routing_area_update:req", "PS Routing Area Update Requests" },
[HNB_CTR_DTAP_PS_RAU_ACK] = { "dtap:ps:routing_area_update:accept", "PS Routing Area Update Accepts" },
[HNB_CTR_DTAP_PS_RAU_REJ] = { "dtap:ps:routing_area_update:reject", "PS Routing Area Update Rejects" },
[HNB_CTR_GTPU_PACKETS_UL] = {
"gtpu:packets:ul",
"Count of GTP-U packets received from the HNB",
},
[HNB_CTR_GTPU_TOTAL_BYTES_UL] = {
"gtpu:total_bytes:ul",
"Count of total GTP-U bytes received from the HNB, including the GTP-U/UDP/IP headers",
},
[HNB_CTR_GTPU_UE_BYTES_UL] = {
"gtpu:ue_bytes:ul",
"Assuming an IP header length of 20 bytes, GTP-U bytes received from the HNB, excluding the GTP-U/UDP/IP headers",
},
[HNB_CTR_GTPU_PACKETS_DL] = {
"gtpu:packets:dl",
"Count of GTP-U packets sent to the HNB",
},
[HNB_CTR_GTPU_TOTAL_BYTES_DL] = {
"gtpu:total_bytes:dl",
"Count of total GTP-U bytes sent to the HNB, including the GTP-U/UDP/IP headers",
},
[HNB_CTR_GTPU_UE_BYTES_DL] = {
"gtpu:ue_bytes:dl",
"Assuming an IP header length of 20 bytes, GTP-U bytes sent to the HNB, excluding the GTP-U/UDP/IP headers",
},
};
const struct rate_ctr_group_desc hnb_ctrg_desc = {
@@ -479,6 +479,24 @@ const struct osmo_stat_item_group_desc hnb_statg_desc = {
.item_desc = hnb_stat_desc,
};
static void hnb_persistent_disconnected_timeout_cb(void *data)
{
hnb_persistent_free(data);
}
static void hnb_persistent_disconnected_timeout_schedule(struct hnb_persistent *hnbp)
{
unsigned long period_s = osmo_tdef_get(hnbgw_T_defs, -35, OSMO_TDEF_S, 60*60*24*7);
if (period_s < 1) {
LOG_HNBP(hnbp, LOGL_INFO,
"timer X35 is zero, not setting a disconnected timeout for this hnb-persistent instance.\n");
return;
}
/* It is fine if the timer is already active, osmo_timer_del() is done implicitly by the osmo_timer API. */
osmo_timer_setup(&hnbp->disconnected_timeout, hnb_persistent_disconnected_timeout_cb, hnbp);
osmo_timer_schedule(&hnbp->disconnected_timeout, period_s, 0);
}
struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id)
{
struct hnb_persistent *hnbp = talloc_zero(g_hnbgw, struct hnb_persistent);
@@ -486,7 +504,7 @@ struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id)
return NULL;
hnbp->id = *id;
hnbp->id_str = talloc_strdup(hnbp, umts_cell_id_name(id));
hnbp->id_str = talloc_strdup(hnbp, umts_cell_id_to_str(id));
hnbp->ctrs = rate_ctr_group_alloc(hnbp, &hnb_ctrg_desc, 0);
if (!hnbp->ctrs)
goto out_free;
@@ -497,6 +515,15 @@ struct hnb_persistent *hnb_persistent_alloc(const struct umts_cell_id *id)
osmo_stat_item_group_set_name(hnbp->statg, hnbp->id_str);
llist_add(&hnbp->list, &g_hnbgw->hnb_persistent_list);
hash_add(g_hnbgw->hnb_persistent_by_id, &hnbp->node_by_id, umts_cell_id_hash(&hnbp->id));
if (g_hnbgw->nft_kpi.active)
nft_kpi_hnb_persistent_add(hnbp);
/* Normally the disconnected timer runs only when the hNodeB is not currently connected on Iuh. This here is paranoia:
* In case we have to HNBAP HNB Register Reject, the disconnected timer should be active on this unused hnbp.
* On success, hnb_persistent_registered() will stop the disconnected timer directly after this. */
hnb_persistent_disconnected_timeout_schedule(hnbp);
return hnbp;
@@ -510,19 +537,93 @@ out_free:
struct hnb_persistent *hnb_persistent_find_by_id(const struct umts_cell_id *id)
{
struct hnb_persistent *hnbp;
llist_for_each_entry(hnbp, &g_hnbgw->hnb_persistent_list, list) {
uint32_t id_hash = umts_cell_id_hash(id);
hash_for_each_possible (g_hnbgw->hnb_persistent_by_id, hnbp, node_by_id, id_hash) {
if (umts_cell_id_equal(&hnbp->id, id))
return hnbp;
}
return NULL;
}
/* Read the peer's remote IP address from the Iuh conn's fd, and set up GTP-U counters for that remote address. */
static void hnb_persistent_update_remote_addr(struct hnb_persistent *hnbp)
{
socklen_t socklen;
struct osmo_sockaddr osa;
struct osmo_sockaddr_str remote_str;
int fd;
fd = osmo_stream_srv_get_fd(hnbp->ctx->conn);
if (fd < 0) {
LOG_HNBP(hnbp, LOGL_ERROR, "no active socket fd, cannot set up traffic counters\n");
return;
}
socklen = sizeof(struct osmo_sockaddr);
if (getpeername(fd, &osa.u.sa, &socklen)) {
LOG_HNBP(hnbp, LOGL_ERROR, "cannot read remote address, cannot set up traffic counters\n");
return;
}
if (osmo_sockaddr_str_from_osa(&remote_str, &osa)) {
LOG_HNBP(hnbp, LOGL_ERROR, "cannot parse remote address, cannot set up traffic counters\n");
return;
}
/* We got the remote address from the Iuh link (RUA), and now we are blatantly assuming that the hNodeB has its
* GTP endpoint on the same IP address, just with UDP port 2152 (the fixed GTP port as per 3GPP spec). */
remote_str.port = 2152;
if (nft_kpi_hnb_start(hnbp, &remote_str))
LOG_HNBP(hnbp, LOGL_ERROR, "failed to set up traffic counters\n");
}
/* Whenever HNBAP registers a HNB, hnbgw_hnbap.c calls this function to let the hnb_persistent update its state to the
* (new) remote address being active. When calling this function, a hnbp->ctx should be present, with an active
* osmo_stream_srv conn. */
void hnb_persistent_registered(struct hnb_persistent *hnbp)
{
if (!hnbp->ctx) {
LOG_HNBP(hnbp, LOGL_ERROR, "hnb_persistent_registered() invoked, but there is no hnb_ctx\n");
return;
}
/* The hNodeB is now connected, i.e. not disconnected. */
osmo_timer_del(&hnbp->disconnected_timeout);
/* start counting traffic */
if (g_hnbgw->nft_kpi.active)
hnb_persistent_update_remote_addr(hnbp);
}
/* Whenever a HNB is regarded as no longer registered (HNBAP HNB De-Register, or the Iuh link drops), this function is
* called to to let the hnb_persistent update its state to the hNodeB being disconnected. Clear the ctx->persistent and
* hnbp->ctx relations; do not delete the hnb_persistent instance. */
void hnb_persistent_deregistered(struct hnb_persistent *hnbp)
{
/* clear out cross references of hnb_context and hnb_persistent */
if (hnbp->ctx) {
if (hnbp->ctx->persistent == hnbp)
hnbp->ctx->persistent = NULL;
hnbp->ctx = NULL;
}
/* stop counting traffic */
nft_kpi_hnb_stop(hnbp);
/* The hNodeB is now disconnected. Clear out hnb_persistent when the disconnected timeout has passed. */
hnb_persistent_disconnected_timeout_schedule(hnbp);
}
void hnb_persistent_free(struct hnb_persistent *hnbp)
{
/* FIXME: check if in use? */
osmo_timer_del(&hnbp->disconnected_timeout);
nft_kpi_hnb_stop(hnbp);
nft_kpi_hnb_persistent_remove(hnbp);
osmo_stat_item_group_free(hnbp->statg);
rate_ctr_group_free(hnbp->ctrs);
llist_del(&hnbp->list);
hash_del(&hnbp->node_by_id);
talloc_free(hnbp);
}
@@ -847,6 +948,11 @@ static const struct log_info_cat hnbgw_log_cat[] = {
.color = OSMO_LOGCOLOR_DARKYELLOW,
.description = "Core Network side (via SCCP)",
},
[DNFT] = {
.name = "DNFT", .loglevel = LOGL_NOTICE, .enabled = 1,
.color = OSMO_LOGCOLOR_BLUE,
.description = "nftables interaction for retrieving stats",
},
};
const struct log_info hnbgw_log_info = {
@@ -877,8 +983,10 @@ void g_hnbgw_alloc(void *ctx)
g_hnbgw->next_ue_ctx_id = 23;
INIT_LLIST_HEAD(&g_hnbgw->hnb_list);
INIT_LLIST_HEAD(&g_hnbgw->hnb_persistent_list);
INIT_LLIST_HEAD(&g_hnbgw->ue_list);
hash_init(g_hnbgw->hnb_persistent_by_id);
INIT_LLIST_HEAD(&g_hnbgw->sccp.users);
g_hnbgw->mgw_pool = mgcp_client_pool_alloc(g_hnbgw);

View File

@@ -446,8 +446,10 @@ static struct hnbgw_context_map *map_from_conn_id(struct hnbgw_sccp_user *hsu, u
const struct osmo_prim_hdr *oph)
{
struct hnbgw_context_map *map;
hash_for_each_possible(hsu->hnbgw_context_map_by_conn_id, map, hnbgw_sccp_user_entry, conn_id)
return map;
hash_for_each_possible(hsu->hnbgw_context_map_by_conn_id, map, hnbgw_sccp_user_entry, conn_id) {
if (map->scu_conn_id == conn_id)
return map;
}
LOGP(DRANAP, LOGL_ERROR, "Rx for unknown SCCP connection ID: %u: %s\n",
conn_id, osmo_scu_prim_hdr_name_c(OTC_SELECT, oph));
return NULL;

View File

@@ -171,7 +171,7 @@ static int hnbgw_tx_hnb_register_acc(struct hnb_context *ctx)
}
static int hnbgw_tx_ue_register_acc(struct ue_context *ue)
static int hnbgw_tx_ue_register_acc(struct hnb_context *hnb, const char *imsi, uint32_t context_id)
{
HNBAP_UERegisterAccept_t accept_out;
HNBAP_UERegisterAcceptIEs_t accept;
@@ -182,18 +182,20 @@ static int hnbgw_tx_ue_register_acc(struct ue_context *ue)
int rc;
encoded_imsi_len = ranap_imsi_encode(encoded_imsi,
sizeof(encoded_imsi), ue->imsi);
sizeof(encoded_imsi), imsi);
memset(&accept, 0, sizeof(accept));
accept.uE_Identity.present = HNBAP_UE_Identity_PR_iMSI;
OCTET_STRING_fromBuf(&accept.uE_Identity.choice.iMSI,
(const char *)encoded_imsi, encoded_imsi_len);
asn1_u24_to_bitstring(&accept.context_ID, &ctx_id, ue->context_id);
asn1_u24_to_bitstring(&accept.context_ID, &ctx_id, context_id);
memset(&accept_out, 0, sizeof(accept_out));
rc = hnbap_encode_ueregisteraccepties(&accept_out, &accept);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING, &accept.uE_Identity.choice.iMSI);
if (rc < 0) {
LOGHNB(hnb, DHNBAP, LOGL_ERROR,
"Failed to encode HNBAP UE Register Accept message for UE IMSI-%s\n", imsi);
return rc;
}
@@ -204,13 +206,18 @@ static int hnbgw_tx_ue_register_acc(struct ue_context *ue)
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBAP_UERegisterAccept, &accept_out);
return hnbgw_hnbap_tx(ue->hnb, msg);
rc = hnbgw_hnbap_tx(hnb, msg);
if (rc)
LOGHNB(hnb, DHNBAP, LOGL_ERROR,
"Failed to enqueue HNBAP UE Register Accept message for UE IMSI-%s\n", imsi);
return rc;
}
static int hnbgw_tx_ue_register_rej(struct hnb_context *hnb, HNBAP_UE_Identity_t *ue_id, const struct HNBAP_Cause *cause)
{
HNBAP_UERegisterReject_t reject_out;
HNBAP_UERegisterRejectIEs_t reject;
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
struct msgb *msg;
int rc;
@@ -271,6 +278,14 @@ static int hnbgw_tx_ue_register_rej(struct hnb_context *hnb, HNBAP_UE_Identity_t
ue_id->choice.pTMSIRAI.rAI.rAC.size);
break;
case HNBAP_UE_Identity_PR_iMSI:
ranap_bcd_decode(imsi, sizeof(imsi), ue_id->choice.iMSI.buf, ue_id->choice.iMSI.size);
LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "REJ UE_Id IMSI %s\n", imsi);
OCTET_STRING_fromBuf(&reject.uE_Identity.choice.iMSI,
(const char *)ue_id->choice.iMSI.buf, ue_id->choice.iMSI.size);
break;
default:
LOGHNB(hnb, DHNBAP, LOGL_ERROR, "Cannot compose UE Register Reject:"
" unsupported UE ID (present=%d)\n", ue_id->present);
@@ -312,6 +327,9 @@ static int hnbgw_tx_ue_register_rej(struct hnb_context *hnb, HNBAP_UE_Identity_t
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
&reject.uE_Identity.choice.pTMSIRAI.rAI.rAC);
break;
case HNBAP_UE_Identity_PR_iMSI:
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_OCTET_STRING,
&reject.uE_Identity.choice.iMSI);
default:
/* should never happen after above switch() */
@@ -330,8 +348,6 @@ static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Ident
struct msgb *msg;
uint32_t ctx_id;
uint32_t tmsi = 0;
struct ue_context *ue;
struct ue_context *ue_allocated = NULL;
int rc;
memset(&accept, 0, sizeof(accept));
@@ -377,11 +393,7 @@ static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Ident
tmsi = ntohl(tmsi);
LOGHNB(hnb, DHNBAP, LOGL_DEBUG, "HNBAP register with TMSI %x\n", tmsi);
ue = ue_context_by_tmsi(tmsi);
if (!ue)
ue = ue_allocated = ue_context_alloc(hnb, NULL, tmsi);
asn1_u24_to_bitstring(&accept.context_ID, &ctx_id, ue->context_id);
asn1_u24_to_bitstring(&accept.context_ID, &ctx_id, get_next_ue_ctx_id());
memset(&accept_out, 0, sizeof(accept_out));
rc = hnbap_encode_ueregisteraccepties(&accept_out, &accept);
@@ -414,11 +426,8 @@ static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Ident
}
if (rc < 0) {
LOGHNB(hnb, DHNBAP, LOGL_ERROR, "Failed to encode HNBAP UE Register Accept for TMSI 0x%08x\n", tmsi);
/* Encoding failed. Nothing in 'accept_out'. */
/* If we allocated the UE context but the UE REGISTER fails, get rid of it again: there will likely
* never be a UE DE-REGISTER for this UE from the HNB, and the ue_context would linger forever. */
if (ue_allocated)
ue_context_free(ue_allocated);
return rc;
}
@@ -429,13 +438,15 @@ static int hnbgw_tx_ue_register_acc_tmsi(struct hnb_context *hnb, HNBAP_UE_Ident
&accept_out);
rc = hnbgw_hnbap_tx(hnb, msg);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_HNBAP_UERegisterAccept, &accept_out);
if (rc)
LOGHNB(hnb, DHNBAP, LOGL_ERROR, "Failed to transmit HNBAP UE Register Accept for TMSI 0x%08x\n", tmsi);
return rc;
}
static int hnbgw_rx_hnb_deregister(struct hnb_context *ctx, ANY_t *in)
{
HNBAP_HNBDe_RegisterIEs_t ies;
HNBAP_Cause_t cause;
HNBAP_Cause_t cause = {};
int rc;
rc = hnbap_decode_hnbde_registeries(&ies, in);
@@ -460,12 +471,13 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
struct hnb_context *hnb, *tmp;
HNBAP_HNBRegisterRequestIEs_t ies;
int rc;
struct osmo_plmn_id plmn;
struct osmo_fd *ofd = osmo_stream_srv_get_ofd(ctx->conn);
char identity_str[256];
const char *cell_id_str;
struct timespec tp;
HNBAP_Cause_t cause;
HNBAP_Cause_t cause = {};
struct osmo_sockaddr cur_osa = {};
socklen_t len = sizeof(cur_osa);
rc = hnbap_decode_hnbregisterrequesties(&ies, in);
if (rc < 0) {
@@ -478,14 +490,25 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
/* copy all identity parameters from the message to ctx */
OSMO_STRLCPY_ARRAY(ctx->identity_info, identity_str);
/* We want to use struct umts_cell_id as hashtable key. If it ever happens to contain any padding bytes, make
* sure everything is deterministically zero. */
memset(&ctx->id, 0, sizeof(ctx->id));
ctx->id.lac = asn1str_to_u16(&ies.lac);
ctx->id.sac = asn1str_to_u16(&ies.sac);
ctx->id.rac = asn1str_to_u8(&ies.rac);
ctx->id.cid = asn1bitstr_to_u28(&ies.cellIdentity);
osmo_plmn_from_bcd(ies.plmNidentity.buf, &plmn);
ctx->id.mcc = plmn.mcc;
ctx->id.mnc = plmn.mnc;
cell_id_str = umts_cell_id_name(&ctx->id);
osmo_plmn_from_bcd(ies.plmNidentity.buf, &ctx->id.plmn);
cell_id_str = umts_cell_id_to_str(&ctx->id);
if (getpeername(ofd->fd, &cur_osa.u.sa, &len) < 0) {
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "HNB-REGISTER-REQ %s: rejecting due to getpeername() error: %s\n",
cell_id_str, strerror(errno));
hnbap_free_hnbregisterrequesties(&ies);
cause.present = HNBAP_Cause_PR_radioNetwork;
cause.choice.radioNetwork = HNBAP_CauseRadioNetwork_hNB_parameter_mismatch;
return hnbgw_tx_hnb_register_rej(ctx, &cause);
}
hnbp = hnb_persistent_find_by_id(&ctx->id);
if (!hnbp && g_hnbgw->config.accept_all_hnb)
@@ -498,6 +521,7 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
cause.choice.radioNetwork = HNBAP_CauseRadioNetwork_unauthorised_HNB;
return hnbgw_tx_hnb_register_rej(ctx, &cause);
}
ctx->persistent = hnbp;
hnbp->ctx = ctx;
@@ -511,22 +535,13 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
* fault (bug), and we release the old context to keep going... */
struct osmo_fd *other_fd = osmo_stream_srv_get_ofd(hnb->conn);
struct osmo_sockaddr other_osa = {};
struct osmo_sockaddr cur_osa = {};
socklen_t len = sizeof(other_osa);
if (getpeername(other_fd->fd, &other_osa.u.sa, &len) < 0) {
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "BUG! Found old registered HNB with invalid socket, releasing it\n");
hnb_context_release(hnb);
continue;
}
len = sizeof(cur_osa);
if (getpeername(ofd->fd, &cur_osa.u.sa, &len) < 0) {
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Error getpeername(): %s\n", strerror(errno));
if (osmo_sockaddr_cmp(&cur_osa, &other_osa) == 0) {
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "BUG! Found old registered HNB with same remote address, releasing it\n");
hnb_context_release(hnb);
continue;
}
} else if (osmo_sockaddr_cmp(&cur_osa, &other_osa) == 0) {
if (osmo_sockaddr_cmp(&cur_osa, &other_osa) == 0) {
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "BUG! Found old registered HNB with same remote address, releasing it\n");
hnb_context_release(hnb);
continue;
@@ -553,6 +568,8 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
ctx->hnb_registered = true;
hnb_persistent_registered(ctx->persistent);
/* Send HNBRegisterAccept */
rc = hnbgw_tx_hnb_register_acc(ctx);
hnbap_free_hnbregisterrequesties(&ies);
@@ -562,9 +579,7 @@ static int hnbgw_rx_hnb_register_req(struct hnb_context *ctx, ANY_t *in)
static int hnbgw_rx_ue_register_req(struct hnb_context *ctx, ANY_t *in)
{
HNBAP_UERegisterRequestIEs_t ies;
HNBAP_Cause_t cause;
struct ue_context *ue;
struct ue_context *ue_allocated = NULL;
HNBAP_Cause_t cause = {};
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
int rc;
@@ -621,18 +636,10 @@ static int hnbgw_rx_ue_register_req(struct hnb_context *ctx, ANY_t *in)
LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "UE-REGISTER-REQ ID_type=%d imsi=%s cause=%ld\n",
ies.uE_Identity.present, imsi, ies.registration_Cause);
ue = ue_context_by_imsi(imsi);
if (!ue)
ue = ue_allocated = ue_context_alloc(ctx, imsi, 0);
/* Send UERegisterAccept */
rc = hnbgw_tx_ue_register_acc(ue);
if (rc < 0) {
/* If we allocated the UE context but the UE REGISTER fails, get rid of it again: there will likely
* never be a UE DE-REGISTER for this UE from the HNB, and the ue_context would linger forever. */
if (ue_allocated)
ue_context_free(ue_allocated);
}
rc = hnbgw_tx_ue_register_acc(ctx, imsi, get_next_ue_ctx_id());
if (rc < 0)
LOGHNB(ctx, DHNBAP, LOGL_ERROR, "Failed to transmit HNBAP UE Register Accept for IMSI %s\n", imsi);
free_and_return_rc:
hnbap_free_ueregisterrequesties(&ies);
return rc;
@@ -641,8 +648,7 @@ free_and_return_rc:
static int hnbgw_rx_ue_deregister(struct hnb_context *ctx, ANY_t *in)
{
HNBAP_UEDe_RegisterIEs_t ies;
HNBAP_Cause_t cause;
struct ue_context *ue;
HNBAP_Cause_t cause = {};
int rc;
uint32_t ctxid;
@@ -665,9 +671,6 @@ static int hnbgw_rx_ue_deregister(struct hnb_context *ctx, ANY_t *in)
HNBAP_Criticality_ignore, HNBAP_TriggeringMessage_initiating_message);
} else {
LOGHNB(ctx, DHNBAP, LOGL_DEBUG, "UE-DE-REGISTER context=%u cause=%s\n", ctxid, hnbap_cause_str(&ies.cause));
ue = ue_context_by_id(ctxid);
if (ue)
ue_context_free(ue);
}
hnbap_free_uede_registeries(&ies);
@@ -740,8 +743,7 @@ static int hnbgw_rx_unsuccessful_outcome_msg(struct hnb_context *hnb, HNBAP_Unsu
{
/* We don't care much about HNBAP */
LOGHNB(hnb, DHNBAP, LOGL_ERROR, "Received Unsuccessful Outcome, procedureCode %ld, criticality %ld,"
" cell mcc %u mnc %u lac %u rac %u sac %u cid %u\n", msg->procedureCode, msg->criticality,
hnb->id.mcc, hnb->id.mnc, hnb->id.lac, hnb->id.rac, hnb->id.sac, hnb->id.cid);
" cell %s\n", msg->procedureCode, msg->criticality, umts_cell_id_to_str(&hnb->id));
return 0;
}

View File

@@ -35,6 +35,7 @@
#include <osmocom/hnbgw/hnbgw_cn.h>
#include <osmocom/hnbgw/context_map.h>
#include <osmocom/hnbgw/tdefs.h>
#include <osmocom/hnbgw/nft_kpi.h>
#include <osmocom/sigtran/protocol/sua.h>
#include <osmocom/sigtran/sccp_helpers.h>
#include <osmocom/netif/stream.h>
@@ -209,8 +210,9 @@ static void vty_dump_hnb_info(struct vty *vty, struct hnb_context *hnb)
vty_out(vty, "HNB ");
vty_out_ofd_addr(vty, hnb->conn? osmo_stream_srv_get_ofd(hnb->conn) : NULL);
vty_out(vty, " \"%s\"%s", hnb->identity_info, VTY_NEWLINE);
vty_out(vty, " MCC %u MNC %u LAC %u RAC %u SAC %u CID %u SCTP-stream:HNBAP=%u,RUA=%u%s",
hnb->id.mcc, hnb->id.mnc, hnb->id.lac, hnb->id.rac, hnb->id.sac, hnb->id.cid,
vty_out(vty, " MCC %s MNC %s LAC %u RAC %u SAC %u CID %u SCTP-stream:HNBAP=%u,RUA=%u%s",
osmo_mcc_name(hnb->id.plmn.mcc), osmo_mnc_name(hnb->id.plmn.mnc, hnb->id.plmn.mnc_3_digits),
hnb->id.lac, hnb->id.rac, hnb->id.sac, hnb->id.cid,
hnb->hnbap_stream, hnb->rua_stream, VTY_NEWLINE);
llist_for_each_entry(map, &hnb->map_list, hnb_list) {
@@ -227,12 +229,6 @@ static void vty_dump_hnb_info(struct vty *vty, struct hnb_context *hnb)
}
}
static void vty_dump_ue_info(struct vty *vty, struct ue_context *ue)
{
vty_out(vty, "UE IMSI \"%s\" context ID %u HNB %s%s", ue->imsi, ue->context_id,
hnb_context_name(ue->hnb), VTY_NEWLINE);
}
#define SHOW_HNB_STR SHOW_STR "Display information about HNB\n"
DEFUN(show_hnb, show_hnb_cmd, "show hnb all",
@@ -277,18 +273,6 @@ DEFUN(show_one_hnb, show_one_hnb_cmd, "show hnb NAME ",
return CMD_SUCCESS;
}
DEFUN(show_ue, show_ue_cmd, "show ue all",
SHOW_STR "Display HNBAP information about UE\n" "All UE\n")
{
struct ue_context *ue;
llist_for_each_entry(ue, &g_hnbgw->ue_list, list) {
vty_dump_ue_info(vty, ue);
}
return CMD_SUCCESS;
}
DEFUN(show_talloc, show_talloc_cmd, "show talloc", SHOW_STR "Display talloc info")
{
talloc_report_full(g_hnbgw, stderr);
@@ -878,6 +862,36 @@ DEFUN(cfg_hnbgw_no_hnb, cfg_hnbgw_no_hnb_cmd,
return CMD_SUCCESS;
}
#define NFT_KPI_STR "Retrieve traffic counters from nftables\n"
DEFUN(cfg_hnbgw_nft_kpi, cfg_hnbgw_nft_kpi_cmd,
"nft-kpi [TABLE_NAME]",
NFT_KPI_STR
"Set a custom nft table name to use, instead of 'osmo-hnbgw'\n")
{
const char *set_table_name = NULL;
if (argc > 0)
set_table_name = argv[0];
if (vty->type == VTY_TERM)
vty_out(vty, "%% WARNING: nft configuration changes need a restart of osmo-hnbgw%s", VTY_NEWLINE);
g_hnbgw->config.nft_kpi.enable = true;
osmo_talloc_replace_string(g_hnbgw, &g_hnbgw->config.nft_kpi.table_name, set_table_name);
return CMD_SUCCESS;
}
DEFUN(cfg_hnbgw_no_nft_kpi, cfg_hnbgw_no_nft_kpi_cmd,
"no nft-kpi",
NO_STR NFT_KPI_STR)
{
if (vty->type == VTY_TERM)
vty_out(vty, "%% WARNING: nft configuration changes need a restart of osmo-hnbgw%s", VTY_NEWLINE);
g_hnbgw->config.nft_kpi.enable = false;
return CMD_SUCCESS;
}
#if ENABLE_PFCP
static struct cmd_node pfcp_node = {
@@ -1001,6 +1015,12 @@ static int config_write_hnbgw(struct vty *vty)
_config_write_cnpool(vty, &g_hnbgw->sccp.cnpool_iucs);
_config_write_cnpool(vty, &g_hnbgw->sccp.cnpool_iups);
if (g_hnbgw->config.nft_kpi.enable)
vty_out(vty, " nft-kpi%s%s%s",
g_hnbgw->config.nft_kpi.table_name ? " " : "",
g_hnbgw->config.nft_kpi.table_name ? : "",
VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -1125,10 +1145,12 @@ void hnbgw_vty_init(void)
install_element(HNBGW_NODE, &cfg_hnbgw_no_hnb_cmd);
install_node(&hnb_node, NULL);
install_element(HNBGW_NODE, &cfg_hnbgw_nft_kpi_cmd);
install_element(HNBGW_NODE, &cfg_hnbgw_no_nft_kpi_cmd);
install_element_ve(&show_cnlink_cmd);
install_element_ve(&show_hnb_cmd);
install_element_ve(&show_one_hnb_cmd);
install_element_ve(&show_ue_cmd);
install_element_ve(&show_talloc_cmd);
install_element(HNBGW_NODE, &cfg_hnbgw_mgcp_cmd);

111
src/osmo-hnbgw/kpi_dtap.c Normal file
View File

@@ -0,0 +1,111 @@
/* KPI (statistics, counters) at DTAP level */
/* (C) 2024 by Harald Welte <laforge@osmocom.org>
* All Rights Reserved
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <osmocom/core/utils.h>
#include <osmocom/ranap/ranap_common_ran.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/hnbgw/hnbgw.h>
#include <osmocom/hnbgw/context_map.h>
#include <osmocom/hnbgw/kpi.h>
/***********************************************************************
* DOWNLINK messages
***********************************************************************/
void kpi_dtap_process_dl(struct hnbgw_context_map *map, const uint8_t *buf, unsigned int len,
uint8_t sapi)
{
struct hnb_persistent *hnbp = map->hnb_ctx->persistent;
const struct gsm48_hdr *gh = (const struct gsm48_hdr *)buf;
if (len < sizeof(*gh))
return;
/* if you make use of any data beyond the fixed-size gsm48_hdr, you must make sure the underlying
* buffer length is actually long enough! */
if (map->is_ps) {
/* Packet Switched Domain (from SGSN) */
switch (gsm48_hdr_msg_type(gh)) {
case GSM48_MT_GMM_ATTACH_ACK:
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_PS_ATT_ACK);
break;
case GSM48_MT_GMM_ATTACH_REJ:
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_PS_ATT_REJ);
break;
case GSM48_MT_GMM_RA_UPD_ACK:
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_PS_RAU_ACK);
break;
case GSM48_MT_GMM_RA_UPD_REJ:
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_PS_RAU_REJ);
break;
}
} else {
/* Circuit Switched Domain (from MSC) */
switch (gsm48_hdr_msg_type(gh)) {
case GSM48_MT_MM_LOC_UPD_ACCEPT:
/* FIXME: many LU are acknwoeldged implicitly with TMSI allocation */
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_CS_LU_ACC);
break;
case GSM48_MT_MM_LOC_UPD_REJECT:
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_CS_LU_REJ);
break;
}
}
}
/***********************************************************************
* UPLINK messages
***********************************************************************/
void kpi_dtap_process_ul(struct hnbgw_context_map *map, const uint8_t *buf, unsigned int len,
uint8_t sapi)
{
struct hnb_persistent *hnbp = map->hnb_ctx->persistent;
const struct gsm48_hdr *gh = (const struct gsm48_hdr *)buf;
if (len < sizeof(*gh))
return;
/* if you make use of any data beyond the fixed-size gsm48_hdr, you must make sure the underlying
* buffer length is actually long enough! */
if (map->is_ps) {
/* Packet Switched Domain (to SGSN) */
switch (gsm48_hdr_msg_type(gh)) {
case GSM48_MT_GMM_ATTACH_REQ:
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_PS_ATT_REQ);
break;
case GSM48_MT_GMM_RA_UPD_REQ:
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_PS_RAU_REQ);
break;
}
} else {
/* Circuit Switched Domain (to MSC) */
switch (gsm48_hdr_msg_type(gh)) {
case GSM48_MT_MM_LOC_UPD_REQUEST:
HNBP_CTR_INC(hnbp, HNB_CTR_DTAP_CS_LU_REQ);
break;
}
}
}

View File

@@ -36,14 +36,26 @@
static void kpi_ranap_process_dl_iu_rel_cmd(struct hnbgw_context_map *map, const ranap_message *ranap)
{
struct hnb_persistent *hnbp = map->hnb_ctx->persistent;
const RANAP_Cause_t *cause;
OSMO_ASSERT(ranap->procedureCode == RANAP_ProcedureCode_id_Iu_Release);
cause = &ranap->msg.iu_ReleaseCommandIEs.cause;
/* When Iu is released, all RABs are released implicitly */
for (unsigned int i = 0; i < ARRAY_SIZE(map->rab_state); i++) {
unsigned int ctr_num;
switch (map->rab_state[i]) {
case RAB_STATE_ACTIVE:
HNBP_CTR_INC(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT : HNB_CTR_RANAP_CS_RAB_REL_IMPLICIT);
if (cause->present == RANAP_Cause_PR_nAS ||
cause->choice.nAS == RANAP_CauseNAS_normal_release) {
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT;
} else {
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_IMPLICIT_ABNORMAL;
}
if (!map->is_ps)
ctr_num++;
HNBP_CTR_INC(hnbp, ctr_num);
break;
}
}
@@ -106,15 +118,12 @@ static void kpi_ranap_process_dl_rab_ass_req(struct hnbgw_context_map *map, rana
if (ies->presenceMask & RAB_ASSIGNMENTREQUESTIES_RANAP_RAB_RELEASELIST_PRESENT) {
RANAP_RAB_ReleaseList_t *r_list = &ies->raB_ReleaseList;
/* increment number of released RABs, we don't need to do that individually during iteration */
HNBP_CTR_ADD(hnbp, map->is_ps ? HNB_CTR_RANAP_PS_RAB_REL_REQ : HNB_CTR_RANAP_CS_RAB_REL_REQ,
r_list->raB_ReleaseList_ies.list.count);
for (unsigned int i = 0; i < r_list->raB_ReleaseList_ies.list.count; i++) {
RANAP_IE_t *release_list_ie = r_list->raB_ReleaseList_ies.list.array[i];
RANAP_RAB_ReleaseItemIEs_t _rab_rel_item_ies = {};
RANAP_RAB_ReleaseItemIEs_t *rab_rel_item_ies = &_rab_rel_item_ies;
RANAP_RAB_ReleaseItem_t *rab_rel_item;
unsigned int ctr_num;
uint8_t rab_id;
if (!release_list_ie)
@@ -133,6 +142,15 @@ static void kpi_ranap_process_dl_rab_ass_req(struct hnbgw_context_map *map, rana
switch (map->rab_state[rab_id]) {
case RAB_STATE_ACTIVE:
if (rab_rel_item->cause.present == RANAP_Cause_PR_nAS &&
rab_rel_item->cause.choice.nAS == RANAP_CauseNAS_normal_release) {
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_REQ;
} else {
ctr_num = HNB_CTR_RANAP_PS_RAB_REL_REQ_ABNORMAL;
}
if (!map->is_ps)
ctr_num++;
HNBP_CTR_INC(hnbp, ctr_num);
break;
default:
LOG_MAP(map, DRANAP, LOGL_NOTICE,
@@ -147,8 +165,27 @@ static void kpi_ranap_process_dl_rab_ass_req(struct hnbgw_context_map *map, rana
}
}
static void kpi_ranap_process_dl_direct_transfer(struct hnbgw_context_map *map, ranap_message *ranap)
{
const RANAP_DirectTransferIEs_t *dt_ies = &ranap->msg.directTransferIEs;
uint8_t sapi = 0;
if (dt_ies->presenceMask & DIRECTTRANSFERIES_RANAP_SAPI_PRESENT) {
if (dt_ies->sapi == RANAP_SAPI_sapi_3)
sapi = 3;
}
kpi_dtap_process_dl(map, dt_ies->nas_pdu.buf, dt_ies->nas_pdu.size, sapi);
}
void kpi_ranap_process_dl(struct hnbgw_context_map *map, ranap_message *ranap)
{
if (map->hnb_ctx == NULL) {
/* This can happen if the HNB has disconnected and we are processing downlink messages
* from the CN which were already in flight before the CN side has realized the HNB
* is gone. */
return;
}
switch (ranap->procedureCode) {
case RANAP_ProcedureCode_id_RAB_Assignment: /* RAB ASSIGNMENT REQ (8.2) */
kpi_ranap_process_dl_rab_ass_req(map, ranap);
@@ -156,6 +193,9 @@ void kpi_ranap_process_dl(struct hnbgw_context_map *map, ranap_message *ranap)
case RANAP_ProcedureCode_id_Iu_Release:
kpi_ranap_process_dl_iu_rel_cmd(map, ranap); /* IU RELEASE CMD (8.5) */
break;
case RANAP_ProcedureCode_id_DirectTransfer:
kpi_ranap_process_dl_direct_transfer(map, ranap);
break;
default:
break;
}
@@ -352,8 +392,29 @@ static void kpi_ranap_process_ul_rab_ass_resp(struct hnbgw_context_map *map, ran
}
}
static void kpi_ranap_process_ul_initial_ue(struct hnbgw_context_map *map, ranap_message *ranap)
{
const RANAP_InitialUE_MessageIEs_t *iue_ies = &ranap->msg.initialUE_MessageIEs;
kpi_dtap_process_ul(map, iue_ies->nas_pdu.buf, iue_ies->nas_pdu.size, 0);
}
static void kpi_ranap_process_ul_direct_transfer(struct hnbgw_context_map *map, ranap_message *ranap)
{
const RANAP_DirectTransferIEs_t *dt_ies = &ranap->msg.directTransferIEs;
uint8_t sapi = 0;
if (dt_ies->presenceMask & DIRECTTRANSFERIES_RANAP_SAPI_PRESENT) {
if (dt_ies->sapi == RANAP_SAPI_sapi_3)
sapi = 3;
}
kpi_dtap_process_ul(map, dt_ies->nas_pdu.buf, dt_ies->nas_pdu.size, sapi);
}
void kpi_ranap_process_ul(struct hnbgw_context_map *map, ranap_message *ranap)
{
/* we should never be processing uplink messages from a non-existant HNB */
OSMO_ASSERT(map->hnb_ctx);
switch (ranap->procedureCode) {
case RANAP_ProcedureCode_id_RAB_Assignment: /* RAB ASSIGNMENT REQ (8.2) */
kpi_ranap_process_ul_rab_ass_resp(map, ranap);
@@ -364,6 +425,12 @@ void kpi_ranap_process_ul(struct hnbgw_context_map *map, ranap_message *ranap)
* processing of the downlink Iu Release Command. It's not like the RNC/HNB has any way to
* refuse the release anyway. */
break;
case RANAP_ProcedureCode_id_InitialUE_Message:
kpi_ranap_process_ul_initial_ue(map, ranap);
break;
case RANAP_ProcedureCode_id_DirectTransfer:
kpi_ranap_process_ul_direct_transfer(map, ranap);
break;
default:
break;
}

View File

@@ -175,7 +175,7 @@ static void mgw_fsm_crcx_hnb_onenter(struct osmo_fsm_inst *fi, uint32_t prev_sta
mgw_info = (struct mgcp_conn_peer) {
.call_id = (map->rua_ctx_id << 8) | mgw_fsm_priv->rab_id,
.ptime = 20,
.conn_mode = MGCP_CONN_LOOPBACK,
.conn_mode = MGCP_CONN_RECV_ONLY,
};
mgw_info.codecs[0] = CODEC_IUFP;
mgw_info.codecs_len = 1;

1044
src/osmo-hnbgw/nft_kpi.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -222,6 +222,7 @@ int main(int argc, char **argv)
rc = osmo_init_logging2(g_hnbgw, &hnbgw_log_info);
if (rc < 0)
exit(1);
log_enable_multithread();
osmo_stats_init(g_hnbgw);
rc = rate_ctr_init(g_hnbgw);
@@ -329,6 +330,20 @@ int main(int argc, char **argv)
hnbgw_pfcp_init();
#endif
/* If nftables is enabled, initialize the nft table now or fail startup. This is important to immediately let
* the user know if cap_net_admin privileges are missing, and not only when the first hNodeB connects. */
if (g_hnbgw->config.nft_kpi.enable) {
#if ENABLE_NFTABLES
nft_kpi_init(g_hnbgw->config.nft_kpi.table_name);
/* There is no direct error handling here, because nftables initialization happens asynchronously.
* See nft_kpi.c nft_thread_t2m_cb(), case NFT_THREAD_INIT_TABLE to see what happens when initializing
* nftables failed. */
#else
fprintf(stderr, "ERROR: Cannot enable nft KPI, this binary was built without nftables support\n");
exit(1);
#endif
}
hnbgw_cnpool_start(&g_hnbgw->sccp.cnpool_iucs);
hnbgw_cnpool_start(&g_hnbgw->sccp.cnpool_iups);
@@ -345,6 +360,8 @@ int main(int argc, char **argv)
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
osmo_fsm_set_dealloc_ctx(OTC_SELECT);
while (1) {
rc = osmo_select_main_ctx(0);
if (rc < 0)

View File

@@ -294,7 +294,7 @@ static void ps_rab_ass_fsm_wait_local_f_teids(struct osmo_fsm_inst *fi, uint32_t
/* See whether all information is in so that we can forward the modified RAB Assignment Request to RUA. */
static void ps_rab_ass_req_check_local_f_teids(struct ps_rab_ass *rab_ass)
{
struct ps_rab *rab;
struct ps_rab *rab = NULL;
RANAP_RAB_AssignmentRequestIEs_t *ies = &rab_ass->ranap_rab_ass_req_message->msg.raB_AssignmentRequestIEs;
int i;
struct msgb *msg;
@@ -346,6 +346,12 @@ continue_cleanloop:
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_RANAP_RAB_SetupOrModifyItemFirst, &first);
}
if (!rab) {
LOG_PS_RAB_ASS(rab_ass, LOGL_ERROR, "Lookup PS RAB Assignment Request failed\n");
ps_rab_ass_failure(rab_ass);
return;
}
/* Send the modified RAB Assignment Request to the hNodeB, wait for the RAB Assignment Response */
msg = ranap_rab_ass_req_encode(ies);
if (!msg) {

View File

@@ -35,6 +35,13 @@ struct osmo_tdef hnbgw_T_defs[] = {
{.T = 3113, .default_val = 15, .desc = "Time to keep Paging record, for CN pools with more than one link" },
{.T = 4, .default_val = 5, .desc = "Timeout to receive RANAP RESET ACKNOWLEDGE from an MSC/SGSN" },
{.T = -31, .default_val = 15, .desc = "Timeout for establishing and releasing context maps (RUA <-> SCCP)" },
{.T = -34, .default_val = 1000, .unit = OSMO_TDEF_MS, .desc = "Period to query network traffic stats from netfilter" },
{
.T = -35,
.default_val = 60*60*24*7,
.desc = "Clean up all hNodeB persistent state after this time of the hNodeB being disconnected."
" Set to zero to never clear hNodeB persistent state. (default is 60*60*24*27 = a week)",
},
{.T = -1002, .default_val = 10, .desc = "Timeout for the HNB to respond to PS RAB Assignment Request" },
{ }
};

View File

@@ -1,5 +1,6 @@
SUBDIRS = \
ranap_rab_ass \
umts_cell_id \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
@@ -42,7 +43,6 @@ DISTCLEANFILES = \
if ENABLE_EXT_TESTS
python-tests:
$(MAKE) vty-test
$(MAKE) config-tests
osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
else
@@ -68,6 +68,11 @@ vty-test: $(top_builddir)/src/osmo-hnbgw/osmo-hnbgw
$(U) $(srcdir)/$(VTY_TEST)
# Test each config/*.cfg file with the corresponding config/*.vty transcript test.
#
# To be invoked manually only: This is not part of 'make check' because the
# output may change when libosmo-sccp changes its VTY appearance, which can
# cause annoying test fallout.
#
# Each config test runs an osmo-hnbgw process to talk to, so they must not run concurrently.
# To prevent 'make -j N' from running these tests in parallel, call a script with a linear for-loop.
.PHONY: config-tests

View File

@@ -20,6 +20,7 @@ OsmoHNBGW(config-hnbgw)# list
iups
hnb UMTS_CELL_ID
no hnb IDENTITY_INFO
nft-kpi [TABLE_NAME]
...
OsmoHNBGW(config-hnbgw)# plmn?
@@ -82,3 +83,39 @@ hnbgw
...
rnc-id 42
...
OsmoHNBGW(config-hnbgw)# nft-kpi?
nft-kpi Retrieve traffic counters from nftables
OsmoHNBGW(config-hnbgw)# nft-kpi ?
[TABLE_NAME] Set a custom nft table name to use, instead of 'osmo-hnbgw'
OsmoHNBGW(config-hnbgw)# show running-config
... !nft-kpi
OsmoHNBGW(config-hnbgw)# nft-kpi
% WARNING: nft configuration changes need a restart of osmo-hnbgw
OsmoHNBGW(config-hnbgw)# show running-config
...
hnbgw
...
nft-kpi
...
OsmoHNBGW(config-hnbgw)# no nft-kpi
% WARNING: nft configuration changes need a restart of osmo-hnbgw
OsmoHNBGW(config-hnbgw)# show running-config
... !nft-kpi
OsmoHNBGW(config-hnbgw)# nft-kpi maple
% WARNING: nft configuration changes need a restart of osmo-hnbgw
OsmoHNBGW(config-hnbgw)# show running-config
...
hnbgw
...
nft-kpi maple
...
OsmoHNBGW(config-hnbgw)# no nft-kpi
% WARNING: nft configuration changes need a restart of osmo-hnbgw
OsmoHNBGW(config-hnbgw)# show running-config
... !nft-kpi

View File

@@ -5,4 +5,10 @@ AT_SETUP([ranap_rab_ass])
AT_KEYWORDS([ranap_rab_ass])
cat $abs_srcdir/ranap_rab_ass/ranap_rab_ass_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/ranap_rab_ass/ranap_rab_ass_test], [0], [expout], [ignore])
AT_CLEANUP
AT_CLEANUP
AT_SETUP([umts_cell_id])
AT_KEYWORDS([umts_cell_id])
cat $abs_srcdir/umts_cell_id/umts_cell_id_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/umts_cell_id/umts_cell_id_test], [0], [expout], [ignore])
AT_CLEANUP

View File

@@ -0,0 +1,38 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
$(NULL)
AM_CFLAGS = \
-Wall \
-ggdb3 \
$(LIBASN1C_CFLAGS) \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMORANAP_CFLAGS) \
$(LIBOSMOSIGTRAN_CFLAGS) \
$(LIBOSMOMGCPCLIENT_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = -no-install
EXTRA_DIST = \
umts_cell_id_test.ok \
$(NULL)
check_PROGRAMS = \
umts_cell_id_test \
$(NULL)
umts_cell_id_test_SOURCES = \
umts_cell_id_test.c \
$(NULL)
umts_cell_id_test_LDADD = \
$(top_builddir)/src/osmo-hnbgw/libhnbgw.la \
$(NULL)
.PHONY: update_exp
update_exp:
$(builddir)/umts_cell_id_test >$(srcdir)/umts_cell_id_test.ok

View File

@@ -0,0 +1,160 @@
#include <stdio.h>
#include <osmocom/hnbgw/hnbgw.h>
struct test {
const char *id_str;
int expect_rc;
struct umts_cell_id id;
};
struct test tests[] = {
{
.id_str = "001-01-L1-R1-S1-C1",
.id = {
.plmn = {
.mcc = 1,
.mnc = 1,
},
.lac = 1,
.rac = 1,
.sac = 1,
.cid = 1,
},
},
/* ensure that a 3-digit MNC with leading zeroes is kept separate from two-digit MNC */
{
.id_str = "001-001-L1-R1-S1-C1",
.id = {
.plmn = {
.mcc = 1,
.mnc = 1,
.mnc_3_digits = true,
},
.lac = 1,
.rac = 1,
.sac = 1,
.cid = 1,
},
},
{
.id_str = "001-099-L1-R1-S1-C1",
.id = {
.plmn = {
.mcc = 1,
.mnc = 99,
.mnc_3_digits = true,
},
.lac = 1,
.rac = 1,
.sac = 1,
.cid = 1,
},
},
{
.id_str = "001-99-L1-R1-S1-C1",
.id = {
.plmn = {
.mcc = 1,
.mnc = 99,
.mnc_3_digits = false,
},
.lac = 1,
.rac = 1,
.sac = 1,
.cid = 1,
},
},
{
.id_str = "999-999-L65534-R65535-S65535-C268435455",
.id = {
.plmn = {
.mcc = 999,
.mnc = 999,
.mnc_3_digits = true,
},
.lac = 65534,
.rac = 65535,
.sac = 65535,
.cid = (1 << 28) - 1,
},
},
{
.id_str = "1000-001-L1-R1-S1-C1",
.expect_rc = -EINVAL,
},
{
.id_str = "001-001-L65535-R1-S1-C1",
.expect_rc = -EINVAL,
},
/* TODO? There is no bounds checking on RAC and SAC.
{
.id_str = "001-001-L1-R65536-S1-C1",
.expect_rc = -EINVAL,
},
{
.id_str = "001-001-L1-R1-S65536-C1",
.expect_rc = -EINVAL,
},
*/
{
.id_str = "001-001-L1-R1-S1-C268435456",
.expect_rc = -EINVAL,
},
};
int main(void)
{
struct test *t;
for (t = tests; (t - tests) < ARRAY_SIZE(tests); t++) {
int rc;
struct umts_cell_id parsed;
char to_str[128] = {};
printf("\"%s\"\n", t->id_str);
memset(&parsed, 0x2b, sizeof(parsed));
rc = umts_cell_id_from_str(&parsed, t->id_str);
if (rc != t->expect_rc) {
printf(" ERROR: umts_cell_id_from_str(): expected rc == %d, got %d\n",
t->expect_rc, rc);
continue;
}
if (rc) {
if (rc == t->expect_rc)
printf(" expected rc != 0: ok\n");
continue;
}
printf(" -> umts_cell_id_from_str(): ok\n");
rc = umts_cell_id_to_str_buf(to_str, sizeof(to_str), &parsed);
if (rc <= 0) {
printf(" ERROR: umts_cell_id_to_str_buf(): expected rc == 0, got %d\n", rc);
continue;
} else {
printf(" -> umts_cell_id_to_str_buf(): ok\n");
if (strcmp(t->id_str, to_str))
printf(" ERROR: conversion to umts_cell_id and back to string doesn't return the original string\n");
printf(" -> \"%s\"\n", to_str);
}
if (umts_cell_id_equal(&t->id, &parsed)) {
printf(" umts_cell_id_equal(expected, parsed): ok\n");
} else {
char to_str_expect[128] = {};
umts_cell_id_to_str_buf(to_str_expect, sizeof(to_str_expect), &t->id);
printf(" ERROR: umts_cell_id_equal(expected, parsed) == false\n");
printf(" expected %s\n", to_str_expect);
printf(" got %s\n", to_str);
printf(" expected %s\n", osmo_hexdump((void *)&t->id, sizeof(t->id)));
printf(" got %s\n", osmo_hexdump((void *)&parsed, sizeof(t->id)));
}
}
return 0;
}

View File

@@ -0,0 +1,31 @@
"001-01-L1-R1-S1-C1"
-> umts_cell_id_from_str(): ok
-> umts_cell_id_to_str_buf(): ok
-> "001-01-L1-R1-S1-C1"
umts_cell_id_equal(expected, parsed): ok
"001-001-L1-R1-S1-C1"
-> umts_cell_id_from_str(): ok
-> umts_cell_id_to_str_buf(): ok
-> "001-001-L1-R1-S1-C1"
umts_cell_id_equal(expected, parsed): ok
"001-099-L1-R1-S1-C1"
-> umts_cell_id_from_str(): ok
-> umts_cell_id_to_str_buf(): ok
-> "001-099-L1-R1-S1-C1"
umts_cell_id_equal(expected, parsed): ok
"001-99-L1-R1-S1-C1"
-> umts_cell_id_from_str(): ok
-> umts_cell_id_to_str_buf(): ok
-> "001-99-L1-R1-S1-C1"
umts_cell_id_equal(expected, parsed): ok
"999-999-L65534-R65535-S65535-C268435455"
-> umts_cell_id_from_str(): ok
-> umts_cell_id_to_str_buf(): ok
-> "999-999-L65534-R65535-S65535-C268435455"
umts_cell_id_equal(expected, parsed): ok
"1000-001-L1-R1-S1-C1"
expected rc != 0: ok
"001-001-L65535-R1-S1-C1"
expected rc != 0: ok
"001-001-L1-R1-S1-C268435456"
expected rc != 0: ok