mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-sgsn.git
synced 2025-11-02 13:13:16 +00:00
Compare commits
98 Commits
1.11.1
...
1fd205f0b8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fd205f0b8 | ||
|
|
b062c3647e | ||
|
|
d6c29beaf3 | ||
|
|
a24ebc7051 | ||
|
|
611b32811e | ||
|
|
7673bbd7ad | ||
|
|
b9c9271a1f | ||
|
|
dfcb514076 | ||
|
|
009c25f968 | ||
|
|
202039ed02 | ||
|
|
ecaa198fa0 | ||
|
|
eb5959f6b9 | ||
|
|
c1f2de5790 | ||
|
|
a0203ce7ff | ||
|
|
20d032bdd2 | ||
|
|
203676d047 | ||
|
|
265bc33b05 | ||
|
|
77cf72ea91 | ||
|
|
ad61ccd07b | ||
|
|
02fbdb59c2 | ||
|
|
b1444a6c11 | ||
|
|
6043666db5 | ||
|
|
6534fdbf27 | ||
|
|
2d4fe639cc | ||
|
|
4ba6638840 | ||
|
|
bae75b9a60 | ||
|
|
c156925405 | ||
|
|
2422c0375b | ||
|
|
fec2e5884a | ||
|
|
4b894f1a85 | ||
|
|
328edfd6f8 | ||
|
|
9eb1f7e006 | ||
|
|
f34503289e | ||
|
|
dda9c6780b | ||
|
|
234da0eed4 | ||
|
|
b79797d056 | ||
|
|
34182d28a4 | ||
|
|
901ca02e0e | ||
|
|
944b6dfb8d | ||
|
|
27dd6d84ec | ||
|
|
028c299bb2 | ||
|
|
d3b037ccfa | ||
|
|
921af3d428 | ||
|
|
0e8f055631 | ||
|
|
df44350b4b | ||
|
|
7a45b57485 | ||
|
|
7d94476bde | ||
|
|
6668b2b29a | ||
|
|
b69e326e69 | ||
|
|
11854f52ac | ||
|
|
61d3e3a377 | ||
|
|
d4bded987b | ||
|
|
99bc975225 | ||
|
|
7138f8eb93 | ||
|
|
5b289e83cf | ||
|
|
8a2ed97336 | ||
|
|
72283a1686 | ||
|
|
e7303adc8d | ||
|
|
e8c82d9cd1 | ||
|
|
6213201b95 | ||
|
|
3087322c3e | ||
|
|
b12c892a42 | ||
|
|
a39a564906 | ||
|
|
13592d2fa5 | ||
|
|
12ed86d17a | ||
|
|
35c178e84d | ||
|
|
868d818e6e | ||
|
|
140017e0ed | ||
|
|
c239545936 | ||
|
|
9650961c4b | ||
|
|
c05dad1035 | ||
|
|
7d5337fe80 | ||
|
|
2cd06e28f4 | ||
|
|
0bab0007c7 | ||
|
|
4ced617eb6 | ||
|
|
f8d248e2d1 | ||
|
|
bea6a0ffb5 | ||
|
|
a13cc20c98 | ||
|
|
1fd4a4c135 | ||
|
|
b3ce06058c | ||
|
|
0a6fe9727f | ||
|
|
7ba62d3061 | ||
|
|
62b28ffe1b | ||
|
|
cdf716cf9a | ||
|
|
a9c39c04de | ||
|
|
282de031f1 | ||
|
|
c3156193da | ||
|
|
cd3a8cfad6 | ||
|
|
e51b3be379 | ||
|
|
b34e0a5720 | ||
|
|
05363e0a32 | ||
|
|
a07e6d9c58 | ||
|
|
1ede89a35a | ||
|
|
647304fbe5 | ||
|
|
370d6e8b59 | ||
|
|
dc4c294f94 | ||
|
|
f2545b1b8f | ||
|
|
1f1d90f175 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
open_collective: osmocom
|
||||
@@ -19,7 +19,7 @@ SUBDIRS = \
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
contrib/osmo-sgsn.spec.in \
|
||||
README.md \
|
||||
debian \
|
||||
git-version-gen \
|
||||
osmoappdesc.py \
|
||||
|
||||
45
README.md
45
README.md
@@ -1,28 +1,30 @@
|
||||
osmo-sgssn - Osmocom SGSN Implementation
|
||||
========================================
|
||||
osmo-sgsn - Osmocom SGSN Implementation
|
||||
=======================================
|
||||
|
||||
This repository contains a C-language implementation of a GSM Serving GPRS
|
||||
Support Node (SGSN) for 2G (GSM) and 3G (UMTS). It is part of the
|
||||
This repository contains a C-language implementation of a *Serving GPRS
|
||||
Support Node (SGSN)* for 2.5/2.75G (GPRS/EDGE) and 3G (UMTS). It is part of the
|
||||
[Osmocom](https://osmocom.org/) Open Source Mobile Communications
|
||||
project.
|
||||
|
||||
OsmoSGSN exposes
|
||||
* Gb towards PCUs (e.g. OsmoPCU): Various GbIP flavors + Gb/FR/E1
|
||||
* GTP towards a GGSN (e.g. OsmoGGSN);
|
||||
* IuPS over IP towards RNCs / HNBGW (e.g. osmo-hnbgw)
|
||||
* The Osmocom typical telnet VTY and CTRL interfaces.
|
||||
* The Osmocom typical statsd exporter.
|
||||
* GSUP (custom MAP-like protocol) towards osmo-hlr
|
||||
|
||||
* *Gb* towards PCUs (e.g. [OsmoPCU](https://osmocom.org/projects/osmopcu/wiki/OsmoPCU)): Various GbIP flavors + Gb/FR/E1
|
||||
* *GTP* towards a GGSN (e.g. [OsmoGGSN](https://osmocom.org/projects/openggsn/wiki))
|
||||
* IuPS over IP towards RNCs / HNBGW (e.g. [osmo-hnbgw](https://osmocom.org/projects/osmohnbgw/wiki))
|
||||
* The Osmocom typical telnet *VTY* and *CTRL* interfaces.
|
||||
* The Osmocom typical *statsd* exporter.
|
||||
* GSUP (custom MAP-like protocol) towards [osmo-hlr](https://osmocom.org/projects/osmo-hlr/wiki/OsmoHLR)
|
||||
|
||||
OsmoSGSN implements
|
||||
|
||||
* GPRS mobility management
|
||||
* GPRS session management
|
||||
|
||||
|
||||
Homepage
|
||||
--------
|
||||
|
||||
You can find the OsmoSGSN issue tracker and wiki online at
|
||||
<https://osmocom.org/projects/osmosgsn> and <https://osmocom.org/projects/osmosgsn/wiki>.
|
||||
You can find the OsmoSGSN homepage online at <https://osmocom.org/projects/osmosgsn/wiki>.
|
||||
|
||||
|
||||
GIT Repository
|
||||
@@ -46,6 +48,14 @@ Pre-rendered PDF version of the current "master" can be found at
|
||||
as well as the [VTY Reference Manual](https://ftp.osmocom.org/docs/latest/osmosgsn-vty-reference.pdf)
|
||||
|
||||
|
||||
Forum
|
||||
-----
|
||||
|
||||
We welcome any osmo-sgsn related discussions in the
|
||||
[Cellular Network Infratructure -> 2G/3G Core Network](https://discourse.osmocom.org/c/cni/2g-3g-cn).
|
||||
section of the osmocom discourse (web based Forum).
|
||||
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
|
||||
@@ -58,13 +68,22 @@ Please observe the [Osmocom Mailing List
|
||||
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
|
||||
when posting.
|
||||
|
||||
|
||||
Issue Tracker
|
||||
-------------
|
||||
|
||||
We use the [issue tracker of the osmo-sgsn project on osmocom.org](https://osmocom.org/projects/osmosgsn/issues) for
|
||||
tracking the state of bug reports and feature requests. Feel free to submit any issues you may find, or help
|
||||
us out by resolving existing issues.
|
||||
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Our coding standards are described at
|
||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
|
||||
|
||||
We us a gerrit based patch submission/review process for managing
|
||||
We use a Gerrit based patch submission/review process for managing
|
||||
contributions. Please see
|
||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> for
|
||||
more details
|
||||
|
||||
16
TODO-RELEASE
16
TODO-RELEASE
@@ -1 +1,15 @@
|
||||
#component what description / commit summary line
|
||||
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
|
||||
# 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: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
|
||||
libgtp >1.12.0 new field dir_tun_flags in struct pdp_t
|
||||
libgtp >1.12.0 gtp_set_cb_update_context_ind(), gtp_update_context_resp()
|
||||
libosmocore >1.10.0 enum gsm48_gprs_ie_mm: GSM48_IE_GMM_UE_NET_CAP, GSM48_IE_GMM_VD_PREF_UE_USAGE
|
||||
libosmo-gsup-client >1.8.0 osmo_gsup_client_is_connected(), osmo_gsup_client_get_rem_addr(), osmo_gsup_client_get_rem_port()
|
||||
osmo-iuh >1.7.0 ranap multiple params are constified, see osmo-iuh Change-Id I667dc2ef377c1ceb5b11315458f00b282c143c81
|
||||
osmo-iuh >1.7.0 ranap_new_msg_error_ind()
|
||||
23
configure.ac
23
configure.ac
@@ -38,23 +38,22 @@ dnl use a defined standard across all builds and don't depend on compiler defaul
|
||||
CFLAGS="$CFLAGS -std=gnu11"
|
||||
|
||||
dnl checks for libraries
|
||||
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(LIBOSMOGB, libosmogb >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.4.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSUPCLIENT, libosmo-gsup-client >= 1.7.0)
|
||||
PKG_CHECK_MODULES(LIBGTP, libgtp >= 1.11.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(LIBOSMOGB, libosmogb >= 1.10.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.6.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOGSUPCLIENT, libosmo-gsup-client >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBGTP, libgtp >= 1.12.0)
|
||||
|
||||
# Enable/disable 3G aka IuPS + IuCS support?
|
||||
AC_ARG_ENABLE([iu], [AS_HELP_STRING([--enable-iu], [Build 3G support, aka IuPS and IuCS interfaces])],
|
||||
[osmo_ac_iu="$enableval"],[osmo_ac_iu="no"])
|
||||
if test "x$osmo_ac_iu" = "xyes" ; then
|
||||
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.8.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBASN1C, libasn1c >= 0.9.30)
|
||||
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.5.0)
|
||||
PKG_CHECK_MODULES(LIBOSMORANAP, libosmo-ranap >= 1.6.0)
|
||||
AC_DEFINE(BUILD_IU, 1, [Define if we want to build IuPS and IuCS interfaces support])
|
||||
fi
|
||||
AM_CONDITIONAL(BUILD_IU, test "x$osmo_ac_iu" = "xyes")
|
||||
@@ -244,6 +243,7 @@ AC_OUTPUT(
|
||||
tests/Makefile
|
||||
tests/atlocal
|
||||
tests/gprs/Makefile
|
||||
tests/gprs_routing_area/Makefile
|
||||
tests/sgsn/Makefile
|
||||
tests/gtphub/Makefile
|
||||
tests/xid/Makefile
|
||||
@@ -255,5 +255,4 @@ AC_OUTPUT(
|
||||
doc/manuals/Makefile
|
||||
contrib/Makefile
|
||||
contrib/systemd/Makefile
|
||||
contrib/osmo-sgsn.spec
|
||||
Makefile)
|
||||
|
||||
@@ -32,13 +32,13 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
osmo-build-dep.sh libosmo-netif "" --disable-doxygen
|
||||
osmo-build-dep.sh libosmo-abis
|
||||
osmo-build-dep.sh libosmo-netif
|
||||
osmo-build-dep.sh osmo-ggsn
|
||||
osmo-build-dep.sh osmo-hlr
|
||||
|
||||
if [ "x$IU" = "x--enable-iu" ]; then
|
||||
osmo-build-dep.sh libosmo-sccp
|
||||
osmo-build-dep.sh libosmo-sigtran "" --disable-doxygen
|
||||
osmo-build-dep.sh libasn1c
|
||||
#osmo-build-dep.sh asn1c aper-prefix # only needed for make regen in osmo-iuh
|
||||
osmo-build-dep.sh osmo-iuh
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
#
|
||||
# spec file for package osmo-sgsn
|
||||
#
|
||||
# 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/4116
|
||||
%define _lto_cflags %{nil}
|
||||
|
||||
%define with_iu 1
|
||||
Name: osmo-sgsn
|
||||
Version: @VERSION@
|
||||
Release: 0
|
||||
Summary: Osmocom's SGSN for 2G and 3G packet-switched mobile networks
|
||||
License: AGPL-3.0-or-later AND GPL-2.0-or-later
|
||||
Group: Productivity/Telephony/Servers
|
||||
URL: https://osmocom.org/projects/osmosgsn
|
||||
Source: %{name}-%{version}.tar.xz
|
||||
BuildRequires: autoconf
|
||||
BuildRequires: automake
|
||||
BuildRequires: libtool
|
||||
BuildRequires: pkgconfig
|
||||
%if 0%{?suse_version}
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%endif
|
||||
BuildRequires: pkgconfig(libcares)
|
||||
BuildRequires: pkgconfig(libcrypto) >= 0.9.5
|
||||
BuildRequires: pkgconfig(libgtp) >= 1.11.0
|
||||
BuildRequires: pkgconfig(libosmo-gsup-client) >= 1.7.0
|
||||
BuildRequires: pkgconfig(libosmo-netif) >= 1.4.0
|
||||
BuildRequires: pkgconfig(libosmoabis) >= 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
|
||||
%{?systemd_requires}
|
||||
%if %{with_iu}
|
||||
BuildRequires: pkgconfig(libasn1c)
|
||||
BuildRequires: pkgconfig(libosmo-ranap) >= 1.5.0
|
||||
BuildRequires: pkgconfig(libosmo-sigtran) >= 1.8.0
|
||||
%endif
|
||||
|
||||
%description
|
||||
OsmoSGSN is Osmocom's Serving GPRS Support Node for 2G and 3G
|
||||
packet-switched mobile networks.
|
||||
|
||||
%package -n osmo-gtphub
|
||||
Summary: Osmocom GTP Hub: Proxy for GTP traffic between multiple SGSNs and GGSNs
|
||||
Group: Productivity/Telephony/Servers
|
||||
|
||||
%description -n osmo-gtphub
|
||||
Osmocom GTP Hub: Proxy for GTP traffic between multiple SGSNs and GGSNs.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
echo "%{version}" >.tarball-version
|
||||
autoreconf -fi
|
||||
%configure \
|
||||
%if %{with_iu}
|
||||
--enable-iu \
|
||||
%endif
|
||||
--docdir=%{_docdir}/%{name} \
|
||||
--with-systemdsystemunitdir=%{_unitdir}
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
%make_install
|
||||
|
||||
%if 0%{?suse_version}
|
||||
%preun %service_del_preun %{name}.service
|
||||
%postun %service_del_postun %{name}.service
|
||||
%pre %service_add_pre %{name}.service
|
||||
%post %service_add_post %{name}.service
|
||||
%preun -n osmo-gtphub %service_del_preun osmo-gtphub.service
|
||||
%postun -n osmo-gtphub %service_del_postun osmo-gtphub.service
|
||||
%pre -n osmo-gtphub %service_add_pre osmo-gtphub.service
|
||||
%post -n osmo-gtphub %service_add_post osmo-gtphub.service
|
||||
%endif
|
||||
|
||||
%check
|
||||
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
|
||||
|
||||
%files
|
||||
%doc AUTHORS README.md
|
||||
%dir %{_docdir}/%{name}/examples
|
||||
%dir %{_docdir}/%{name}/examples/osmo-sgsn
|
||||
%exclude %{_docdir}/%{name}/examples/osmo-gtphub
|
||||
%{_docdir}/%{name}/examples/osmo-sgsn/osmo-sgsn-accept-all.cfg
|
||||
%{_docdir}/%{name}/examples/osmo-sgsn/osmo-sgsn.cfg
|
||||
%{_docdir}/%{name}/examples/osmo-sgsn/osmo-sgsn_custom-sccp.cfg
|
||||
%{_bindir}/osmo-sgsn
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-sgsn.cfg
|
||||
%{_unitdir}/%{name}.service
|
||||
|
||||
%files -n osmo-gtphub
|
||||
%dir %{_docdir}/%{name}/examples
|
||||
%dir %{_docdir}/%{name}/examples/osmo-gtphub
|
||||
%{_docdir}/%{name}/examples/osmo-gtphub/osmo-gtphub-1iface.cfg
|
||||
%{_docdir}/%{name}/examples/osmo-gtphub/osmo-gtphub.cfg
|
||||
%{_bindir}/osmo-gtphub
|
||||
%dir %{_sysconfdir}/osmocom
|
||||
%config(noreplace) %{_sysconfdir}/osmocom/osmo-gtphub.cfg
|
||||
%{_unitdir}/osmo-gtphub.service
|
||||
|
||||
%changelog
|
||||
@@ -5,6 +5,8 @@ Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=osmocom
|
||||
Group=osmocom
|
||||
ExecStart=/usr/bin/osmo-gtphub -c /etc/osmocom/osmo-gtphub.cfg
|
||||
StateDirectory=osmocom
|
||||
WorkingDirectory=%S/osmocom
|
||||
|
||||
@@ -11,6 +11,8 @@ Type=simple
|
||||
StateDirectory=osmocom
|
||||
WorkingDirectory=%S/osmocom
|
||||
Restart=always
|
||||
User=osmocom
|
||||
Group=osmocom
|
||||
ExecStart=/usr/bin/osmo-sgsn -c /etc/osmocom/osmo-sgsn.cfg
|
||||
RestartSec=2
|
||||
|
||||
|
||||
40
debian/changelog
vendored
40
debian/changelog
vendored
@@ -1,3 +1,43 @@
|
||||
osmo-sgsn (1.12.0) unstable; urgency=medium
|
||||
|
||||
[ Andreas Eversberg ]
|
||||
* Use uniform log format for default config files
|
||||
|
||||
[ Vadim Yanitskiy ]
|
||||
* build: include README.md into the release tarball
|
||||
* gmm: cosmetic: fix preprocessor macro formatting
|
||||
* gmm: mmctx_timer_stop(): warn about timer not running
|
||||
* VTY: move default settings to sgsn_instance_alloc()
|
||||
* VTY: sync default UMTS UEA config with osmo-msc
|
||||
* README.md: cosmetic: fix a typo
|
||||
|
||||
[ Pau Espin Pedrol ]
|
||||
* gsup: Use new libosmogsm struct osmo_gsup_pdp_info fields
|
||||
|
||||
[ Harald Welte ]
|
||||
* Add funding link to github mirror
|
||||
* README.md: Overhaul (more links; improved formatting)
|
||||
* README.md: Add Forum and Issue Tracker sections
|
||||
|
||||
[ Max ]
|
||||
* .deb/.rpm: add osmocom user during package install
|
||||
|
||||
[ Oliver Smith ]
|
||||
* contrib/osmo-sgsn.spec: fix build for almalinux:8
|
||||
* .deb/.rpm: various fixes related to non-root
|
||||
* contrib: remove rpm spec file
|
||||
* debian/postinst: add checks, be verbose
|
||||
* sgsn/sgsn_vty: create state-dir
|
||||
* debian/osmo-gtphub.init: delete
|
||||
* doc: set state-dir to /var/lib/osmocom/osmo-sgsn
|
||||
|
||||
[ Alexander Couzens ]
|
||||
* docs: replace legacy NS with new NS2 chapters
|
||||
* docs: update year to 2024
|
||||
* docs: front page: use https:// instead of http://
|
||||
|
||||
-- Oliver Smith <osmith@sysmocom.de> Wed, 24 Jul 2024 17:31:38 +0200
|
||||
|
||||
osmo-sgsn (1.11.1) unstable; urgency=medium
|
||||
|
||||
[ Philipp Maier ]
|
||||
|
||||
16
debian/control
vendored
16
debian/control
vendored
@@ -11,16 +11,14 @@ Build-Depends: debhelper (>= 10),
|
||||
pkg-config,
|
||||
libtalloc-dev,
|
||||
libc-ares-dev,
|
||||
libgtp-dev (>= 1.11.0),
|
||||
libosmocore-dev (>= 1.9.0),
|
||||
libosmo-abis-dev (>= 1.5.0),
|
||||
libosmo-netif-dev (>= 1.4.0),
|
||||
libosmo-gsup-client-dev (>= 1.7.0),
|
||||
libgtp-dev (>= 1.12.0),
|
||||
libosmocore-dev (>= 1.10.0),
|
||||
libosmo-abis-dev (>= 1.6.0),
|
||||
libosmo-gsup-client-dev (>= 1.8.0),
|
||||
libasn1c-dev (>= 0.9.30),
|
||||
libosmo-ranap-dev (>= 1.5.0),
|
||||
libosmo-sigtran-dev (>= 1.8.0),
|
||||
libosmo-sccp-dev (>= 1.8.0),
|
||||
osmo-gsm-manuals-dev (>= 1.5.0)
|
||||
libosmo-ranap-dev (>= 1.6.0),
|
||||
libosmo-sigtran-dev (>= 1.9.0),
|
||||
osmo-gsm-manuals-dev (>= 1.6.0)
|
||||
Standards-Version: 3.9.8
|
||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-sgsn
|
||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-sgsn
|
||||
|
||||
150
debian/osmo-gtphub.init
vendored
150
debian/osmo-gtphub.init
vendored
@@ -1,150 +0,0 @@
|
||||
#!/bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: osmo-gtphub
|
||||
# Required-Start: $network $local_fs
|
||||
# Required-Stop:
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Osmocom GTP hub
|
||||
# Description: Osmocom GTP hub
|
||||
### END INIT INFO
|
||||
|
||||
# Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
|
||||
|
||||
# PATH should only include /usr/* if it runs after the mountnfs.sh script
|
||||
PATH=/sbin:/usr/sbin:/bin:/usr/bin
|
||||
NAME=osmo-gtphub # Introduce the short server's name here
|
||||
DESC="Osmocom GTP hub" # Introduce a short description here
|
||||
DAEMON=/usr/bin/osmo-gtphub # Introduce the server's location here
|
||||
SCRIPTNAME=/etc/init.d/osmo-gtphub
|
||||
|
||||
# Exit if the package is not installed
|
||||
[ -x $DAEMON ] || exit 0
|
||||
|
||||
# Read configuration variable file if it is present
|
||||
[ -r /etc/default/osmo-gtphub ] && . /etc/default/osmo-gtphub
|
||||
|
||||
# Load the VERBOSE setting and other rcS variables
|
||||
. /lib/init/vars.sh
|
||||
|
||||
# Define LSB log_* functions.
|
||||
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
DAEMON_ARGS="$DAEMON_ARGS -D -c $CONFIG_FILE"
|
||||
|
||||
#
|
||||
# Function that starts the daemon/service
|
||||
#
|
||||
do_start()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been started
|
||||
# 1 if daemon was already running
|
||||
# 2 if daemon could not be started
|
||||
start-stop-daemon --start --quiet --exec $DAEMON --test > /dev/null \
|
||||
|| return 1
|
||||
start-stop-daemon --start --quiet --exec $DAEMON -- \
|
||||
$DAEMON_ARGS \
|
||||
|| return 2
|
||||
# Add code here, if necessary, that waits for the process to be ready
|
||||
# to handle requests from services started subsequently which depend
|
||||
# on this one. As a last resort, sleep for some time.
|
||||
}
|
||||
|
||||
#
|
||||
# Function that stops the daemon/service
|
||||
#
|
||||
do_stop()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been stopped
|
||||
# 1 if daemon was already stopped
|
||||
# 2 if daemon could not be stopped
|
||||
# other if a failure occurred
|
||||
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME
|
||||
RETVAL="$?"
|
||||
[ "$RETVAL" = 2 ] && return 2
|
||||
# Wait for children to finish too if this is a daemon that forks
|
||||
# and if the daemon is only ever run from this initscript.
|
||||
# If the above conditions are not satisfied then add some other code
|
||||
# that waits for the process to drop all resources that could be
|
||||
# needed by services started subsequently. A last resort is to
|
||||
# sleep for some time.
|
||||
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
|
||||
[ "$?" = 2 ] && return 2
|
||||
return "$RETVAL"
|
||||
}
|
||||
|
||||
#
|
||||
# Function that sends a SIGHUP to the daemon/service
|
||||
#
|
||||
do_reload() {
|
||||
#
|
||||
# If the daemon can reload its configuration without
|
||||
# restarting (for example, when it is sent a SIGHUP),
|
||||
# then implement that here.
|
||||
#
|
||||
start-stop-daemon --stop --signal 1 --quiet $PIDFILE --name $NAME
|
||||
return 0
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
|
||||
do_start
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
stop)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
status)
|
||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
#reload|force-reload)
|
||||
#
|
||||
# If do_reload() is not implemented then leave this commented out
|
||||
# and leave 'force-reload' as an alias for 'restart'.
|
||||
#
|
||||
#log_daemon_msg "Reloading $DESC" "$NAME"
|
||||
#do_reload
|
||||
#log_end_msg $?
|
||||
#;;
|
||||
restart|force-reload)
|
||||
#
|
||||
# If the "reload" option is implemented then remove the
|
||||
# 'force-reload' alias
|
||||
#
|
||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1)
|
||||
do_start
|
||||
case "$?" in
|
||||
0) log_end_msg 0 ;;
|
||||
1) log_end_msg 1 ;; # Old process is still running
|
||||
*) log_end_msg 1 ;; # Failed to start
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
# Failed to stop
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
|
||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
|
||||
:
|
||||
38
debian/postinst
vendored
Executable file
38
debian/postinst
vendored
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh -e
|
||||
case "$1" in
|
||||
configure)
|
||||
# Create the osmocom group and user (if it doesn't exist yet)
|
||||
if ! getent group osmocom >/dev/null; then
|
||||
groupadd --system osmocom
|
||||
fi
|
||||
if ! getent passwd osmocom >/dev/null; then
|
||||
useradd \
|
||||
--system \
|
||||
--gid osmocom \
|
||||
--home-dir /var/lib/osmocom \
|
||||
--shell /sbin/nologin \
|
||||
--comment "Open Source Mobile Communications" \
|
||||
osmocom
|
||||
fi
|
||||
|
||||
# Fix permissions of previous (root-owned) install (OS#4107)
|
||||
if dpkg --compare-versions "$2" le "1.12.0"; then
|
||||
if [ -e /etc/osmocom/osmo-sgsn.cfg ]; then
|
||||
chown -v osmocom:osmocom /etc/osmocom/osmo-sgsn.cfg
|
||||
chmod -v 0660 /etc/osmocom/osmo-sgsn.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#
|
||||
@@ -5,6 +5,14 @@
|
||||
! For the test, try to use most config commands.
|
||||
!
|
||||
|
||||
log stderr
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
|
||||
line vty
|
||||
no login
|
||||
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
! Osmocom gtphub configuration
|
||||
!
|
||||
|
||||
log stderr
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
|
||||
line vty
|
||||
no login
|
||||
|
||||
|
||||
@@ -2,10 +2,19 @@
|
||||
! Osmocom SGSN configuration
|
||||
!
|
||||
!
|
||||
log stderr
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
sgsn
|
||||
gtp state-dir /var/lib/osmocom/osmo-sgsn
|
||||
gtp local-ip 127.0.0.1
|
||||
ggsn 0 remote-ip 127.0.0.2
|
||||
ggsn 0 gtp-version 1
|
||||
|
||||
@@ -2,10 +2,19 @@
|
||||
! Osmocom SGSN configuration
|
||||
!
|
||||
!
|
||||
log stderr
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
sgsn
|
||||
gtp state-dir /var/lib/osmocom/osmo-sgsn
|
||||
gtp local-ip 127.0.0.1
|
||||
ggsn 0 remote-ip 127.0.0.2
|
||||
ggsn 0 gtp-version 1
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
! Osmocom SGSN configuration
|
||||
!
|
||||
!
|
||||
log stderr
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
@@ -15,6 +23,7 @@ cs7 instance 0
|
||||
asp asp-clnt-OsmoSGSN-A
|
||||
routing-key 3 0.23.4
|
||||
sgsn
|
||||
gtp state-dir /var/lib/osmocom/osmo-sgsn
|
||||
gtp local-ip 127.0.0.1
|
||||
ggsn 0 remote-ip 127.0.0.2
|
||||
ggsn 0 gtp-version 1
|
||||
|
||||
@@ -15,6 +15,14 @@
|
||||
Conversion to asciidoc, removal of sysmoBTS specific parts.
|
||||
</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>3</revnumber>
|
||||
<date>April 2024</date>
|
||||
<authorinitials>AC</authorinitials>
|
||||
<revremark>
|
||||
Replace NS chapter with new NS2 chapter to match the code.
|
||||
</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
|
||||
<authorgroup>
|
||||
@@ -29,10 +37,21 @@
|
||||
<jobtitle>Managing Director</jobtitle>
|
||||
</affiliation>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Alexander</firstname>
|
||||
<surname>Couzens</surname>
|
||||
<email>acouzens@sysmocom.de</email>
|
||||
<authorinitials>AC</authorinitials>
|
||||
<affiliation>
|
||||
<shortaffil>sysmocom</shortaffil>
|
||||
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
|
||||
<jobtitle>Developer</jobtitle>
|
||||
</affiliation>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<copyright>
|
||||
<year>2013-2016</year>
|
||||
<year>2013-2024</year>
|
||||
<holder>sysmocom - s.f.m.c. GmbH</holder>
|
||||
</copyright>
|
||||
|
||||
@@ -47,8 +66,8 @@
|
||||
</para>
|
||||
<para>
|
||||
The Asciidoc source code of this manual can be found at
|
||||
<ulink url="http://git.osmocom.org/osmo-gsm-manuals/">
|
||||
http://git.osmocom.org/osmo-gsm-manuals/
|
||||
<ulink url="https://git.osmocom.org/osmo-gsm-manuals/">
|
||||
https://git.osmocom.org/osmo-gsm-manuals/
|
||||
</ulink>
|
||||
</para>
|
||||
</legalnotice>
|
||||
|
||||
@@ -21,7 +21,7 @@ include::{srcdir}/chapters/configuration.adoc[]
|
||||
|
||||
include::./common/chapters/cs7-config.adoc[]
|
||||
|
||||
include::./common/chapters/gb.adoc[]
|
||||
include::./common/chapters/gb-ns2.adoc[]
|
||||
|
||||
include::./common/chapters/control_if.adoc[]
|
||||
|
||||
|
||||
@@ -8,12 +8,14 @@ noinst_HEADERS = \
|
||||
gprs_gmm.h \
|
||||
gprs_gmm_fsm.h \
|
||||
gprs_gmm_attach.h \
|
||||
gprs_gmm_util.h \
|
||||
gprs_mm_state_gb_fsm.h \
|
||||
gprs_mm_state_iu_fsm.h \
|
||||
gprs_ns.h \
|
||||
gprs_llc.h \
|
||||
gprs_llc_xid.h \
|
||||
gprs_ranap.h \
|
||||
gprs_routing_area.h \
|
||||
gprs_sm.h \
|
||||
gprs_sndcp_comp.h \
|
||||
gprs_sndcp_dcomp.h \
|
||||
@@ -25,8 +27,12 @@ noinst_HEADERS = \
|
||||
gtp.h \
|
||||
gtp_ggsn.h \
|
||||
gtp_mme.h \
|
||||
iu_client.h \
|
||||
iu_rnc.h \
|
||||
iu_rnc_fsm.h \
|
||||
mmctx.h \
|
||||
pdpctx.h \
|
||||
sccp.h \
|
||||
sgsn.h \
|
||||
sgsn_rim.h \
|
||||
signal.h \
|
||||
|
||||
@@ -22,11 +22,11 @@ enum {
|
||||
DRANAP,
|
||||
DSUA,
|
||||
DV42BIS,
|
||||
DIUCS,
|
||||
DSIGTRAN,
|
||||
DGTP,
|
||||
DOBJ,
|
||||
DRIM,
|
||||
DRA, /* Routing Area handling */
|
||||
Debug_LastEntry,
|
||||
};
|
||||
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
struct osmo_prim_hdr;
|
||||
struct sgsn_mm_ctx;
|
||||
|
||||
/* Called by bssgp layer when a prim is received from lower layers. */
|
||||
int sgsn_bssgp_rx_prim(struct osmo_prim_hdr *oph);
|
||||
|
||||
/* called by the bssgp layer to send NS PDUs */
|
||||
int sgsn_bssgp_dispatch_ns_unitdata_req_cb(void *ctx, struct msgb *msg);
|
||||
|
||||
/* page a MS in its routing area */
|
||||
int sgsn_bssgp_page_ps_ra(struct sgsn_mm_ctx *mmctx);
|
||||
/* page a MS in a single cell */
|
||||
int sgsn_bssgp_page_ps_bvci(struct sgsn_mm_ctx *mmctx, uint16_t nsei, uint16_t bvci);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define _GPRS_GMM_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
@@ -9,6 +10,7 @@
|
||||
|
||||
struct sgsn_mm_ctx;
|
||||
struct gprs_llc_llme;
|
||||
struct osmo_routing_area_id;
|
||||
|
||||
int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm,
|
||||
const struct osmo_auth_vector *vec,
|
||||
@@ -28,12 +30,16 @@ void gsm0408_gprs_access_denied(struct sgsn_mm_ctx *mmctx, int gmm_cause);
|
||||
void gsm0408_gprs_access_cancelled(struct sgsn_mm_ctx *mmctx, int gmm_cause);
|
||||
void gsm0408_gprs_authenticate(struct sgsn_mm_ctx *mmctx);
|
||||
|
||||
int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli);
|
||||
int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli,
|
||||
int gprs_gmm_rx_suspend(struct osmo_routing_area_id *raid, uint32_t tlli);
|
||||
int gprs_gmm_rx_resume(struct osmo_routing_area_id *raid, uint32_t tlli,
|
||||
uint8_t suspend_ref);
|
||||
|
||||
int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme,
|
||||
bool drop_cipherable);
|
||||
#ifdef BUILD_IU
|
||||
int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id, uint16_t *sai);
|
||||
#endif /* ifdef BUILD_IU */
|
||||
|
||||
/* Has to be called whenever any PDU (signaling, data, ...) has been received */
|
||||
void gprs_gb_recv_pdu(struct sgsn_mm_ctx *mmctx, const struct msgb *msg);
|
||||
|
||||
@@ -44,7 +50,7 @@ int gsm48_tx_gmm_att_rej(struct sgsn_mm_ctx *mm,
|
||||
uint8_t gmm_cause);
|
||||
int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm);
|
||||
|
||||
int gprs_gmm_attach_req_ies(struct msgb *a, struct msgb *b);
|
||||
int gprs_gmm_msg_cmp(struct msgb *a, struct msgb *b);
|
||||
|
||||
int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx);
|
||||
/* TODO: move extract_subscr_* when gsm48_gmm_authorize() got removed */
|
||||
|
||||
42
include/osmocom/sgsn/gprs_gmm_util.h
Normal file
42
include/osmocom/sgsn/gprs_gmm_util.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
struct msgb;
|
||||
|
||||
extern const struct tlv_definition gsm48_gmm_ie_tlvdef;
|
||||
|
||||
/* 9.4.14 RAU Request */
|
||||
struct gprs_gmm_ra_upd_req {
|
||||
uint8_t skip_ind; /* 10.3.1 */
|
||||
uint8_t update_type; /* 10.5.5.18 */
|
||||
bool follow_up_req; /* 10.5.5.18 */
|
||||
uint8_t cksq; /* 10.5.1.2 */
|
||||
struct osmo_routing_area_id old_rai; /* 10.5.5.15 */
|
||||
uint8_t *ms_radio_cap; /* 10.5.5.12a */
|
||||
uint8_t ms_radio_cap_len;
|
||||
struct tlv_parsed tlv;
|
||||
};
|
||||
|
||||
struct gprs_gmm_att_req {
|
||||
uint8_t skip_ind; /* 10.3.1 */
|
||||
uint8_t attach_type; /* 10.5.5.2 */
|
||||
bool follow_up_req; /* 10.5.5.2 */
|
||||
uint8_t cksq; /* 10.5.1.2 */
|
||||
uint16_t drx_parms; /* 10.5.5.6 */
|
||||
struct osmo_mobile_identity mi; /* 10.5.1.4 */
|
||||
struct osmo_routing_area_id old_rai; /* 10.5.5.15 */
|
||||
uint8_t *ms_network_cap; /* 10.5.5.12 */
|
||||
uint8_t ms_network_cap_len;
|
||||
uint8_t *ms_radio_cap; /* 10.5.5.12a */
|
||||
uint8_t ms_radio_cap_len;
|
||||
struct tlv_parsed tlv;
|
||||
};
|
||||
|
||||
int gprs_gmm_parse_ra_upd_req(const struct msgb *msg, struct gprs_gmm_ra_upd_req *rau_req);
|
||||
int gprs_gmm_parse_att_req(const struct msgb *msg, struct gprs_gmm_att_req *att_req);
|
||||
@@ -17,6 +17,7 @@ enum mm_state_iu_fsm_events {
|
||||
E_PMM_PS_CONN_RELEASE,
|
||||
E_PMM_PS_CONN_ESTABLISH,
|
||||
E_PMM_RA_UPDATE, /* = Serving RNC relocation */
|
||||
E_PMM_RX_GGSN_GTPU_DT_EI, /* param: struct sgsn_pdp_ctx *pctx */
|
||||
};
|
||||
|
||||
extern struct osmo_fsm mm_state_iu_fsm;
|
||||
|
||||
@@ -1,32 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
|
||||
#ifdef BUILD_IU
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/ranap/ranap_msg_factory.h>
|
||||
#include <osmocom/ranap/iu_client.h>
|
||||
#include <osmocom/sgsn/iu_client.h>
|
||||
#include <osmocom/sgsn/sccp.h>
|
||||
|
||||
struct sgsn_mm_ctx;
|
||||
struct sgsn_pdp_ctx;
|
||||
|
||||
void activate_pdp_rabs(struct sgsn_mm_ctx *ctx);
|
||||
/* struct RANAP_GlobalRNC_ID with a coupled buffer where .buf points to.
|
||||
* Used to easily generate a struct RANAP_GlobalRNC_ID to encode,
|
||||
* see sgsn_ranap_iu_grnc_id_compose(). */
|
||||
struct iu_grnc_id {
|
||||
uint8_t plmn_buf[3];
|
||||
struct RANAP_GlobalRNC_ID grnc_id;
|
||||
};
|
||||
int sgsn_ranap_iu_grnc_id_compose(struct iu_grnc_id *dst, const struct osmo_rnc_id *src);
|
||||
|
||||
int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data);
|
||||
int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp);
|
||||
|
||||
/* free the Iu UE context */
|
||||
void sgsn_ranap_iu_free(struct sgsn_mm_ctx *ctx);
|
||||
int sgsn_ranap_iu_tx(struct msgb *msg, uint8_t sapi);
|
||||
int sgsn_ranap_iu_tx_rab_ps_ass_req(struct ranap_ue_conn_ctx *ue_ctx,
|
||||
uint8_t rab_id, uint32_t gtp_ip, uint32_t gtp_tei);
|
||||
int sgsn_ranap_iu_tx_sec_mode_cmd(struct ranap_ue_conn_ctx *uectx, struct osmo_auth_vector *vec,
|
||||
int send_ck, int new_key);
|
||||
int sgsn_ranap_iu_tx_common_id(struct ranap_ue_conn_ctx *ue_ctx, const char *imsi);
|
||||
|
||||
/* send a Iu Release Command and free afterwards the UE context */
|
||||
void sgsn_ranap_iu_release_free(struct sgsn_mm_ctx *ctx,
|
||||
const struct RANAP_Cause *cause);
|
||||
int sgsn_ranap_iu_tx_release(struct ranap_ue_conn_ctx *ctx, const struct RANAP_Cause *cause);
|
||||
/* Transmit a Iu Release Command and submit event RANAP_IU_EVENT_IU_RELEASE upon
|
||||
* Release Complete or timeout. Caller is responsible to free the context and
|
||||
* closing the SCCP connection (sgsn_ranap_iu_free_ue) upon recieval of the event. */
|
||||
void sgsn_ranap_iu_tx_release_free(struct ranap_ue_conn_ctx *ctx,
|
||||
const struct RANAP_Cause *cause,
|
||||
int timeout);
|
||||
|
||||
int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id, uint16_t *sai);
|
||||
int sgsn_ranap_iu_tx_cl(struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_sccp_addr *dst_addr,
|
||||
struct msgb *msg);
|
||||
int sgsn_ranap_iu_tx_error_ind(struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_sccp_addr *dst_addr,
|
||||
const RANAP_Cause_t *cause);
|
||||
|
||||
#else /* ifndef BUILD_IU */
|
||||
inline static void sgsn_ranap_iu_free(void *ctx) {};
|
||||
inline static void sgsn_ranap_iu_release_free(void *ctx, void *cause) {};
|
||||
#endif /* BUILD_IU*/
|
||||
void sgsn_ranap_iu_handle_co_initial(struct ranap_iu_rnc *iu_rnc,
|
||||
uint32_t conn_id,
|
||||
const ranap_message *message);
|
||||
void sgsn_ranap_iu_handle_co(struct ranap_ue_conn_ctx *ue_ctx, const ranap_message *message);
|
||||
|
||||
/* Entry points from rx SCCP: */
|
||||
int sgsn_ranap_iu_rx_cl_msg(struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_scu_unitdata_param *ud_prim,
|
||||
const uint8_t *data, size_t len);
|
||||
int sgsn_ranap_iu_rx_co_initial_msg(struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_sccp_addr *rem_sccp_addr,
|
||||
uint32_t conn_id,
|
||||
const uint8_t *data, size_t len);
|
||||
int sgsn_ranap_iu_rx_co_msg(struct ranap_ue_conn_ctx *ue_ctx, const uint8_t *data, size_t len);
|
||||
|
||||
#endif /* ifdef BUILD_IU */
|
||||
|
||||
struct ranap_ue_conn_ctx;
|
||||
/* On RANAP, Returns pointer to he associated ranap_ue_conn_ctx in msg, filled
|
||||
|
||||
140
include/osmocom/sgsn/gprs_routing_area.h
Normal file
140
include/osmocom/sgsn/gprs_routing_area.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/*! \file gprs_routing_area.h */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
|
||||
/* rai -> struct osmo_routing_area_id * */
|
||||
#define LOGRAI(level, rai, fmt, args...) \
|
||||
do {\
|
||||
char __log_rai_buf[32]; \
|
||||
LOGP(DRA, level, "RA(%s) " fmt, \
|
||||
osmo_rai_name2_buf(__log_rai_buf, sizeof(__log_rai_buf), rai), \
|
||||
## args); \
|
||||
} while (0)
|
||||
|
||||
/* ra -> struct sgsn_ra * */
|
||||
#define LOGRA(level, ra, fmt, args...) \
|
||||
LOGRAI(level, (&(ra)->rai), fmt, ## args)
|
||||
|
||||
struct sgsn_instance;
|
||||
struct sgsn_mm_ctx;
|
||||
|
||||
struct sgsn_ra_global {
|
||||
/* list of struct sgsn_ra */
|
||||
struct llist_head ra_list;
|
||||
};
|
||||
|
||||
enum sgsn_ra_ran_type {
|
||||
RA_TYPE_GERAN_Gb,
|
||||
RA_TYPE_UTRAN_Iu,
|
||||
};
|
||||
|
||||
extern const struct value_string sgsn_ra_ran_type_names[];
|
||||
|
||||
struct sgsn_ra {
|
||||
/* Entry in sgsn_ra_global->ra_list */
|
||||
struct llist_head list;
|
||||
|
||||
struct osmo_routing_area_id rai;
|
||||
|
||||
/* For GERAN: every PCU is connected to the SGSN. It allows the SGSN to know every single cell.
|
||||
* For routing, the SGSN must know to which PCU a given cell is connected.
|
||||
* It is possible that more than one PCU serves the same Routing Area.
|
||||
*
|
||||
* For UTRAN: only the RNC (HNB via HNBGW) is communicating with the SGSN.
|
||||
* The SGSN doesn't know every cell, because they aren't accepted individually by the SGSN.
|
||||
* The SGSN only "knows" RAI/SAI if they have been used. In the future it would be a good idea to
|
||||
* allow configuring RA in the vty/config as well.
|
||||
* Similar to the GERAN Cell, but iu_client doesn't notify us for every given SAI, only for RAC.
|
||||
* Further the SGSN doesn't get informed about Service Area and can't relate the SAI to a given UE.
|
||||
* For UTRAN only do a LAC/RAC <> RNC relation and don't have a specific cell relation.
|
||||
*/
|
||||
enum sgsn_ra_ran_type ran_type;
|
||||
union {
|
||||
struct {
|
||||
/* the RNC id must be the same for a given Routing Area */
|
||||
struct osmo_rnc_id rnc_id;
|
||||
} utran;
|
||||
} u;
|
||||
|
||||
/* GERAN/UTRAN: cells contains a list of sgsn_ra_cells which are alive */
|
||||
struct llist_head cells_alive_list;
|
||||
};
|
||||
|
||||
struct sgsn_ra_cell {
|
||||
/* Entry in sgsn_ra->cells */
|
||||
struct llist_head list;
|
||||
|
||||
/*! link back to the parent */
|
||||
struct sgsn_ra *ra;
|
||||
|
||||
enum sgsn_ra_ran_type ran_type;
|
||||
union {
|
||||
struct {
|
||||
uint16_t nsei;
|
||||
uint16_t bvci;
|
||||
uint16_t cell_id;
|
||||
} geran;
|
||||
|
||||
struct {
|
||||
/* the RNC id must be the same for a given Routing Area */
|
||||
uint16_t sac;
|
||||
} utran;
|
||||
} u;
|
||||
};
|
||||
|
||||
void sgsn_ra_init(struct sgsn_instance *inst);
|
||||
|
||||
struct sgsn_ra *sgsn_ra_alloc(const struct osmo_routing_area_id *rai, enum sgsn_ra_ran_type ran_type);
|
||||
struct sgsn_ra *sgsn_ra_find_or_create(const struct osmo_routing_area_id *rai, enum sgsn_ra_ran_type ran_type);
|
||||
struct sgsn_ra *sgsn_ra_get_ra(const struct osmo_routing_area_id *rai);
|
||||
void sgsn_ra_free(struct sgsn_ra *ra);
|
||||
struct sgsn_ra_cell *sgsn_ra_cell_alloc_geran(struct sgsn_ra *ra, uint16_t cell_id, uint16_t nsei, uint16_t bvci);
|
||||
void sgsn_ra_cell_free(struct sgsn_ra_cell *cell);
|
||||
|
||||
/* GERAN */
|
||||
/* Called by BSSGP layer to inform about a reset on a PtP BVCI */
|
||||
int sgsn_ra_geran_bvc_cell_reset_ind(uint16_t nsei, uint16_t bvci, struct osmo_cell_global_id_ps *cgi_ps);
|
||||
/* Called by BSSGP layer to inform about a reset on a Signal BVCI */
|
||||
void sgsn_ra_geran_bvc_sign_reset_ind(uint16_t nsei);
|
||||
/* FIXME: handle BVC BLOCK/UNBLOCK/UNAVAILABLE */
|
||||
/* Called by NS-VC layer to inform about an unavailable NSEI (and all BVCI on them) */
|
||||
int sgsn_ra_geran_nsei_failure_ind(uint16_t nsei);
|
||||
|
||||
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_cgi_ps(const struct osmo_cell_global_id_ps *cgi_ps);
|
||||
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_lai(const struct osmo_location_area_id *lai, uint16_t cell_id);
|
||||
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_cgi(const struct osmo_cell_global_id *cgi);
|
||||
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_ra(const struct sgsn_ra *ra, uint16_t cell_id);
|
||||
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_gb(uint16_t nsei, uint16_t bvci);
|
||||
|
||||
/* UTRAN */
|
||||
int sgsn_ra_utran_register(const struct osmo_routing_area_id *rai, const struct osmo_rnc_id *rnc_id);
|
||||
|
||||
struct sgsn_ra *sgsn_ra_geran_get_ra(const struct osmo_routing_area_id *rai);
|
||||
|
||||
/* Page the whole routing area for this mmctx */
|
||||
int sgsn_ra_geran_page_ra(const struct osmo_routing_area_id *rai, struct sgsn_mm_ctx *mmctx);
|
||||
struct sgsn_ra *sgsn_ra_utran_get_ra(const struct osmo_routing_area_id *rai);
|
||||
|
||||
/*
|
||||
* return value for callbacks.
|
||||
* STOP: stop calling the callback for the remaining cells, sgsn_ra_foreach_ra() returns 0
|
||||
* CONT: continue to call the callback for remaining cells
|
||||
* ABORT: stop calling the callback for the remaining cells, sgsn_ra_foreach_ra() returns -1
|
||||
*/
|
||||
#define SGSN_RA_CB_STOP 1
|
||||
#define SGSN_RA_CB_CONT 0
|
||||
#define SGSN_RA_CB_ERROR -1
|
||||
|
||||
typedef int (sgsn_ra_cb_t)(struct sgsn_ra_cell *ra_cell, void *cb_data);
|
||||
int sgsn_ra_foreach_cell(struct sgsn_ra *ra, sgsn_ra_cb_t *cb, void *cb_data);
|
||||
int sgsn_ra_foreach_cell2(struct osmo_routing_area_id *rai, sgsn_ra_cb_t *cb, void *cb_data);
|
||||
|
||||
/* Page the whole routing area for this mmctx */
|
||||
int sgsn_ra_utran_page_ra(const struct osmo_routing_area_id *rai, const struct sgsn_mm_ctx *mmctx);
|
||||
@@ -47,7 +47,7 @@ struct gprs_sndcp_entity {
|
||||
struct llist_head list;
|
||||
|
||||
/* FIXME: move this RA_ID up to the LLME or even higher */
|
||||
struct gprs_ra_id ra_id;
|
||||
struct osmo_routing_area_id rai;
|
||||
/* reference to the LLC Entity below this SNDCP entity */
|
||||
struct gprs_llc_lle *lle;
|
||||
/* The NSAPI we shall use on top of LLC */
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/socket.h>
|
||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
||||
|
||||
#include <osmocom/sgsn/apn.h>
|
||||
|
||||
@@ -54,7 +56,9 @@ struct sgsn_subscriber_pdp_data {
|
||||
struct llist_head list;
|
||||
|
||||
unsigned int context_id;
|
||||
uint16_t pdp_type;
|
||||
enum gsm48_pdp_type_org pdp_type_org;
|
||||
enum gsm48_pdp_type_nr pdp_type_nr;
|
||||
struct osmo_sockaddr pdp_address[2];
|
||||
char apn_str[GSM_APN_LENGTH];
|
||||
uint8_t qos_subscribed[20];
|
||||
size_t qos_subscribed_len;
|
||||
|
||||
@@ -23,8 +23,7 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
|
||||
uint16_t nsapi,
|
||||
struct tlv_parsed *tp);
|
||||
|
||||
int sgsn_gtp_data_req(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi,
|
||||
int sgsn_gtp_data_req(struct osmo_routing_area_id *rai, int32_t tlli, uint8_t nsapi,
|
||||
struct msgb *msg, uint32_t npdu_len, uint8_t *npdu);
|
||||
int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx);
|
||||
void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen);
|
||||
int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/gprs/protocol/gsm_24_301.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
|
||||
struct gsn_t;
|
||||
|
||||
@@ -20,6 +21,9 @@ struct sgsn_mme_ctx {
|
||||
char *name;
|
||||
struct in_addr remote_addr;
|
||||
|
||||
struct osmo_gummei gummei;
|
||||
bool gummei_valid;
|
||||
|
||||
/* is it the default route for outgoing message? are all incoming messages accepted? */
|
||||
bool default_route;
|
||||
};
|
||||
@@ -30,6 +34,7 @@ void sgsn_mme_ctx_free(struct sgsn_mme_ctx *mme);
|
||||
struct sgsn_mme_ctx *sgsn_mme_ctx_by_name(const struct sgsn_instance *sgsn, const char *name);
|
||||
struct sgsn_mme_ctx *sgsn_mme_ctx_by_addr(const struct sgsn_instance *sgsn, const struct in_addr *addr);
|
||||
struct sgsn_mme_ctx *sgsn_mme_ctx_by_route(const struct sgsn_instance *sgsn, const struct osmo_eutran_tai *tai);
|
||||
struct sgsn_mme_ctx *sgsn_mme_ctx_by_gummei(const struct sgsn_instance *sgsn, const struct osmo_gummei *gummei);
|
||||
struct sgsn_mme_ctx *sgsn_mme_ctx_by_default_route(const struct sgsn_instance *sgsn);
|
||||
|
||||
void sgsn_mme_ctx_route_add(struct sgsn_mme_ctx *mme, const struct osmo_eutran_tai *tai);
|
||||
|
||||
95
include/osmocom/sgsn/iu_client.h
Normal file
95
include/osmocom/sgsn/iu_client.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/core/defs.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
#include <osmocom/iuh/common.h>
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/vty.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
struct msgb;
|
||||
struct osmo_auth_vector;
|
||||
|
||||
struct RANAP_RAB_SetupOrModifiedItemIEs_s;
|
||||
|
||||
struct ranap_iu_rnc;
|
||||
|
||||
struct ranap_ue_conn_ctx {
|
||||
struct llist_head list; /* item in sgsn_sccp->ue_conn_ctx_list */
|
||||
struct ranap_iu_rnc *rnc;
|
||||
uint32_t conn_id;
|
||||
int integrity_active;
|
||||
struct gprs_ra_id ra_id;
|
||||
enum ranap_nsap_addr_enc rab_assign_addr_enc;
|
||||
bool notification; /* send notification to the upstream user */
|
||||
/* if true the ue_ctx will be free on Iu release complete */
|
||||
bool free_on_release;
|
||||
/* Will be set when the Iu Release Command has been sent */
|
||||
struct osmo_timer_list release_timeout;
|
||||
};
|
||||
|
||||
enum ranap_iu_event_new_area_type {
|
||||
RANAP_IU_NEW_LAC,
|
||||
RANAP_IU_NEW_RAC,
|
||||
};
|
||||
|
||||
struct ranap_iu_event_new_area {
|
||||
const struct osmo_rnc_id *rnc_id;
|
||||
enum ranap_iu_event_new_area_type cell_type;
|
||||
union {
|
||||
const struct osmo_location_area_id *lai;
|
||||
const struct osmo_routing_area_id *rai;
|
||||
} u;
|
||||
};
|
||||
|
||||
enum ranap_iu_event_type {
|
||||
RANAP_IU_EVENT_RAB_ASSIGN,
|
||||
RANAP_IU_EVENT_SECURITY_MODE_COMPLETE,
|
||||
RANAP_IU_EVENT_IU_RELEASE, /* An actual Iu Release message was received */
|
||||
RANAP_IU_EVENT_LINK_INVALIDATED, /* A SUA link was lost or closed down */
|
||||
RANAP_IU_EVENT_NEW_AREA, /* Either a new LAC/RAC has been detected */
|
||||
};
|
||||
|
||||
extern const struct value_string iu_client_event_type_names[];
|
||||
static inline const char *iu_client_event_type_str(enum ranap_iu_event_type e)
|
||||
{
|
||||
return get_value_string(iu_client_event_type_names, e);
|
||||
}
|
||||
|
||||
/* Implementations of iu_recv_cb_t shall find the ranap_ue_conn_ctx in msg->dst. */
|
||||
typedef int (*ranap_iu_recv_cb_t)(struct msgb *msg, struct gprs_ra_id *ra_id,
|
||||
uint16_t *sai);
|
||||
|
||||
typedef int (*ranap_iu_event_cb_t)(struct ranap_ue_conn_ctx *ue_ctx,
|
||||
enum ranap_iu_event_type type, void *data);
|
||||
|
||||
typedef int (*ranap_iu_rab_ass_resp_cb_t)(struct ranap_ue_conn_ctx *ue_ctx, uint8_t rab_id,
|
||||
struct RANAP_RAB_SetupOrModifiedItemIEs_s *setup_ies);
|
||||
|
||||
int global_iu_event(struct ranap_ue_conn_ctx *ue_ctx,
|
||||
enum ranap_iu_event_type type,
|
||||
void *data);
|
||||
|
||||
|
||||
int ranap_iu_init(void *ctx);
|
||||
|
||||
int ranap_iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac)
|
||||
OSMO_DEPRECATED("Use ranap_iu_page_cs2 instead");
|
||||
|
||||
int ranap_iu_page_ps(const char *imsi, const uint32_t *ptmsi, uint16_t lac, uint8_t rac)
|
||||
OSMO_DEPRECATED("Use ranap_iu_page_ps2 instead");
|
||||
|
||||
int ranap_iu_page_cs2(const char *imsi, const uint32_t *tmsi, const struct osmo_location_area_id *lai);
|
||||
int ranap_iu_page_ps2(const char *imsi, const uint32_t *ptmsi, const struct osmo_routing_area_id *rai);
|
||||
|
||||
|
||||
struct ranap_ue_conn_ctx *ue_conn_ctx_alloc(struct ranap_iu_rnc *rnc, uint32_t conn_id);
|
||||
|
||||
void ue_conn_ctx_link_invalidated_free(struct ranap_ue_conn_ctx *ue);
|
||||
|
||||
/* freeing the UE will release all resources
|
||||
* This will close the SCCP connection connected to the UE */
|
||||
void sgsn_ranap_iu_free_ue(struct ranap_ue_conn_ctx *ue_ctx);
|
||||
57
include/osmocom/sgsn/iu_rnc.h
Normal file
57
include/osmocom/sgsn/iu_rnc.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/defs.h>
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
#include <osmocom/iuh/common.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
struct iu_lac_rac_entry {
|
||||
struct llist_head entry;
|
||||
struct osmo_routing_area_id rai;
|
||||
};
|
||||
|
||||
/* A remote RNC (Radio Network Controller, like BSC but for UMTS) that has
|
||||
* called us and is currently reachable at the given osmo_sccp_addr. So, when we
|
||||
* know a LAC for a subscriber, we can page it at the RNC matching that LAC or
|
||||
* RAC. An HNB-GW typically presents itself as if it were a single RNC, even
|
||||
* though it may have several RNCs in hNodeBs connected to it. Those will then
|
||||
* share the same RNC id, which they actually receive and adopt from the HNB-GW
|
||||
* in the HNBAP HNB REGISTER ACCEPT message. */
|
||||
struct ranap_iu_rnc {
|
||||
struct llist_head entry;
|
||||
|
||||
struct osmo_rnc_id rnc_id;
|
||||
struct sgsn_sccp_user_iups *scu_iups;
|
||||
struct osmo_sccp_addr sccp_addr;
|
||||
struct osmo_fsm_inst *fi;
|
||||
|
||||
/* A list of struct iu_lac_rac_entry */
|
||||
struct llist_head lac_rac_list;
|
||||
};
|
||||
|
||||
struct ranap_iu_rnc *iu_rnc_find_or_create(const struct osmo_rnc_id *rnc_id,
|
||||
struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_sccp_addr *addr);
|
||||
|
||||
struct ranap_iu_rnc *iu_rnc_find_by_addr(const struct osmo_sccp_addr *rnc_sccp_addr);
|
||||
|
||||
void iu_rnc_update_rai_seen(struct ranap_iu_rnc *rnc, const struct osmo_routing_area_id *rai);
|
||||
|
||||
void iu_rnc_discard_all_ue_ctx(struct ranap_iu_rnc *rnc);
|
||||
|
||||
int iu_rnc_tx_paging_cmd(struct ranap_iu_rnc *rnc,
|
||||
const char *imsi,
|
||||
const uint32_t *tmsi,
|
||||
bool is_ps,
|
||||
uint32_t paging_cause);
|
||||
|
||||
#define LOG_RNC_CAT(IU_RNC, subsys, loglevel, fmt, args ...) \
|
||||
LOGPFSMSL((IU_RNC)->fi, subsys, loglevel, fmt, ## args)
|
||||
|
||||
#define LOG_RNC(IU_RNC, loglevel, fmt, args ...) \
|
||||
LOG_RNC_CAT(IU_RNC, DRANAP, loglevel, fmt, ## args)
|
||||
37
include/osmocom/sgsn/iu_rnc_fsm.h
Normal file
37
include/osmocom/sgsn/iu_rnc_fsm.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/fsm.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
|
||||
struct ranap_iu_rnc;
|
||||
|
||||
enum iu_rnc_state {
|
||||
IU_RNC_ST_WAIT_RX_RESET = 0,
|
||||
IU_RNC_ST_WAIT_RX_RESET_ACK,
|
||||
IU_RNC_ST_READY,
|
||||
IU_RNC_ST_DISCARDING,
|
||||
};
|
||||
|
||||
struct iu_rnc_ev_msg_up_co_initial_ctx {
|
||||
struct ranap_iu_rnc *rnc;
|
||||
uint32_t conn_id;
|
||||
ranap_message message;
|
||||
};
|
||||
|
||||
struct iu_rnc_ev_msg_up_co_ctx {
|
||||
struct ranap_ue_conn_ctx *ue_ctx;
|
||||
ranap_message message;
|
||||
};
|
||||
|
||||
enum iu_rnc_event {
|
||||
IU_RNC_EV_MSG_UP_CO_INITIAL, /* struct iu_rnc_ev_msg_up_co_initial_ctx* */
|
||||
IU_RNC_EV_MSG_UP_CO, /* struct iu_rnc_ev_msg_up_co_ctx* */
|
||||
IU_RNC_EV_RX_RESET, /* no param */
|
||||
IU_RNC_EV_RX_RESET_ACK, /* no param */
|
||||
IU_RNC_EV_MSG_DOWN_CL, /* struct msgb* */
|
||||
IU_RNC_EV_AVAILABLE,
|
||||
IU_RNC_EV_UNAVAILABLE
|
||||
};
|
||||
|
||||
extern struct osmo_fsm iu_rnc_fsm;
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <netinet/in.h>
|
||||
#include <inttypes.h>
|
||||
@@ -105,7 +107,7 @@ struct sgsn_mm_ctx {
|
||||
char imei[GSM23003_IMEISV_NUM_DIGITS+1];
|
||||
/* Opt: Software Version Numbber / TS 23.195 */
|
||||
char msisdn[GSM_EXTENSION_LENGTH];
|
||||
struct gprs_ra_id ra;
|
||||
struct osmo_routing_area_id ra;
|
||||
struct {
|
||||
uint16_t cell_id; /* Gb only */
|
||||
uint32_t cell_id_age; /* Gb only */
|
||||
@@ -231,7 +233,8 @@ static inline bool sgsn_mm_ctx_is_authenticated(struct sgsn_mm_ctx *ctx)
|
||||
|
||||
#ifdef BUILD_IU
|
||||
#define LOGIUP(ue, level, fmt, args...) \
|
||||
LOGP(DMM, level, "UE(0x%x){%s} " fmt, ue->conn_id, osmo_rai_name(&(ue)->ra_id), ## args)
|
||||
LOGP(DMM, level, "UE(0x%x){%s} " fmt, (ue) ? (ue)->conn_id : 0xffffffff, \
|
||||
(ue) ? osmo_rai_name(&(ue)->ra_id) : "", ## args)
|
||||
#else
|
||||
#define LOGIUP(ue, level, fmt, args...) \
|
||||
LOGP(DMM, level, "UE(%p){NOTSUPPORTED} " fmt, ue, ## args)
|
||||
@@ -253,18 +256,19 @@ static inline bool sgsn_mm_ctx_is_authenticated(struct sgsn_mm_ctx *ctx)
|
||||
|
||||
/* look-up a SGSN MM context based on TLLI + RAI */
|
||||
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
|
||||
const struct gprs_ra_id *raid);
|
||||
const struct osmo_routing_area_id *raid);
|
||||
struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t tmsi);
|
||||
struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi);
|
||||
struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx);
|
||||
struct sgsn_mm_ctx *sgsn_mm_ctx_by_llme(const struct gprs_llc_llme *llme);
|
||||
|
||||
/* look-up by matching TLLI and P-TMSI (think twice before using this) */
|
||||
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli,
|
||||
const struct gprs_ra_id *raid);
|
||||
const struct osmo_routing_area_id *raid);
|
||||
|
||||
/* Allocate a new SGSN MM context */
|
||||
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli,
|
||||
const struct gprs_ra_id *raid);
|
||||
const struct osmo_routing_area_id *raid);
|
||||
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx);
|
||||
|
||||
void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx);
|
||||
@@ -281,7 +285,17 @@ struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
|
||||
struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
|
||||
uint8_t tid);
|
||||
|
||||
bool sgsn_mm_ctx_is_r99(const struct sgsn_mm_ctx *mm);
|
||||
|
||||
uint32_t sgsn_alloc_ptmsi(void);
|
||||
|
||||
/* Called on subscriber data updates */
|
||||
void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx);
|
||||
|
||||
#ifdef BUILD_IU
|
||||
struct RANAP_Cause;
|
||||
void sgsn_mm_ctx_iu_activate_rabs(struct sgsn_mm_ctx *ctx);
|
||||
void sgsn_mm_ctx_iu_ranap_release_free(struct sgsn_mm_ctx *ctx,
|
||||
const struct RANAP_Cause *cause);
|
||||
void sgsn_mm_ctx_iu_ranap_free(struct sgsn_mm_ctx *ctx);
|
||||
#endif /* ifdef BUILD_IU */
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <netinet/in.h>
|
||||
#include <inttypes.h>
|
||||
@@ -68,6 +70,10 @@ struct sgsn_pdp_ctx {
|
||||
//uint32_t qos_profile_neg;
|
||||
uint8_t radio_prio;
|
||||
//uint32_t charging_id;
|
||||
bool ue_pdp_active; /* PDP Context is active for this NSAPI? */
|
||||
/* Keeps original SGSN local TEID when lib->teid_own is updated with
|
||||
* RNC's TEID upon use of Direct Tunnel feature: */
|
||||
uint32_t sgsn_teid_own;
|
||||
|
||||
struct osmo_timer_list timer;
|
||||
unsigned int T; /* Txxxx number */
|
||||
@@ -90,5 +96,10 @@ struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
|
||||
void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp);
|
||||
void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp);
|
||||
|
||||
#ifdef BUILD_IU
|
||||
int sgsn_pdp_ctx_iu_rab_activate(struct sgsn_pdp_ctx *pdp, uint8_t rab_id);
|
||||
int sgsn_pdp_ctx_iu_rab_deactivate(struct sgsn_pdp_ctx *pdp, uint8_t rab_id);
|
||||
#endif /* ifdef BUILD_IU */
|
||||
|
||||
char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len, bool return_ipv6);
|
||||
|
||||
|
||||
46
include/osmocom/sgsn/sccp.h
Normal file
46
include/osmocom/sgsn/sccp.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* SCCP Handling */
|
||||
/* (C) 2025 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
|
||||
struct sgsn_instance;
|
||||
struct ranap_ue_conn_ctx;
|
||||
|
||||
struct sgsn_sccp_user_iups {
|
||||
struct sgsn_instance *sgsn; /* backpointer */
|
||||
struct osmo_sccp_instance *sccp; /* backpointer */
|
||||
struct osmo_sccp_user *scu; /* IuPS */
|
||||
struct osmo_sccp_addr local_sccp_addr;
|
||||
struct llist_head ue_conn_ctx_list; /* list of "struct ranap_ue_conn_ctx" */
|
||||
struct llist_head ue_conn_sccp_addr_list; /* list of "struct iu_new_ctx_entry" */
|
||||
};
|
||||
|
||||
struct sgsn_sccp_user_iups *sgsn_scu_iups_inst_alloc(struct sgsn_instance *sgsn, struct osmo_sccp_instance *sccp);
|
||||
void sgsn_scu_iups_free(struct sgsn_sccp_user_iups *scu_iups);
|
||||
|
||||
int sgsn_scu_iups_tx_data_req(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id, struct msgb *ranap_msg);
|
||||
|
||||
struct ranap_ue_conn_ctx *sgsn_scu_iups_ue_conn_ctx_find(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id);
|
||||
|
||||
int sgsn_sccp_init(struct sgsn_instance *sgsn);
|
||||
void sgsn_sccp_release(struct sgsn_instance *sgsn);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#ifndef _SGSN_H
|
||||
#define _SGSN_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/core/stat_item.h>
|
||||
#include <osmocom/crypt/gprs_cipher.h>
|
||||
#include <osmocom/gprs/gprs_ns2.h>
|
||||
#include <osmocom/gprs/gprs_bssgp.h>
|
||||
@@ -14,19 +16,21 @@
|
||||
#include <osmocom/gsupclient/gsup_client.h>
|
||||
#include <osmocom/sgsn/common.h>
|
||||
|
||||
#include "../../config.h"
|
||||
|
||||
#if BUILD_IU
|
||||
#include <osmocom/ranap/iu_client.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sgsn/iu_client.h>
|
||||
#endif
|
||||
|
||||
#include <ares.h>
|
||||
#include <gtp.h>
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
|
||||
struct hostent;
|
||||
|
||||
#define SGSN_ERROR_CAUSE_NONE (-1)
|
||||
|
||||
/* This rac will be used internally. RAC with 0xff will be rejected */
|
||||
#define OSMO_RESERVED_RAC 0xff
|
||||
|
||||
enum sgsn_auth_policy {
|
||||
SGSN_AUTH_POLICY_OPEN,
|
||||
SGSN_AUTH_POLICY_CLOSED,
|
||||
@@ -152,20 +156,51 @@ struct sgsn_instance {
|
||||
ares_channel ares_channel;
|
||||
struct ares_addr_node *ares_servers;
|
||||
|
||||
/* Routing areas */
|
||||
struct sgsn_ra_global *routing_area;
|
||||
|
||||
struct rate_ctr_group *rate_ctrs;
|
||||
struct osmo_stat_item_group *statg;
|
||||
|
||||
struct llist_head apn_list; /* list of struct sgsn_apn_ctx */
|
||||
struct llist_head ggsn_list; /* list of struct sgsn_ggsn_ctx */
|
||||
struct llist_head mme_list; /* list of struct sgsn_mme_ctx */
|
||||
struct llist_head mm_list; /* list of struct sgsn_mm_ctx */
|
||||
struct llist_head pdp_list; /* list of struct sgsn_pdp_ctx */
|
||||
#if BUILD_IU
|
||||
struct llist_head rnc_list; /* list of struct ranap_iu_rnc */
|
||||
#endif /* if BUILD_IU */
|
||||
|
||||
struct ctrl_handle *ctrlh;
|
||||
|
||||
#if BUILD_IU
|
||||
/* SCCP (Iu) */
|
||||
struct {
|
||||
struct osmo_sccp_instance *sccp;
|
||||
struct sgsn_sccp_user_iups *scu_iups;
|
||||
} sccp;
|
||||
#endif /* if BUILD_IU */
|
||||
};
|
||||
|
||||
extern struct osmo_tdef sgsn_T_defs[];
|
||||
extern struct sgsn_instance *sgsn;
|
||||
extern void *tall_sgsn_ctx;
|
||||
|
||||
enum {
|
||||
SGSN_STAT_IU_PEERS_TOTAL,
|
||||
SGSN_STAT_IU_PEERS_ACTIVE,
|
||||
};
|
||||
static inline void sgsn_stat_inc(unsigned int idx, int32_t value)
|
||||
{
|
||||
osmo_stat_item_inc(osmo_stat_item_group_get_item(sgsn->statg, idx), value);
|
||||
}
|
||||
|
||||
static inline void sgsn_stat_dec(unsigned int idx, int32_t value)
|
||||
{
|
||||
osmo_stat_item_dec(osmo_stat_item_group_get_item(sgsn->statg, idx), value);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ctrl interface related work (sgsn_ctrl.c)
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
|
||||
app_configs = {
|
||||
"sgsn": ["doc/examples/osmo-sgsn/osmo-sgsn.cfg"],
|
||||
"sgsn": ["tests/osmo-sgsn.cfg"],
|
||||
"gtphub": ["doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg"]
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ apps = [(4245, "src/sgsn/osmo-sgsn", "OsmoSGSN", "sgsn"),
|
||||
(4253, "src/gtphub/osmo-gtphub", "OsmoGTPhub", "gtphub")
|
||||
]
|
||||
|
||||
vty_command = ["./src/sgsn/osmo-sgsn", "-c",
|
||||
"doc/examples/osmo-sgsn/osmo-sgsn.cfg"]
|
||||
vty_command = ["./src/sgsn/osmo-sgsn", "-c", "tests/osmo-sgsn.cfg"]
|
||||
|
||||
vty_app = apps[0]
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <gtp.h>
|
||||
#include <gtpie.h>
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
#include <osmocom/gtp/gtpie.h>
|
||||
|
||||
#include <osmocom/gtphub/gtphub.h>
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
|
||||
@@ -45,8 +45,10 @@ osmo_sgsn_SOURCES = \
|
||||
gprs_gmm_attach.c \
|
||||
gprs_gmm.c \
|
||||
gprs_gmm_fsm.c \
|
||||
gprs_gmm_util.c \
|
||||
gprs_mm_state_gb_fsm.c \
|
||||
gprs_ns.c \
|
||||
gprs_routing_area.c \
|
||||
gprs_sm.c \
|
||||
gprs_sndcp.c \
|
||||
gprs_sndcp_comp.c \
|
||||
@@ -79,7 +81,6 @@ osmo_sgsn_LDADD = \
|
||||
$(top_builddir)/src/gprs/gprs_utils.o \
|
||||
$(top_builddir)/src/gprs/sgsn_ares.o \
|
||||
$(OSMO_LIBS) \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOGSUPCLIENT_LIBS) \
|
||||
$(LIBCARES_LIBS) \
|
||||
$(LIBGTP_LIBS) \
|
||||
@@ -95,6 +96,11 @@ osmo_sgsn_LDADD += \
|
||||
|
||||
osmo_sgsn_SOURCES += \
|
||||
gprs_mm_state_iu_fsm.c \
|
||||
gprs_ranap.c
|
||||
gprs_ranap.c \
|
||||
iu_client.c \
|
||||
iu_rnc.c \
|
||||
iu_rnc_fsm.c \
|
||||
sccp.c \
|
||||
$(NULL)
|
||||
|
||||
endif
|
||||
|
||||
@@ -20,21 +20,49 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/prim.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
|
||||
#include <osmocom/gprs/gprs_bssgp.h>
|
||||
#include <osmocom/gprs/gprs_ns2.h>
|
||||
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
|
||||
#include <osmocom/sgsn/gprs_llc.h>
|
||||
#include <osmocom/sgsn/gprs_gmm.h>
|
||||
#include <osmocom/sgsn/gprs_routing_area.h>
|
||||
#include <osmocom/sgsn/sgsn_rim.h>
|
||||
#include <osmocom/sgsn/mmctx.h>
|
||||
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
|
||||
static int bssgp_nm_bvc_reset_ind(struct osmo_bssgp_prim *bp)
|
||||
{
|
||||
struct osmo_cell_global_id_ps cgi_ps = {};
|
||||
|
||||
/* Signalling: BVCI == 0, no CGI-PS
|
||||
* PtP BVCI: BVCI != 0, CGI-PS */
|
||||
|
||||
if (bp->bvci == 0) {
|
||||
sgsn_ra_geran_bvc_sign_reset_ind(bp->nsei);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!bp->tp)
|
||||
return -EINVAL;
|
||||
|
||||
if (!TLVP_PRES_LEN(bp->tp, BSSGP_IE_CELL_ID, 8))
|
||||
return -EINVAL;
|
||||
|
||||
bssgp_parse_cell_id2(&cgi_ps.rai, &cgi_ps.cell_identity, TLVP_VAL(bp->tp, BSSGP_IE_CELL_ID), 8);
|
||||
return sgsn_ra_geran_bvc_cell_reset_ind(bp->nsei, bp->bvci, &cgi_ps);
|
||||
}
|
||||
|
||||
/* call-back function for the BSSGP protocol */
|
||||
int sgsn_bssgp_rx_prim(struct osmo_prim_hdr *oph)
|
||||
{
|
||||
struct osmo_bssgp_prim *bp;
|
||||
struct osmo_routing_area_id rai = {};
|
||||
bp = container_of(oph, struct osmo_bssgp_prim, oph);
|
||||
|
||||
switch (oph->sap) {
|
||||
@@ -45,15 +73,28 @@ int sgsn_bssgp_rx_prim(struct osmo_prim_hdr *oph)
|
||||
}
|
||||
break;
|
||||
case SAP_BSSGP_GMM:
|
||||
gprs_rai_to_osmo(&rai, bp->ra_id);
|
||||
switch (oph->primitive) {
|
||||
case PRIM_BSSGP_GMM_SUSPEND:
|
||||
return gprs_gmm_rx_suspend(bp->ra_id, bp->tlli);
|
||||
return gprs_gmm_rx_suspend(&rai, bp->tlli);
|
||||
case PRIM_BSSGP_GMM_RESUME:
|
||||
return gprs_gmm_rx_resume(bp->ra_id, bp->tlli,
|
||||
return gprs_gmm_rx_resume(&rai, bp->tlli,
|
||||
bp->u.resume.suspend_ref);
|
||||
}
|
||||
break;
|
||||
case SAP_BSSGP_NM:
|
||||
switch (oph->primitive) {
|
||||
case PRIM_NM_BVC_RESET:
|
||||
if (oph->operation == PRIM_OP_INDICATION)
|
||||
bssgp_nm_bvc_reset_ind(bp);
|
||||
break;
|
||||
case PRIM_NM_BVC_BLOCK:
|
||||
case PRIM_NM_BVC_UNBLOCK:
|
||||
case PRIM_NM_STATUS:
|
||||
case PRIM_NM_LLC_DISCARDED:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case SAP_BSSGP_RIM:
|
||||
return sgsn_rim_rx_from_gb(bp, oph->msg);
|
||||
@@ -61,26 +102,20 @@ int sgsn_bssgp_rx_prim(struct osmo_prim_hdr *oph)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sgsn_bssgp_page_ps_ra(struct sgsn_mm_ctx *mmctx)
|
||||
int sgsn_bssgp_page_ps_bvci(struct sgsn_mm_ctx *mmctx, uint16_t nsei, uint16_t bvci)
|
||||
{
|
||||
struct bssgp_paging_info pinfo;
|
||||
int rc;
|
||||
|
||||
/* FIXME: page whole routing area, not only the last known cell */
|
||||
|
||||
/* initiate PS PAGING procedure */
|
||||
memset(&pinfo, 0, sizeof(pinfo));
|
||||
pinfo.mode = BSSGP_PAGING_PS;
|
||||
pinfo.scope = BSSGP_PAGING_BVCI;
|
||||
pinfo.bvci = mmctx->gb.bvci;
|
||||
pinfo.bvci = bvci;
|
||||
pinfo.imsi = mmctx->imsi;
|
||||
pinfo.ptmsi = &mmctx->p_tmsi;
|
||||
pinfo.drx_params = mmctx->drx_parms;
|
||||
pinfo.qos[0] = 0; // FIXME
|
||||
rc = bssgp_tx_paging(mmctx->gb.nsei, 0, &pinfo);
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PAGING_PS));
|
||||
|
||||
return rc;
|
||||
return bssgp_tx_paging(nsei, 0, &pinfo);
|
||||
}
|
||||
|
||||
/* called by the bssgp layer to send NS PDUs */
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
#include <osmocom/crypt/utran_cipher.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
||||
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
#include <osmocom/gprs/gprs_bssgp.h>
|
||||
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
@@ -63,30 +65,10 @@
|
||||
#include <osmocom/sgsn/gprs_sm.h>
|
||||
#include <osmocom/sgsn/gtp.h>
|
||||
#include <osmocom/sgsn/pdpctx.h>
|
||||
|
||||
#include <pdp.h>
|
||||
#include <osmocom/sgsn/gprs_gmm_util.h>
|
||||
|
||||
#define PTMSI_ALLOC
|
||||
|
||||
static const struct tlv_definition gsm48_gmm_att_tlvdef = {
|
||||
.def = {
|
||||
[GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
[GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 },
|
||||
[GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 },
|
||||
[GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 },
|
||||
[GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 },
|
||||
[GSM48_IE_GMM_AUTH_RES_EXT] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_AUTH_FAIL_PAR] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_RX_NPDU_NUM_LIST] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 },
|
||||
[GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 },
|
||||
},
|
||||
};
|
||||
|
||||
/* Our implementation, should be kept in SGSN */
|
||||
|
||||
@@ -111,9 +93,14 @@ static void mmctx_timer_start(struct sgsn_mm_ctx *mm, unsigned int T)
|
||||
|
||||
static void mmctx_timer_stop(struct sgsn_mm_ctx *mm, unsigned int T)
|
||||
{
|
||||
if (mm->T != T)
|
||||
if (!osmo_timer_pending(&mm->timer)) {
|
||||
LOGMMCTXP(LOGL_NOTICE, mm, "Stopping *inactive* MM timer %u\n", T);
|
||||
return;
|
||||
}
|
||||
if (mm->T != T) {
|
||||
LOGMMCTXP(LOGL_ERROR, mm, "Stopping MM timer %u but "
|
||||
"%u is running\n", T, mm->T);
|
||||
}
|
||||
osmo_timer_del(&mm->timer);
|
||||
}
|
||||
|
||||
@@ -136,13 +123,13 @@ int gsm48_gmm_sendmsg(struct msgb *msg, int command,
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(mm->ctrg, GMM_CTR_PKTS_SIG_OUT));
|
||||
#ifdef BUILD_IU
|
||||
if (mm->ran_type == MM_CTX_T_UTRAN_Iu)
|
||||
return ranap_iu_tx(msg, GPRS_SAPI_GMM);
|
||||
return sgsn_ranap_iu_tx(msg, GPRS_SAPI_GMM);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef BUILD_IU
|
||||
if (MSG_IU_UE_CTX(msg))
|
||||
return ranap_iu_tx(msg, GPRS_SAPI_GMM);
|
||||
return sgsn_ranap_iu_tx(msg, GPRS_SAPI_GMM);
|
||||
#endif
|
||||
|
||||
/* caller needs to provide TLLI, BVCI and NSEI */
|
||||
@@ -209,6 +196,61 @@ static void mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx, const char *log_text)
|
||||
sgsn_mm_ctx_cleanup_free(ctx);
|
||||
}
|
||||
|
||||
|
||||
/* 3GPP TS 24.008 § 10.5.7.1 Process PDP context status value, bit 0 corresponds to nsapi 0 */
|
||||
static void process_ms_ctx_status(struct sgsn_mm_ctx *mmctx,
|
||||
uint16_t pdp_status)
|
||||
{
|
||||
struct sgsn_pdp_ctx *pdp, *pdp2;
|
||||
/* 24.008 4.7.5.1.3: If the PDP context status information element is
|
||||
* included in ROUTING AREA UPDATE REQUEST message, then the network
|
||||
* shall deactivate all those PDP contexts locally (without peer to
|
||||
* peer signalling between the MS and the network), which are not in SM
|
||||
* state PDP-INACTIVE on network side but are indicated by the MS as
|
||||
* being in state PDP-INACTIVE. */
|
||||
|
||||
/* NSAPI 0 - 4 are spare, ignore these */
|
||||
pdp_status &= 0xffe0;
|
||||
|
||||
llist_for_each_entry_safe(pdp, pdp2, &mmctx->pdp_list, list) {
|
||||
bool active = (pdp_status & (1 << pdp->nsapi));
|
||||
if (active)
|
||||
continue;
|
||||
|
||||
LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u "
|
||||
"due to PDP CTX STATUS IE=0x%02x\n",
|
||||
pdp->nsapi, pdp_status);
|
||||
pdp->ue_pdp_active = false;
|
||||
if (pdp->ggsn)
|
||||
sgsn_delete_pdp_ctx(pdp);
|
||||
else /* GTP side already detached, freeing */
|
||||
sgsn_pdp_ctx_free(pdp);
|
||||
}
|
||||
}
|
||||
|
||||
/* 3GPP TS 24.008 § 10.5.7.1 Encode PDP context status value, bit 0 correspond to nsapi 0 */
|
||||
uint16_t encode_ms_ctx_status(struct sgsn_mm_ctx *mmctx)
|
||||
{
|
||||
struct sgsn_pdp_ctx *pdp;
|
||||
|
||||
uint16_t pdp_status = 0;
|
||||
|
||||
llist_for_each_entry(pdp, &mmctx->pdp_list, list) {
|
||||
if (pdp->ue_pdp_active)
|
||||
pdp_status |= (1 << pdp->nsapi);
|
||||
}
|
||||
|
||||
return pdp_status;
|
||||
}
|
||||
|
||||
/* 3GPP TS 24.008 § 4.7.13.4/10.5.7.1 Service request procedure not accepted by the
|
||||
* network. Returns true if MS has active PDP contexts in pdp_status */
|
||||
bool pdp_status_has_active_nsapis(uint16_t pdp_status)
|
||||
{
|
||||
/* NSAPI 0 - 4 are spare and should be ignored 0 */
|
||||
return (pdp_status >> 5) != 0;
|
||||
}
|
||||
|
||||
/* Chapter 9.4.18 */
|
||||
static int _tx_status(struct msgb *msg, uint8_t cause,
|
||||
struct sgsn_mm_ctx *mmctx)
|
||||
@@ -307,7 +349,7 @@ int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm)
|
||||
t = osmo_tdef_get(sgsn->cfg.T_defs, 3312, OSMO_TDEF_S, -1);
|
||||
aa->ra_upd_timer = gprs_secs_to_tmr_floor(t);
|
||||
aa->radio_prio = 0x44; /* lowest */
|
||||
gsm48_encode_ra(&aa->ra_id, &mm->ra);
|
||||
osmo_routing_area_id_encode_buf((uint8_t *) &aa->ra_id, sizeof(struct gsm48_ra_id), &mm->ra);
|
||||
|
||||
#if 0
|
||||
/* Optional: P-TMSI signature */
|
||||
@@ -437,16 +479,6 @@ int gsm48_tx_gmm_id_req(struct sgsn_mm_ctx *mm, uint8_t id_type)
|
||||
return gsm48_gmm_sendmsg(msg, 1, mm, false);
|
||||
}
|
||||
|
||||
/* determine if the MS/UE supports R99 or later */
|
||||
static bool mmctx_is_r99(const struct sgsn_mm_ctx *mm)
|
||||
{
|
||||
if (mm->ms_network_capa.len < 1)
|
||||
return false;
|
||||
if (mm->ms_network_capa.buf[0] & 0x01)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static enum gprs_ciph_algo gprs_ms_net_select_best_gea(uint8_t net_mask, uint8_t ms_mask) {
|
||||
uint8_t common_mask = net_mask & ms_mask;
|
||||
uint8_t r = 0;
|
||||
@@ -472,8 +504,8 @@ int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm,
|
||||
LOGMMCTXP(LOGL_INFO, mm, "<- GMM AUTH AND CIPHERING REQ (rand = %s,"
|
||||
" mmctx_is_r99=%d, vec->auth_types=0x%x",
|
||||
osmo_hexdump(vec->rand, sizeof(vec->rand)),
|
||||
mmctx_is_r99(mm), vec->auth_types);
|
||||
if (mmctx_is_r99(mm) && vec
|
||||
sgsn_mm_ctx_is_r99(mm), vec->auth_types);
|
||||
if (sgsn_mm_ctx_is_r99(mm) && vec
|
||||
&& (vec->auth_types & OSMO_AUTH_TYPE_UMTS)) {
|
||||
LOGPC(DMM, LOGL_INFO, ", autn = %s)\n",
|
||||
osmo_hexdump(vec->autn, sizeof(vec->autn)));
|
||||
@@ -516,7 +548,7 @@ int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm,
|
||||
* the optional AUTN IE. If a classic GSM SIM is
|
||||
* inserted, it will simply ignore AUTN and just use
|
||||
* RAND */
|
||||
if (mmctx_is_r99(mm) &&
|
||||
if (sgsn_mm_ctx_is_r99(mm) &&
|
||||
(vec->auth_types & OSMO_AUTH_TYPE_UMTS)) {
|
||||
msgb_tlv_put(msg, GSM48_IE_GMM_AUTN,
|
||||
sizeof(vec->autn), vec->autn);
|
||||
@@ -560,7 +592,7 @@ static enum osmo_sub_auth_type check_auth_resp(struct sgsn_mm_ctx *ctx,
|
||||
* However, on GERAN, even if we sent a UMTS AKA Authentication Request, the MS may decide to
|
||||
* instead reply with a GSM AKA SRES response. */
|
||||
if (is_utran
|
||||
|| (mmctx_is_r99(ctx) && (vec->auth_types & OSMO_AUTH_TYPE_UMTS)
|
||||
|| (sgsn_mm_ctx_is_r99(ctx) && (vec->auth_types & OSMO_AUTH_TYPE_UMTS)
|
||||
&& (res_len > sizeof(vec->sres)))) {
|
||||
expect_type = OSMO_AUTH_TYPE_UMTS;
|
||||
expect_str = "UMTS RES";
|
||||
@@ -605,6 +637,7 @@ static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx,
|
||||
{
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
|
||||
struct gsm48_auth_ciph_resp *acr = (struct gsm48_auth_ciph_resp *)gh->data;
|
||||
struct osmo_mobile_identity mi;
|
||||
struct tlv_parsed tp;
|
||||
struct gsm_auth_tuple *at;
|
||||
const char *res_name = "(no response)";
|
||||
@@ -630,7 +663,7 @@ static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx,
|
||||
/* Stop T3360 */
|
||||
mmctx_timer_stop(ctx, 3360);
|
||||
|
||||
tlv_parse(&tp, &gsm48_gmm_att_tlvdef, acr->data,
|
||||
tlv_parse(&tp, &gsm48_gmm_ie_tlvdef, acr->data,
|
||||
(msg->data + msg->len) - acr->data, 0, 0);
|
||||
|
||||
if (!TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_SRES) ||
|
||||
@@ -641,6 +674,28 @@ static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* IMEI SV parsing */
|
||||
if (TLVP_LEN(&tp, GSM48_IE_GMM_IMEISV) != 9) {
|
||||
LOGMMCTXP(LOGL_ERROR, ctx, "-> GMM AUTH AND CIPH RESPONSE: Invalid IMEISV\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* FIXME: should we allow invalid IMEI SV? Are there phones which have broken SV in hex? */
|
||||
if (osmo_mobile_identity_decode(&mi,
|
||||
TLVP_VAL(&tp, GSM48_IE_GMM_IMEISV),
|
||||
TLVP_LEN(&tp, GSM48_IE_GMM_IMEISV),
|
||||
false)) {
|
||||
LOGMMCTXP(LOGL_ERROR, ctx, "-> GMM AUTH AND CIPH RESPONSE: Cannot decode IMEISV\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (mi.type != GSM_MI_TYPE_IMEISV) {
|
||||
LOGMMCTXP(LOGL_ERROR, ctx, "-> GMM AUTH AND CIPH RESPONSE: Invalid MI type found in IMEISV %d\n", mi.type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memcpy(&ctx->imei, mi.imeisv, ARRAY_SIZE(ctx->imei));
|
||||
|
||||
/* Start with the good old 4-byte SRES */
|
||||
memcpy(res, TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_SRES), 4);
|
||||
res_len = 4;
|
||||
@@ -689,7 +744,7 @@ static int gsm48_rx_gmm_auth_ciph_fail(struct sgsn_mm_ctx *ctx,
|
||||
LOGMMCTXP(LOGL_INFO, ctx, "-> GMM AUTH AND CIPH FAILURE (cause = %s)\n",
|
||||
get_value_string(gsm48_gmm_cause_names, gmm_cause));
|
||||
|
||||
tlv_parse(&tp, &gsm48_gmm_att_tlvdef, gh->data+1, msg->len - 1, 0, 0);
|
||||
tlv_parse(&tp, &gsm48_gmm_ie_tlvdef, gh->data+1, msg->len - 1, 0, 0);
|
||||
|
||||
/* Only if GMM cause is present and the AUTS is provided, we can
|
||||
* start re-sync procedure */
|
||||
@@ -924,7 +979,7 @@ int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx)
|
||||
send_ck ? "sending" : "not sending", sgsn->cfg.uea_encryption_mask);
|
||||
/* FIXME: we should send the set of allowed UEA, as in ranap_new_msg_sec_mod_cmd2(). However, this
|
||||
* is not possible in the iu_client API. See OS#5487. */
|
||||
rc = ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, send_ck, ctx->iu.new_key);
|
||||
rc = sgsn_ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, send_ck, ctx->iu.new_key);
|
||||
ctx->iu.new_key = 0;
|
||||
return rc;
|
||||
}
|
||||
@@ -959,7 +1014,7 @@ int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx)
|
||||
rc = gsm48_tx_gmm_service_ack(ctx);
|
||||
|
||||
if (ctx->iu.service.type != GPRS_SERVICE_T_SIGNALLING)
|
||||
activate_pdp_rabs(ctx);
|
||||
sgsn_mm_ctx_iu_activate_rabs(ctx);
|
||||
|
||||
return rc;
|
||||
#endif
|
||||
@@ -1169,6 +1224,10 @@ static void mmctx_handle_rat_change(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
|
||||
static uint8_t gprs_ms_net_cap_gea_mask(const uint8_t *ms_net_cap, uint8_t cap_len)
|
||||
{
|
||||
uint8_t mask = (1 << GPRS_ALGO_GEA0);
|
||||
|
||||
if (cap_len == 0)
|
||||
return mask;
|
||||
|
||||
mask |= (0x80 & ms_net_cap[0]) ? (1 << GPRS_ALGO_GEA1) : 0;
|
||||
|
||||
if (cap_len < 2)
|
||||
@@ -1190,7 +1249,7 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
|
||||
uint8_t msnc_len, att_type, mi_len, ms_ra_acc_cap_len;
|
||||
uint16_t drx_par;
|
||||
char mi_log_string[32];
|
||||
struct gprs_ra_id ra_id;
|
||||
struct osmo_routing_area_id rai;
|
||||
uint16_t cid = 0;
|
||||
enum gsm48_gmm_cause reject_cause;
|
||||
struct osmo_mobile_identity mi;
|
||||
@@ -1205,10 +1264,10 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
|
||||
|
||||
if (!MSG_IU_UE_CTX(msg)) {
|
||||
/* Gb mode */
|
||||
cid = bssgp_parse_cell_id(&ra_id, msgb_bcid(msg));
|
||||
bssgp_parse_cell_id2(&rai, &cid, msgb_bcid(msg), 8);
|
||||
} else {
|
||||
#ifdef BUILD_IU
|
||||
ra_id = MSG_IU_UE_CTX(msg)->ra_id;
|
||||
gprs_rai_to_osmo(&rai, &MSG_IU_UE_CTX(msg)->ra_id);
|
||||
#else
|
||||
LOGMMCTXP(LOGL_ERROR, ctx, "Cannot handle Iu Attach Request, built without Iu support\n");
|
||||
return -ENOTSUP;
|
||||
@@ -1271,7 +1330,7 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
|
||||
if (MSG_IU_UE_CTX(msg))
|
||||
ctx = sgsn_mm_ctx_alloc_iu(MSG_IU_UE_CTX(msg));
|
||||
else
|
||||
ctx = sgsn_mm_ctx_alloc_gb(0, &ra_id);
|
||||
ctx = sgsn_mm_ctx_alloc_gb(0, &rai);
|
||||
if (!ctx) {
|
||||
reject_cause = GMM_CAUSE_NET_FAIL;
|
||||
goto rejected;
|
||||
@@ -1289,7 +1348,7 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
|
||||
if (MSG_IU_UE_CTX(msg))
|
||||
ctx = sgsn_mm_ctx_alloc_iu(MSG_IU_UE_CTX(msg));
|
||||
else
|
||||
ctx = sgsn_mm_ctx_alloc_gb(msgb_tlli(msg), &ra_id);
|
||||
ctx = sgsn_mm_ctx_alloc_gb(msgb_tlli(msg), &rai);
|
||||
if (!ctx) {
|
||||
reject_cause = GMM_CAUSE_NET_FAIL;
|
||||
goto rejected;
|
||||
@@ -1313,7 +1372,7 @@ static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg,
|
||||
}
|
||||
msgid2mmctx(ctx, msg);
|
||||
/* Update MM Context with currient RA and Cell ID */
|
||||
ctx->ra = ra_id;
|
||||
ctx->ra = rai;
|
||||
if (ctx->ran_type == MM_CTX_T_GERAN_Gb)
|
||||
ctx->gb.cell_id = cid;
|
||||
|
||||
@@ -1391,11 +1450,11 @@ static int gsm48_rx_gmm_att_compl(struct sgsn_mm_ctx *mmctx)
|
||||
/* only in case SGSN offered new P-TMSI */
|
||||
LOGMMCTXP(LOGL_INFO, mmctx, "-> GMM ATTACH COMPLETE\n");
|
||||
|
||||
#ifdef BUILD_IU
|
||||
#ifdef BUILD_IU
|
||||
if (mmctx->iu.ue_ctx) {
|
||||
ranap_iu_tx_release(mmctx->iu.ue_ctx, NULL);
|
||||
sgsn_ranap_iu_tx_release(mmctx->iu.ue_ctx, NULL);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
mmctx_timer_stop(mmctx, 3350);
|
||||
mmctx->t3350_mode = GMM_T3350_MODE_NONE;
|
||||
@@ -1424,25 +1483,18 @@ static int gsm48_rx_gmm_att_compl(struct sgsn_mm_ctx *mmctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Checks if two attach request contain the IEs and IE values
|
||||
/* Checks if two GMM are the same (required diffing Attach Requests/RAU Requests
|
||||
* return 0 if equal
|
||||
* return -1 if error
|
||||
* return 1 if unequal
|
||||
*
|
||||
* Only do a simple memcmp for now.
|
||||
*/
|
||||
int gprs_gmm_attach_req_ies(struct msgb *a, struct msgb *b)
|
||||
int gprs_gmm_msg_cmp(struct msgb *a, struct msgb *b)
|
||||
{
|
||||
struct gsm48_hdr *gh_a = (struct gsm48_hdr *) msgb_gmmh(a);
|
||||
struct gsm48_hdr *gh_b = (struct gsm48_hdr *) msgb_gmmh(b);
|
||||
|
||||
#define GMM_ATTACH_REQ_LEN 26
|
||||
if (msgb_l3len(a) != msgb_l3len(b))
|
||||
return 2;
|
||||
|
||||
/* there is the LLC FCS behind */
|
||||
if (msgb_l3len(a) < GMM_ATTACH_REQ_LEN || msgb_l3len(b) < GMM_ATTACH_REQ_LEN)
|
||||
return -1;
|
||||
|
||||
return !!memcmp(gh_a, gh_b, GMM_ATTACH_REQ_LEN);
|
||||
return memcmp(gh_a, gh_b, msgb_l3len(a));
|
||||
}
|
||||
|
||||
/* 3GPP TS 24.008 § 4.7.4.1 / 9.4.5.2 MO Detach request */
|
||||
@@ -1507,10 +1559,12 @@ static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm)
|
||||
rua = (struct gsm48_ra_upd_ack *) msgb_put(msg, sizeof(*rua));
|
||||
rua->force_stby = 0; /* not indicated */
|
||||
rua->upd_result = 0; /* RA updated */
|
||||
|
||||
/* Periodic RA update timer */
|
||||
t = osmo_tdef_get(sgsn->cfg.T_defs, 3312, OSMO_TDEF_S, -1);
|
||||
rua->ra_upd_timer = gprs_secs_to_tmr_floor(t);
|
||||
|
||||
gsm48_encode_ra(&rua->ra_id, &mm->ra);
|
||||
osmo_routing_area_id_encode_buf((uint8_t *)&rua->ra_id, sizeof(struct gsm48_ra_id), &mm->ra);
|
||||
|
||||
#if 0
|
||||
/* Optional: P-TMSI signature */
|
||||
@@ -1535,12 +1589,19 @@ static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm)
|
||||
}
|
||||
*l = rc;
|
||||
#endif
|
||||
/* MS identity */
|
||||
/* List of Received N-PDU */
|
||||
|
||||
/* Optional: Negotiated READY timer value */
|
||||
t = osmo_tdef_get(sgsn->cfg.T_defs, 3314, OSMO_TDEF_S, -1);
|
||||
msgb_tv_put(msg, GSM48_IE_GMM_TIMER_READY, gprs_secs_to_tmr_floor(t));
|
||||
|
||||
/* Option: MS ID, ... */
|
||||
/* GMM cause */
|
||||
/* PDP Context Status */
|
||||
uint16_t pdp_ctx_status = encode_ms_ctx_status(mm);
|
||||
msgb_tlv_put(msg, GSM48_IE_GMM_PDP_CTX_STATUS, 2, (uint8_t *) &pdp_ctx_status);
|
||||
|
||||
/* MS ID, ... */
|
||||
return gsm48_gmm_sendmsg(msg, 0, mm, true);
|
||||
}
|
||||
|
||||
@@ -1565,47 +1626,6 @@ int gsm48_tx_gmm_ra_upd_rej(struct msgb *old_msg, uint8_t cause)
|
||||
return gsm48_gmm_sendmsg(msg, 0, NULL, false);
|
||||
}
|
||||
|
||||
static void process_ms_ctx_status(struct sgsn_mm_ctx *mmctx,
|
||||
const uint8_t *pdp_status)
|
||||
{
|
||||
struct sgsn_pdp_ctx *pdp, *pdp2;
|
||||
/* 24.008 4.7.5.1.3: If the PDP context status information element is
|
||||
* included in ROUTING AREA UPDATE REQUEST message, then the network
|
||||
* shall deactivate all those PDP contexts locally (without peer to
|
||||
* peer signalling between the MS and the network), which are not in SM
|
||||
* state PDP-INACTIVE on network side but are indicated by the MS as
|
||||
* being in state PDP-INACTIVE. */
|
||||
|
||||
llist_for_each_entry_safe(pdp, pdp2, &mmctx->pdp_list, list) {
|
||||
bool inactive = (pdp->nsapi < 8) ?
|
||||
!(pdp_status[0] & (1 << pdp->nsapi)) :
|
||||
!(pdp_status[1] & (1 << (pdp->nsapi - 8)));
|
||||
if (!inactive)
|
||||
continue;
|
||||
|
||||
LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u "
|
||||
"due to PDP CTX STATUS IE=0x%02x%02x\n",
|
||||
pdp->nsapi, pdp_status[1], pdp_status[0]);
|
||||
if (pdp->ggsn)
|
||||
sgsn_delete_pdp_ctx(pdp);
|
||||
else /* GTP side already detached, freeing */
|
||||
sgsn_pdp_ctx_free(pdp);
|
||||
}
|
||||
}
|
||||
|
||||
/* 3GPP TS 24.008 § 4.7.13.4 Service request procedure not accepted by the
|
||||
* network. Returns true if MS has active PDP contexts in pdp_status */
|
||||
bool pdp_status_has_active_nsapis(const uint8_t *pdp_status, const size_t pdp_status_len)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < pdp_status_len; i++)
|
||||
if (pdp_status[i] != 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Chapter 9.4.14: Routing area update request */
|
||||
static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
|
||||
struct gprs_llc_llme *llme)
|
||||
@@ -1613,52 +1633,30 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
|
||||
#ifndef PTMSI_ALLOC
|
||||
struct sgsn_signal_data sig_data;
|
||||
#endif
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
|
||||
uint8_t *cur = gh->data;
|
||||
uint8_t ms_ra_acc_cap_len;
|
||||
struct gprs_ra_id old_ra_id;
|
||||
struct tlv_parsed tp;
|
||||
uint8_t upd_type;
|
||||
enum gsm48_gmm_cause reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC;
|
||||
struct gprs_gmm_ra_upd_req req;
|
||||
int rc;
|
||||
|
||||
rc = gprs_gmm_parse_ra_upd_req(msg, &req);
|
||||
if (rc) {
|
||||
reject_cause = rc;
|
||||
goto rejected;
|
||||
}
|
||||
|
||||
/* TODO: In iu mode - handle follow-on request.
|
||||
* The follow-on request can be signaled in an Attach Request on IuPS.
|
||||
* This means the MS/UE asks to keep the PS connection open for further requests
|
||||
* after the Attach Request succeed.
|
||||
* The SGSN can decide if it close the connection or not. Both are spec conform. */
|
||||
|
||||
/* Update Type 10.5.5.18 */
|
||||
upd_type = *cur++ & 0x07;
|
||||
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(sgsn->rate_ctrs, CTR_GPRS_ROUTING_AREA_REQUEST));
|
||||
LOGMMCTXP(LOGL_INFO, mmctx, "-> GMM RA UPDATE REQUEST type=\"%s\"\n",
|
||||
get_value_string(gprs_upd_t_strs, upd_type));
|
||||
get_value_string(gprs_upd_t_strs, req.update_type));
|
||||
|
||||
/* Old routing area identification 10.5.5.15 */
|
||||
gsm48_parse_ra(&old_ra_id, cur);
|
||||
cur += 6;
|
||||
|
||||
/* MS Radio Access Capability 10.5.5.12a */
|
||||
ms_ra_acc_cap_len = *cur++;
|
||||
if (ms_ra_acc_cap_len > 52) {
|
||||
LOGMMCTXP(LOGL_ERROR, mmctx,
|
||||
"Rejecting GMM RA Update Request: MS Radio Access Capability too long"
|
||||
" (ms_ra_acc_cap_len = %u > 52)\n", ms_ra_acc_cap_len);
|
||||
reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC;
|
||||
goto rejected;
|
||||
}
|
||||
cur += ms_ra_acc_cap_len;
|
||||
|
||||
/* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status,
|
||||
* DRX parameter, MS network capability */
|
||||
tlv_parse(&tp, &gsm48_gmm_att_tlvdef, cur,
|
||||
(msg->data + msg->len) - cur, 0, 0);
|
||||
|
||||
switch (upd_type) {
|
||||
switch (req.update_type) {
|
||||
case GPRS_UPD_T_RA_LA:
|
||||
case GPRS_UPD_T_RA_LA_IMSI_ATT:
|
||||
LOGMMCTXP(LOGL_NOTICE, mmctx, "Update type %i unsupported in Mode III, is your SI13 corrupt?\n", upd_type);
|
||||
LOGMMCTXP(LOGL_NOTICE, mmctx, "Update type %d unsupported in Mode III, is your SI13 corrupt?\n", req.update_type);
|
||||
reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC;
|
||||
goto rejected;
|
||||
case GPRS_UPD_T_RA:
|
||||
@@ -1677,13 +1675,13 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
|
||||
/* Look-up the MM context based on old RA-ID and TLLI */
|
||||
if (!MSG_IU_UE_CTX(msg)) {
|
||||
/* Gb */
|
||||
mmctx = sgsn_mm_ctx_by_tlli_and_ptmsi(msgb_tlli(msg), &old_ra_id);
|
||||
} else if (TLVP_PRESENT(&tp, GSM48_IE_GMM_ALLOC_PTMSI)) {
|
||||
mmctx = sgsn_mm_ctx_by_tlli_and_ptmsi(msgb_tlli(msg), &req.old_rai);
|
||||
} else if (TLVP_PRESENT(&req.tlv, GSM48_IE_GMM_ALLOC_PTMSI)) {
|
||||
#ifdef BUILD_IU
|
||||
/* In Iu mode search only for ptmsi */
|
||||
struct osmo_mobile_identity mi;
|
||||
if (osmo_mobile_identity_decode(&mi, TLVP_VAL(&tp, GSM48_IE_GMM_ALLOC_PTMSI),
|
||||
TLVP_LEN(&tp, GSM48_IE_GMM_ALLOC_PTMSI), false)
|
||||
if (osmo_mobile_identity_decode(&mi, TLVP_VAL(&req.tlv, GSM48_IE_GMM_ALLOC_PTMSI),
|
||||
TLVP_LEN(&req.tlv, GSM48_IE_GMM_ALLOC_PTMSI), false)
|
||||
|| mi.type != GSM_MI_TYPE_TMSI) {
|
||||
LOGIUP(MSG_IU_UE_CTX(msg), LOGL_ERROR, "Cannot decode P-TMSI\n");
|
||||
goto rejected;
|
||||
@@ -1703,7 +1701,7 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
|
||||
msgb_tlli(msg),
|
||||
mmctx->p_tmsi, mmctx->p_tmsi_old,
|
||||
mmctx->gb.tlli, mmctx->gb.tlli_new,
|
||||
osmo_rai_name(&mmctx->ra));
|
||||
osmo_rai_name2(&mmctx->ra));
|
||||
/* A RAT change will trigger the common procedure
|
||||
* below after handling the RAT change. Protect it
|
||||
* here from being called twice */
|
||||
@@ -1711,26 +1709,26 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
|
||||
osmo_fsm_inst_dispatch(mmctx->gmm_fsm, E_GMM_COMMON_PROC_INIT_REQ, NULL);
|
||||
|
||||
}
|
||||
} else if (!gprs_ra_id_equals(&mmctx->ra, &old_ra_id) ||
|
||||
} else if (osmo_rai_cmp(&mmctx->ra, &req.old_rai) ||
|
||||
mmctx->gmm_fsm->state == ST_GMM_DEREGISTERED)
|
||||
{
|
||||
/* We've received either a RAU for a MS which isn't registered
|
||||
* or a RAU with an unknown RA ID. As long the SGSN doesn't support
|
||||
* PS handover we treat this as invalid RAU */
|
||||
struct gprs_ra_id new_ra_id;
|
||||
struct osmo_routing_area_id new_ra_id = {};
|
||||
char new_ra[32];
|
||||
|
||||
bssgp_parse_cell_id(&new_ra_id, msgb_bcid(msg));
|
||||
osmo_rai_name_buf(new_ra, sizeof(new_ra), &new_ra_id);
|
||||
bssgp_parse_cell_id2(&new_ra_id, NULL, msgb_bcid(msg), 8);
|
||||
osmo_rai_name2_buf(new_ra, sizeof(new_ra), &new_ra_id);
|
||||
|
||||
if (mmctx->gmm_fsm->state == ST_GMM_DEREGISTERED)
|
||||
LOGMMCTXP(LOGL_INFO, mmctx,
|
||||
"Rejecting RAU - GMM state is deregistered. Old RA: %s New RA: %s\n",
|
||||
osmo_rai_name(&old_ra_id), new_ra);
|
||||
osmo_rai_name2(&req.old_rai), new_ra);
|
||||
else
|
||||
LOGMMCTXP(LOGL_INFO, mmctx,
|
||||
"Rejecting RAU - Old RA doesn't match MM. Old RA: %s New RA: %s\n",
|
||||
osmo_rai_name(&old_ra_id), new_ra);
|
||||
osmo_rai_name2(&req.old_rai), new_ra);
|
||||
|
||||
reject_cause = GMM_CAUSE_IMPL_DETACHED;
|
||||
goto rejected;
|
||||
@@ -1742,6 +1740,21 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
|
||||
* in the MS */
|
||||
LOGGBP(llme, DMM, LOGL_NOTICE, "LLC XID RESET\n");
|
||||
gprs_llgmm_reset_oldmsg(msg, GPRS_SAPI_GMM, llme);
|
||||
|
||||
/* The RAU didn't come from expected TLLI+RAI, so it's for sure bad and should be rejected.
|
||||
* In any case, before unassigning (freeing) the LLME during the REJECT below, make sure
|
||||
* beforehand that if there's an mmctx relating to that llme it is also freed.
|
||||
* Otherwise it would be kept pointining at a dangling freed llme object.
|
||||
*/
|
||||
mmctx = sgsn_mm_ctx_by_llme(llme);
|
||||
if (mmctx) {
|
||||
char old_ra_id_name[32];
|
||||
osmo_rai_name2_buf(old_ra_id_name, sizeof(old_ra_id_name), &req.old_rai);
|
||||
LOGMMCTXP(LOGL_NOTICE, mmctx,
|
||||
"Rx RA Update Request with unexpected TLLI=%08x Old RA=%s (expected Old RA: %s)!\n",
|
||||
msgb_tlli(msg), old_ra_id_name, osmo_rai_name2(&mmctx->ra));
|
||||
/* mmctx will be released (and its llme un assigned) after REJECT below. */
|
||||
}
|
||||
}
|
||||
/* The MS has to perform GPRS attach */
|
||||
/* Device is still IMSI attached for CS but initiate GPRS ATTACH,
|
||||
@@ -1763,13 +1776,13 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
|
||||
|
||||
/* Update the MM context with the new RA-ID */
|
||||
if (mmctx->ran_type == MM_CTX_T_GERAN_Gb && msgb_bcid(msg)) {
|
||||
bssgp_parse_cell_id(&mmctx->ra, msgb_bcid(msg));
|
||||
bssgp_parse_cell_id2(&mmctx->ra, NULL, msgb_bcid(msg), 8);
|
||||
/* Update the MM context with the new (i.e. foreign) TLLI */
|
||||
mmctx->gb.tlli = msgb_tlli(msg);
|
||||
}
|
||||
/* Update the MM context with the new DRX params */
|
||||
if (TLVP_PRESENT(&tp, GSM48_IE_GMM_DRX_PARAM))
|
||||
memcpy(&mmctx->drx_parms, TLVP_VAL(&tp, GSM48_IE_GMM_DRX_PARAM), sizeof(mmctx->drx_parms));
|
||||
if (TLVP_PRESENT(&req.tlv, GSM48_IE_GMM_DRX_PARAM))
|
||||
memcpy(&mmctx->drx_parms, TLVP_VAL(&req.tlv, GSM48_IE_GMM_DRX_PARAM), sizeof(mmctx->drx_parms));
|
||||
|
||||
/* FIXME: Update the MM context with the MS radio acc capabilities */
|
||||
/* FIXME: Update the MM context with the MS network capabilities */
|
||||
@@ -1802,8 +1815,8 @@ static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg,
|
||||
|
||||
/* Look at PDP Context Status IE and see if MS's view of
|
||||
* activated/deactivated NSAPIs agrees with our view */
|
||||
if (TLVP_PRESENT(&tp, GSM48_IE_GMM_PDP_CTX_STATUS)) {
|
||||
const uint8_t *pdp_status = TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS);
|
||||
if (TLVP_PRESENT(&req.tlv, GSM48_IE_GMM_PDP_CTX_STATUS)) {
|
||||
uint16_t pdp_status = osmo_load16le(TLVP_VAL(&req.tlv, GSM48_IE_GMM_PDP_CTX_STATUS));
|
||||
process_ms_ctx_status(mmctx, pdp_status);
|
||||
}
|
||||
|
||||
@@ -1826,7 +1839,7 @@ rejected:
|
||||
#ifdef BUILD_IU
|
||||
else if (MSG_IU_UE_CTX(msg)) {
|
||||
unsigned long X1001 = osmo_tdef_get(sgsn->cfg.T_defs, -1001, OSMO_TDEF_S, -1);
|
||||
ranap_iu_tx_release_free(MSG_IU_UE_CTX(msg), NULL, (int) X1001);
|
||||
sgsn_ranap_iu_tx_release_free(MSG_IU_UE_CTX(msg), NULL, (int) X1001);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1923,7 +1936,7 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
|
||||
LOGPC(DMM, LOGL_INFO, "\n");
|
||||
|
||||
/* Optional: PDP context status, MBMS context status, Uplink data status, Device properties */
|
||||
tlv_parse(&tp, &gsm48_gmm_att_tlvdef, cur, (msg->data + msg->len) - cur, 0, 0);
|
||||
tlv_parse(&tp, &gsm48_gmm_ie_tlvdef, cur, (msg->data + msg->len) - cur, 0, 0);
|
||||
|
||||
switch (mi.type) {
|
||||
case GSM_MI_TYPE_IMSI:
|
||||
@@ -1962,8 +1975,8 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
|
||||
/* Look at PDP Context Status IE and see if MS's view of
|
||||
* activated/deactivated NSAPIs agrees with our view */
|
||||
if (TLVP_PRESENT(&tp, GSM48_IE_GMM_PDP_CTX_STATUS)) {
|
||||
const uint8_t *pdp_status = TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS);
|
||||
const size_t pdp_status_len = TLVP_LEN(&tp, GSM48_IE_GMM_PDP_CTX_STATUS);
|
||||
/* TODO: Use tlvp_val16le when available */
|
||||
uint16_t pdp_status = osmo_load16le(TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS));
|
||||
|
||||
process_ms_ctx_status(ctx, pdp_status);
|
||||
|
||||
@@ -1972,7 +1985,7 @@ static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg)
|
||||
* Active state in pdp_status but there is no PDP contexts on
|
||||
* SGSN side then Reject with the cause will force the mobile to
|
||||
* reset PDP contexts */
|
||||
if (llist_empty(&ctx->pdp_list) && pdp_status_has_active_nsapis(pdp_status, pdp_status_len)) {
|
||||
if (llist_empty(&ctx->pdp_list) && pdp_status_has_active_nsapis(pdp_status)) {
|
||||
reject_cause = GMM_CAUSE_NO_PDP_ACTIVATED;
|
||||
goto rejected;
|
||||
}
|
||||
@@ -2271,7 +2284,7 @@ int gsm0408_gprs_force_reattach(struct sgsn_mm_ctx *mmctx)
|
||||
return rc;
|
||||
}
|
||||
|
||||
int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli)
|
||||
int gprs_gmm_rx_suspend(struct osmo_routing_area_id *raid, uint32_t tlli)
|
||||
{
|
||||
struct sgsn_mm_ctx *mmctx;
|
||||
|
||||
@@ -2293,7 +2306,7 @@ int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli,
|
||||
int gprs_gmm_rx_resume(struct osmo_routing_area_id *raid, uint32_t tlli,
|
||||
uint8_t suspend_ref)
|
||||
{
|
||||
struct sgsn_mm_ctx *mmctx;
|
||||
@@ -2334,10 +2347,10 @@ int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme,
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
|
||||
uint8_t pdisc = gsm48_hdr_pdisc(gh);
|
||||
struct sgsn_mm_ctx *mmctx;
|
||||
struct gprs_ra_id ra_id;
|
||||
struct osmo_routing_area_id ra_id = {};
|
||||
int rc = -EINVAL;
|
||||
|
||||
bssgp_parse_cell_id(&ra_id, msgb_bcid(msg));
|
||||
bssgp_parse_cell_id2(&ra_id, NULL, msgb_bcid(msg), 8);
|
||||
mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id);
|
||||
if (mmctx) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PKTS_SIG_IN));
|
||||
@@ -2366,3 +2379,44 @@ int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme,
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef BUILD_IU
|
||||
/* Main entry point for incoming 04.08 GPRS messages from Iu */
|
||||
int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id,
|
||||
uint16_t *sai)
|
||||
{
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
|
||||
uint8_t pdisc = gsm48_hdr_pdisc(gh);
|
||||
struct sgsn_mm_ctx *mmctx;
|
||||
int rc = -EINVAL;
|
||||
|
||||
mmctx = sgsn_mm_ctx_by_ue_ctx(MSG_IU_UE_CTX(msg));
|
||||
if (mmctx) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PKTS_SIG_IN));
|
||||
if (ra_id)
|
||||
memcpy(&mmctx->ra, ra_id, sizeof(mmctx->ra));
|
||||
}
|
||||
|
||||
/* MMCTX can be NULL */
|
||||
|
||||
switch (pdisc) {
|
||||
case GSM48_PDISC_MM_GPRS:
|
||||
rc = gsm0408_rcv_gmm(mmctx, msg, NULL, false);
|
||||
#pragma message "set drop_cipherable arg for gsm0408_rcv_gmm() from IuPS?"
|
||||
break;
|
||||
case GSM48_PDISC_SM_GPRS:
|
||||
rc = gsm0408_rcv_gsm(mmctx, msg, NULL);
|
||||
break;
|
||||
default:
|
||||
LOGMMCTXP(LOGL_NOTICE, mmctx,
|
||||
"Unknown GSM 04.08 discriminator 0x%02x: %s\n",
|
||||
pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg)));
|
||||
/* FIXME: return status message */
|
||||
break;
|
||||
}
|
||||
|
||||
/* MMCTX can be invalid */
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* ifdef BUILD_IU */
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/crypt/utran_cipher.h>
|
||||
|
||||
@@ -7,6 +9,7 @@
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/gprs_gmm.h>
|
||||
#include <osmocom/sgsn/mmctx.h>
|
||||
#include <osmocom/sgsn/gprs_ranap.h>
|
||||
#include <osmocom/sgsn/sgsn.h>
|
||||
|
||||
#define X(s) (1 << (s))
|
||||
@@ -274,7 +277,7 @@ static void st_iu_security_cmd_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_
|
||||
|
||||
/* FIXME: we should send the set of allowed UEA, as in ranap_new_msg_sec_mod_cmd2(). However, this
|
||||
* is not possible in the iu_client API. See OS#5487. */
|
||||
ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, send_ck, ctx->iu.new_key);
|
||||
sgsn_ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, send_ck, ctx->iu.new_key);
|
||||
ctx->iu.new_key = 0;
|
||||
#endif
|
||||
}
|
||||
@@ -385,7 +388,7 @@ void gmm_attach_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *
|
||||
/* 04.08 4.7.3.1.6 d) Abnormal Case
|
||||
* Only do action if Req IEs differs. */
|
||||
if (ctx->gmm_att_req.attach_req &&
|
||||
gprs_gmm_attach_req_ies(new_attach_req, ctx->gmm_att_req.attach_req)) {
|
||||
gprs_gmm_msg_cmp(new_attach_req, ctx->gmm_att_req.attach_req)) {
|
||||
osmo_fsm_inst_state_chg(fi, ST_INIT, 0, 0);
|
||||
st_init(fi, event, data);
|
||||
}
|
||||
|
||||
273
src/sgsn/gprs_gmm_util.c
Normal file
273
src/sgsn/gprs_gmm_util.c
Normal file
@@ -0,0 +1,273 @@
|
||||
/*! \file gprs_bssgp_util.c
|
||||
* GPRS GMM protocol implementation as per 3GPP TS 24.008 */
|
||||
/*
|
||||
* (C) 2009-2015 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2024 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0+
|
||||
*
|
||||
* Author: Alexander Couzens <lynxis@fe80.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gprs/gprs_msgb.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/gprs_gmm_util.h>
|
||||
|
||||
const struct tlv_definition gsm48_gmm_ie_tlvdef = {
|
||||
.def = {
|
||||
[GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
[GSM48_IE_GMM_PTMSI_TYPE] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
[GSM48_IE_GMM_TMSI_BASED_NRI_C] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 },
|
||||
[GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 },
|
||||
[GSM48_IE_GMM_ADD_IDENTITY] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_RAI2] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 },
|
||||
[GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 },
|
||||
[GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_CAUSE] = { TLV_TYPE_TV, 1 },
|
||||
[GSM48_IE_GMM_RX_NPDU_NUM_LIST] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 },
|
||||
[GSM48_IE_GMM_AUTN] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_AUTH_RES_EXT] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_TIMER_T3302] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_AUTH_FAIL_PAR] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_TIMER_T3346] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_UE_NET_CAP] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_VD_PREF_UE_USAGE] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_NET_FEAT_SUPPORT] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
},
|
||||
};
|
||||
|
||||
/* Minimum length of a GMM Attach Request (only L3/GMM) */
|
||||
#define GSM48_GMM_ATT_REQ_MIN_LEN 25
|
||||
const struct tlv_definition gsm48_gmm_att_ie_tlvdef = {
|
||||
.def = {
|
||||
[GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 },
|
||||
[GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 },
|
||||
[GSM48_IE_GMM_TMSI_STATUS] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
[GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_MS_CLASSMARK2] = { TLV_TYPE_TLV, 3 },
|
||||
[GSM48_IE_GMM_MS_CLASSMARK3] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_SUPP_CODEC_LIST] = { TLV_TYPE_TLV, 3 },
|
||||
[GSM48_IE_GMM_UE_NET_CAP] = { TLV_TYPE_TLV, 2 },
|
||||
[GSM48_IE_GMM_ADD_IDENTITY] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_RAI2] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_VD_PREF_UE_USAGE] = { TLV_TYPE_TLV, 1 },
|
||||
[GSM48_IE_GMM_DEVICE_PROP] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
[GSM48_IE_GMM_PTMSI_TYPE] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
[GSM48_IE_GMM_MS_NET_FEAT_SUP] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
[GSM48_IE_GMM_ADD_UPDATE_TYPE] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
[GSM48_IE_GMM_TMSI_BASED_NRI_C] = { TLV_TYPE_TLV, 4 },
|
||||
[GMM48_IE_GMM_TIMER_T3324] = { TLV_TYPE_TLV, 3 },
|
||||
[GSM48_IE_GMM_TIMER_T3312_EXT] = { TLV_TYPE_TLV, 3 },
|
||||
[GSM48_IE_GMM_EXT_DRX_PARAMS] = { TLV_TYPE_TLV, 3 },
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const struct tlv_definition gsm48_gmm_rau_ie_tlvdef = {
|
||||
.def = {
|
||||
[GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 },
|
||||
[GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 },
|
||||
[GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 },
|
||||
[GSM48_IE_GMM_TMSI_STATUS] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
[GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 5 },
|
||||
[GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 2 },
|
||||
[GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 2 },
|
||||
[GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 1 },
|
||||
[GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_UE_NET_CAP] = { TLV_TYPE_TLV, 2 },
|
||||
[GSM48_IE_GMM_ADD_IDENTITY] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_RAI2] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_MS_CLASSMARK2] = { TLV_TYPE_TLV, 3 },
|
||||
[GSM48_IE_GMM_MS_CLASSMARK3] = { TLV_TYPE_TLV, 0 },
|
||||
[GSM48_IE_GMM_SUPP_CODEC_LIST] = { TLV_TYPE_TLV, 3 },
|
||||
[GSM48_IE_GMM_VD_PREF_UE_USAGE] = { TLV_TYPE_TLV, 1 },
|
||||
[GSM48_IE_GMM_PTMSI_TYPE] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
[GSM48_IE_GMM_DEVICE_PROP] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
[GSM48_IE_GMM_MS_NET_FEAT_SUP] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
[GSM48_IE_GMM_OLD_LAI] = { TLV_TYPE_TLV, 5 },
|
||||
[GSM48_IE_GMM_ADD_UPDATE_TYPE] = { TLV_TYPE_SINGLE_TV, 1 },
|
||||
[GSM48_IE_GMM_TMSI_BASED_NRI_C] = { TLV_TYPE_TLV, 4 },
|
||||
[GMM48_IE_GMM_TIMER_T3324] = { TLV_TYPE_TLV, 3 },
|
||||
[GSM48_IE_GMM_TIMER_T3312_EXT] = { TLV_TYPE_TLV, 3 },
|
||||
[GSM48_IE_GMM_EXT_DRX_PARAMS] = { TLV_TYPE_TLV, 3 },
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*! Parse 24.008 9.4.1 Attach Request
|
||||
* \param[in] msg l3 pointers must point to gmm.
|
||||
* \param[out] rau_req parsed RA update request
|
||||
* \returns 0 on success or GMM cause
|
||||
*/
|
||||
int gprs_gmm_parse_att_req(const struct msgb *msg, struct gprs_gmm_att_req *att_req)
|
||||
{
|
||||
uint8_t *cur, len;
|
||||
size_t mandatory_fields_len;
|
||||
struct gsm48_hdr *gh;
|
||||
int ret;
|
||||
|
||||
OSMO_ASSERT(msg);
|
||||
OSMO_ASSERT(att_req);
|
||||
|
||||
memset(att_req, 0, sizeof(struct gprs_gmm_att_req));
|
||||
/* all mandatory fields */
|
||||
if (msgb_l3len(msg) < GSM48_GMM_ATT_REQ_MIN_LEN)
|
||||
return GMM_CAUSE_PROTO_ERR_UNSPEC;
|
||||
|
||||
gh = (struct gsm48_hdr *) msgb_gmmh(msg);
|
||||
cur = gh->data;
|
||||
|
||||
att_req->skip_ind = gh->proto_discr >> 4;
|
||||
|
||||
/* LV: MS network cap 10.5.5.12 */
|
||||
len = *cur++;
|
||||
if (msgb_l3len(msg) < (len + 21 + (cur - msgb_gmmh(msg))))
|
||||
return GMM_CAUSE_PROTO_ERR_UNSPEC;
|
||||
/* MS network cap can't be empty */
|
||||
if (len == 0)
|
||||
return GMM_CAUSE_INV_MAND_INFO;
|
||||
|
||||
att_req->ms_network_cap = cur;
|
||||
att_req->ms_network_cap_len = len;
|
||||
cur += len;
|
||||
|
||||
/* V: Update Type 10.5.5.18 */
|
||||
att_req->attach_type = *cur & 0x07;
|
||||
att_req->follow_up_req = !!(*cur & 0x08);
|
||||
|
||||
/* V: GPRS Ciphering Key Sequence 10.5.1.2 */
|
||||
att_req->cksq = *cur >> 4;
|
||||
cur++;
|
||||
|
||||
/* V: DRX parameter 10.5.5.6 */
|
||||
att_req->drx_parms = osmo_load16le(cur);
|
||||
cur += 2;
|
||||
|
||||
/* LV: Mobile identity 10.5.1.4 */
|
||||
len = *cur++;
|
||||
if (msgb_l3len(msg) < (len + 12 + (cur - msgb_gmmh(msg))))
|
||||
return GMM_CAUSE_PROTO_ERR_UNSPEC;
|
||||
ret = osmo_mobile_identity_decode(&att_req->mi, cur, len, false);
|
||||
if (ret)
|
||||
return GMM_CAUSE_PROTO_ERR_UNSPEC;
|
||||
cur += len;
|
||||
|
||||
/* V: Old routing area identification 10.5.5.15 */
|
||||
osmo_routing_area_id_decode(&att_req->old_rai, cur, 6);
|
||||
cur += 6;
|
||||
|
||||
/* LV: MS radio cap 10.5.5.12a */
|
||||
len = *cur++;
|
||||
if (msgb_l3len(msg) < (len + (cur - msgb_gmmh(msg))))
|
||||
return GMM_CAUSE_PROTO_ERR_UNSPEC;
|
||||
/* 24.008 Rel 17 specifies min 5, but SGSN will still work with 4 */
|
||||
if (len < 4)
|
||||
return GMM_CAUSE_INV_MAND_INFO;
|
||||
|
||||
att_req->ms_radio_cap = cur;
|
||||
att_req->ms_radio_cap_len = len;
|
||||
cur += len;
|
||||
|
||||
mandatory_fields_len = (cur - msgb_gmmh(msg));
|
||||
if (msgb_l3len(msg) == mandatory_fields_len)
|
||||
return 0;
|
||||
|
||||
ret = tlv_parse(&att_req->tlv, &gsm48_gmm_att_ie_tlvdef,
|
||||
cur, msgb_l3len(msg) - mandatory_fields_len, 0, 0);
|
||||
|
||||
/* gracefully handle unknown IEs (partial parsing) */
|
||||
if (ret < 0 && ret != OSMO_TLVP_ERR_UNKNOWN_TLV_TYPE) {
|
||||
LOGP(DMM, LOGL_NOTICE, "%s(): tlv_parse() failed (%d)\n", __func__, ret);
|
||||
return GMM_CAUSE_COND_IE_ERR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Parse 24.008 9.4.14 RAU Request
|
||||
* \param[in] msg l3 pointers must point to gmm.
|
||||
* \param[out] rau_req parsed RA update request
|
||||
* \returns 0 on success or GMM cause
|
||||
*/
|
||||
int gprs_gmm_parse_ra_upd_req(const struct msgb *msg, struct gprs_gmm_ra_upd_req *rau_req)
|
||||
{
|
||||
uint8_t *cur, len;
|
||||
size_t mandatory_fields_len;
|
||||
struct gsm48_hdr *gh;
|
||||
int ret;
|
||||
|
||||
OSMO_ASSERT(msg);
|
||||
OSMO_ASSERT(rau_req);
|
||||
|
||||
memset(rau_req, 0, sizeof(struct gprs_gmm_ra_upd_req));
|
||||
|
||||
/* all mandatory fields + variable length MS Radio Cap (min value) would be 15 bytes.
|
||||
* But even short radio capabilities we should handle with 14 bytes */
|
||||
if (msgb_l3len(msg) < 14)
|
||||
return GMM_CAUSE_PROTO_ERR_UNSPEC;
|
||||
|
||||
gh = (struct gsm48_hdr *) msgb_gmmh(msg);
|
||||
cur = gh->data;
|
||||
|
||||
rau_req->skip_ind = gh->proto_discr >> 4;
|
||||
|
||||
/* V: Update Type 10.5.5.18 */
|
||||
rau_req->update_type = *cur & 0x07;
|
||||
rau_req->follow_up_req = !!(*cur & 0x08);
|
||||
/* V: GPRS Ciphering Key Sequence 10.5.1.2 */
|
||||
rau_req->cksq = *cur >> 4;
|
||||
cur++;
|
||||
|
||||
/* V: Old routing area identification 10.5.5.15 */
|
||||
osmo_routing_area_id_decode(&rau_req->old_rai, cur, 6);
|
||||
cur += 6;
|
||||
|
||||
/* LV: MS radio cap 10.5.5.12a */
|
||||
len = *cur++;
|
||||
if (msgb_l3len(msg) < (len + (cur - msgb_gmmh(msg))))
|
||||
return GMM_CAUSE_PROTO_ERR_UNSPEC;
|
||||
|
||||
rau_req->ms_radio_cap = cur;
|
||||
rau_req->ms_radio_cap_len = len;
|
||||
cur += len;
|
||||
|
||||
mandatory_fields_len = (cur - msgb_gmmh(msg));
|
||||
if (msgb_l3len(msg) == mandatory_fields_len)
|
||||
return 0;
|
||||
|
||||
ret = tlv_parse(&rau_req->tlv, &gsm48_gmm_rau_ie_tlvdef,
|
||||
cur, msgb_l3len(msg) - mandatory_fields_len, 0, 0);
|
||||
|
||||
if (ret < 0)
|
||||
return GMM_CAUSE_COND_IE_ERR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1112,7 +1112,7 @@ int gprs_llgmm_assign(struct gprs_llc_llme *llme,
|
||||
llme->state = GPRS_LLMS_ASSIGNED;
|
||||
} else if (old_tlli != TLLI_UNASSIGNED && new_tlli == TLLI_UNASSIGNED) {
|
||||
/* TLLI Unassignment 8.3.3) */
|
||||
llme->tlli = llme->old_tlli = 0;
|
||||
llme->tlli = llme->old_tlli = TLLI_UNASSIGNED;
|
||||
llme->state = GPRS_LLMS_UNASSIGNED;
|
||||
for (i = 0; i < ARRAY_SIZE(llme->lle); i++) {
|
||||
struct gprs_llc_lle *l = &llme->lle[i];
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <osmocom/sgsn/sgsn.h>
|
||||
#include <osmocom/sgsn/gprs_ranap.h>
|
||||
#include <osmocom/sgsn/gtp.h>
|
||||
#include <osmocom/sgsn/gtp_ggsn.h>
|
||||
#include <osmocom/sgsn/pdpctx.h>
|
||||
#include <osmocom/sgsn/mmctx.h>
|
||||
|
||||
@@ -44,17 +45,30 @@ static const struct osmo_tdef_state_timeout mm_state_iu_fsm_timeouts[32] = {
|
||||
#define mm_state_iu_fsm_state_chg(fi, NEXT_STATE) \
|
||||
osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, mm_state_iu_fsm_timeouts, sgsn->cfg.T_defs, -1)
|
||||
|
||||
static void mmctx_change_gtpu_endpoints_to_sgsn(struct sgsn_mm_ctx *mm_ctx)
|
||||
|
||||
static void pdpctx_change_gtpu_endpoint_to_sgsn(const struct sgsn_mm_ctx *mm_ctx, struct sgsn_pdp_ctx *pdp)
|
||||
{
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
LOGMMCTXP(LOGL_INFO, mm_ctx, "Changing GTP-U endpoints %s/0x%08x -> %s/0x%08x\n",
|
||||
sgsn_gtp_ntoa(&pdp->lib->gsnlu), pdp->lib->teid_own,
|
||||
inet_ntop(AF_INET, &sgsn->cfg.gtp_listenaddr.sin_addr, buf, sizeof(buf)),
|
||||
pdp->sgsn_teid_own);
|
||||
pdp->lib->gsnlu.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr);
|
||||
memcpy(pdp->lib->gsnlu.v, &sgsn->cfg.gtp_listenaddr.sin_addr,
|
||||
sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
|
||||
pdp->lib->teid_own = pdp->sgsn_teid_own;
|
||||
/* Disable Direct Tunnel Flags DTI. Other flags make no sense here, so also set to 0. */
|
||||
pdp->lib->dir_tun_flags.l = 1;
|
||||
pdp->lib->dir_tun_flags.v[0] = 0x00;
|
||||
}
|
||||
|
||||
static void mmctx_change_gtpu_endpoints_to_sgsn(struct sgsn_mm_ctx *mm_ctx, struct sgsn_pdp_ctx *pdp_skip_gtp_upd_req)
|
||||
{
|
||||
struct sgsn_pdp_ctx *pdp;
|
||||
llist_for_each_entry(pdp, &mm_ctx->pdp_list, list) {
|
||||
LOGMMCTXP(LOGL_INFO, mm_ctx, "Changing GTP-U endpoints %s -> %s\n",
|
||||
sgsn_gtp_ntoa(&pdp->lib->gsnlu),
|
||||
inet_ntop(AF_INET, &sgsn->cfg.gtp_listenaddr.sin_addr, buf, sizeof(buf)));
|
||||
sgsn_pdp_upd_gtp_u(pdp,
|
||||
&sgsn->cfg.gtp_listenaddr.sin_addr,
|
||||
sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
|
||||
pdpctx_change_gtpu_endpoint_to_sgsn(mm_ctx, pdp);
|
||||
if (pdp != pdp_skip_gtp_upd_req)
|
||||
gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,34 +80,41 @@ static void st_pmm_detached(struct osmo_fsm_inst *fi, uint32_t event, void *data
|
||||
break;
|
||||
case E_PMM_PS_DETACH:
|
||||
break;
|
||||
case E_PMM_RX_GGSN_GTPU_DT_EI:
|
||||
/* This should in general not happen, since Direct Tunnel is not
|
||||
* enabled during PMM-IDLE, but there may be a race condition of
|
||||
* packets/events, so simply ignore it. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void st_pmm_connected(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct sgsn_mm_ctx *ctx = fi->priv;
|
||||
struct sgsn_pdp_ctx *pctx;
|
||||
|
||||
switch(event) {
|
||||
case E_PMM_PS_CONN_RELEASE:
|
||||
sgsn_ranap_iu_free(ctx);
|
||||
sgsn_mm_ctx_iu_ranap_free(ctx);
|
||||
mm_state_iu_fsm_state_chg(fi, ST_PMM_IDLE);
|
||||
mmctx_change_gtpu_endpoints_to_sgsn(ctx, NULL);
|
||||
break;
|
||||
case E_PMM_PS_DETACH:
|
||||
sgsn_ranap_iu_release_free(ctx, NULL);
|
||||
sgsn_mm_ctx_iu_ranap_release_free(ctx, NULL);
|
||||
mm_state_iu_fsm_state_chg(fi, ST_PMM_DETACHED);
|
||||
break;
|
||||
case E_PMM_RA_UPDATE:
|
||||
break;
|
||||
case E_PMM_RX_GGSN_GTPU_DT_EI:
|
||||
/* GTPU Direct Tunnel (RNC<->GGSN): GGSN Received Error Indication when transmitting DL data*/
|
||||
pctx = (struct sgsn_pdp_ctx *)data;
|
||||
sgsn_mm_ctx_iu_ranap_free(ctx);
|
||||
mm_state_iu_fsm_state_chg(fi, ST_PMM_IDLE);
|
||||
mmctx_change_gtpu_endpoints_to_sgsn(ctx, pctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void st_pmm_idle_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
struct sgsn_mm_ctx *ctx = fi->priv;
|
||||
|
||||
mmctx_change_gtpu_endpoints_to_sgsn(ctx);
|
||||
}
|
||||
|
||||
static void st_pmm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
switch(event) {
|
||||
@@ -104,12 +125,19 @@ static void st_pmm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
case E_PMM_PS_DETACH:
|
||||
mm_state_iu_fsm_state_chg(fi, ST_PMM_DETACHED);
|
||||
break;
|
||||
case E_PMM_RX_GGSN_GTPU_DT_EI:
|
||||
/* This should in general not happen, since Direct Tunnel is not
|
||||
* enabled during PMM-IDLE, but there may be a race condition of
|
||||
* packets/events, so simply ignore it. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct osmo_fsm_state mm_state_iu_fsm_states[] = {
|
||||
[ST_PMM_DETACHED] = {
|
||||
.in_event_mask = X(E_PMM_PS_ATTACH) | X(E_PMM_PS_DETACH),
|
||||
.in_event_mask = X(E_PMM_PS_ATTACH) |
|
||||
X(E_PMM_PS_DETACH) |
|
||||
X(E_PMM_RX_GGSN_GTPU_DT_EI),
|
||||
.out_state_mask = X(ST_PMM_CONNECTED),
|
||||
.name = "Detached",
|
||||
.action = st_pmm_detached,
|
||||
@@ -118,7 +146,8 @@ static struct osmo_fsm_state mm_state_iu_fsm_states[] = {
|
||||
.in_event_mask =
|
||||
X(E_PMM_PS_CONN_RELEASE) |
|
||||
X(E_PMM_RA_UPDATE) |
|
||||
X(E_PMM_PS_DETACH),
|
||||
X(E_PMM_PS_DETACH) |
|
||||
X(E_PMM_RX_GGSN_GTPU_DT_EI),
|
||||
.out_state_mask = X(ST_PMM_DETACHED) | X(ST_PMM_IDLE),
|
||||
.name = "Connected",
|
||||
.action = st_pmm_connected,
|
||||
@@ -127,10 +156,10 @@ static struct osmo_fsm_state mm_state_iu_fsm_states[] = {
|
||||
.in_event_mask =
|
||||
X(E_PMM_PS_DETACH) |
|
||||
X(E_PMM_PS_CONN_ESTABLISH) |
|
||||
X(E_PMM_PS_ATTACH),
|
||||
X(E_PMM_PS_ATTACH) |
|
||||
X(E_PMM_RX_GGSN_GTPU_DT_EI),
|
||||
.out_state_mask = X(ST_PMM_DETACHED) | X(ST_PMM_CONNECTED),
|
||||
.name = "Idle",
|
||||
.onenter = st_pmm_idle_on_enter,
|
||||
.action = st_pmm_idle,
|
||||
},
|
||||
};
|
||||
@@ -141,6 +170,7 @@ const struct value_string mm_state_iu_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(E_PMM_PS_CONN_ESTABLISH),
|
||||
OSMO_VALUE_STRING(E_PMM_PS_DETACH),
|
||||
OSMO_VALUE_STRING(E_PMM_RA_UPDATE),
|
||||
OSMO_VALUE_STRING(E_PMM_RX_GGSN_GTPU_DT_EI),
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <osmocom/gprs/gprs_ns2.h>
|
||||
#include <osmocom/gprs/gprs_bssgp_bss.h>
|
||||
#include <osmocom/sgsn/gprs_llc.h>
|
||||
#include <osmocom/sgsn/gprs_routing_area.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@@ -52,6 +53,7 @@ void gprs_ns_prim_status_cb(struct osmo_gprs_ns2_prim *nsp)
|
||||
break;
|
||||
case GPRS_NS2_AFF_CAUSE_FAILURE:
|
||||
LOGP(DGPRS, LOGL_NOTICE, "NS-E %d became unavailable\n", nsp->nsei);
|
||||
sgsn_ra_geran_nsei_failure_ind(nsp->nsei);
|
||||
break;
|
||||
default:
|
||||
LOGP(DGPRS, LOGL_NOTICE, "NS: %s Unknown prim %d from NS\n",
|
||||
|
||||
@@ -22,13 +22,22 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <gtp.h>
|
||||
|
||||
#include <asn1c/asn1helpers.h>
|
||||
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/gprs/gprs_msgb.h>
|
||||
|
||||
#include <osmocom/ranap/ranap_common.h>
|
||||
#include <osmocom/ranap/ranap_common_cn.h>
|
||||
#include <osmocom/ranap/ranap_ies_defs.h>
|
||||
#include <osmocom/ranap/ranap_msg_factory.h>
|
||||
#include <osmocom/ranap/iu_helpers.h>
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#include <osmocom/sgsn/gprs_gmm.h>
|
||||
#include <osmocom/sgsn/gprs_sm.h>
|
||||
@@ -37,20 +46,38 @@
|
||||
#include <osmocom/sgsn/gprs_ranap.h>
|
||||
#include <osmocom/sgsn/gprs_gmm_attach.h>
|
||||
#include <osmocom/sgsn/gprs_mm_state_iu_fsm.h>
|
||||
#include <osmocom/sgsn/gprs_routing_area.h>
|
||||
#include <osmocom/sgsn/gtp_ggsn.h>
|
||||
#include <osmocom/sgsn/gtp.h>
|
||||
#include <osmocom/sgsn/iu_rnc.h>
|
||||
#include <osmocom/sgsn/iu_rnc_fsm.h>
|
||||
#include <osmocom/sgsn/pdpctx.h>
|
||||
#include <osmocom/sgsn/mmctx.h>
|
||||
|
||||
/* Send RAB activation requests for all PDP contexts */
|
||||
void activate_pdp_rabs(struct sgsn_mm_ctx *ctx)
|
||||
/* Parsed global RNC id. See also struct RANAP_GlobalRNC_ID, and note that the
|
||||
* PLMN identity is a BCD representation of the MCC and MNC.
|
||||
* See iu_grnc_id_parse(). */
|
||||
static int iu_grnc_id_parse(struct osmo_rnc_id *dst, const struct RANAP_GlobalRNC_ID *src)
|
||||
{
|
||||
struct sgsn_pdp_ctx *pdp;
|
||||
if (ctx->ran_type != MM_CTX_T_UTRAN_Iu)
|
||||
return;
|
||||
llist_for_each_entry(pdp, &ctx->pdp_list, list) {
|
||||
iu_rab_act_ps(pdp->nsapi, pdp);
|
||||
/* The size is coming from arbitrary sender, check it gracefully */
|
||||
if (src->pLMNidentity.size != 3) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Invalid PLMN Identity size: should be 3, is %d\n",
|
||||
src->pLMNidentity.size);
|
||||
return -1;
|
||||
}
|
||||
osmo_plmn_from_bcd(&src->pLMNidentity.buf[0], &dst->plmn);
|
||||
dst->rnc_id = (uint16_t)src->rNC_ID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* not used at present */
|
||||
int sgsn_ranap_iu_grnc_id_compose(struct iu_grnc_id *dst, const struct osmo_rnc_id *src)
|
||||
{
|
||||
dst->grnc_id.pLMNidentity.buf = &dst->plmn_buf[0];
|
||||
dst->grnc_id.pLMNidentity.size = 3;
|
||||
osmo_plmn_to_bcd(dst->grnc_id.pLMNidentity.buf, &src->plmn);
|
||||
dst->grnc_id.rNC_ID = src->rnc_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Callback for RAB assignment response */
|
||||
@@ -60,36 +87,44 @@ static int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrMod
|
||||
bool require_pdp_update = false;
|
||||
struct sgsn_pdp_ctx *pdp = NULL;
|
||||
RANAP_RAB_SetupOrModifiedItem_t *item = &setup_ies->raB_SetupOrModifiedItem;
|
||||
int rc;
|
||||
|
||||
rab_id = item->rAB_ID.buf[0];
|
||||
|
||||
pdp = sgsn_pdp_ctx_by_nsapi(ctx, rab_id);
|
||||
if (!pdp) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Response for unknown RAB/NSAPI=%u\n", rab_id);
|
||||
sgsn_mm_ctx_iu_ranap_release_free(ctx, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (item->transportLayerAddress) {
|
||||
struct osmo_sockaddr addr;
|
||||
LOGPC(DRANAP, LOGL_INFO, " Setup: (%u/%s)", rab_id, osmo_hexdump(item->transportLayerAddress->buf,
|
||||
item->transportLayerAddress->size));
|
||||
switch (item->transportLayerAddress->size) {
|
||||
case 7:
|
||||
/* It must be IPv4 inside a X213 NSAP */
|
||||
memcpy(pdp->lib->gsnlu.v, &item->transportLayerAddress->buf[3], 4);
|
||||
rc = ranap_transp_layer_addr_decode2(&addr, NULL, item->transportLayerAddress);
|
||||
if (rc < 0) {
|
||||
LOGP(DRANAP, LOGL_ERROR,
|
||||
"RAB Assignment Resp: Unknown Transport Layer Address (size %u): %s\n",
|
||||
item->transportLayerAddress->size,
|
||||
osmo_hexdump(item->transportLayerAddress->buf, item->transportLayerAddress->size));
|
||||
goto ret_error;
|
||||
}
|
||||
|
||||
switch (addr.u.sa.sa_family) {
|
||||
case AF_INET:
|
||||
memcpy(pdp->lib->gsnlu.v, (uint8_t *)&addr.u.sin.sin_addr.s_addr, 4);
|
||||
break;
|
||||
case 4:
|
||||
/* It must be a raw IPv4 address */
|
||||
memcpy(pdp->lib->gsnlu.v, item->transportLayerAddress->buf, 4);
|
||||
break;
|
||||
case 16:
|
||||
/* TODO: It must be a raw IPv6 address */
|
||||
case 19:
|
||||
/* TODO: It must be IPv6 inside a X213 NSAP */
|
||||
case AF_INET6:
|
||||
/* TODO: Support IPv6 address */
|
||||
LOGP(DRANAP, LOGL_ERROR,
|
||||
"RAB Assignment Resp: IPv6 transport layer address not supported!\n");
|
||||
goto ret_error;
|
||||
default:
|
||||
LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Resp: Unknown "
|
||||
"transport layer address size %u\n",
|
||||
item->transportLayerAddress->size);
|
||||
return -1;
|
||||
LOGP(DRANAP, LOGL_ERROR,
|
||||
"RAB Assignment Resp: Unexpected transport layer address size %u\n",
|
||||
item->transportLayerAddress->size);
|
||||
goto ret_error;
|
||||
}
|
||||
require_pdp_update = true;
|
||||
}
|
||||
@@ -103,6 +138,8 @@ static int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrMod
|
||||
LOGP(DRANAP, LOGL_DEBUG, "Updating TEID on RNC side from 0x%08x to 0x%08x\n",
|
||||
pdp->lib->teid_own, tei);
|
||||
pdp->lib->teid_own = tei;
|
||||
pdp->lib->dir_tun_flags.l = 1;
|
||||
pdp->lib->dir_tun_flags.v[0] = 0x01; /* Set DTI flag in Direct Tunnel Flags */
|
||||
require_pdp_update = true;
|
||||
}
|
||||
|
||||
@@ -115,18 +152,33 @@ static int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrMod
|
||||
}
|
||||
return 0;
|
||||
|
||||
ret_error:
|
||||
if (pdp->state != PDP_STATE_CR_CONF) {
|
||||
gsm48_tx_gsm_act_pdp_rej(ctx, pdp->ti, GSM_CAUSE_NET_FAIL,
|
||||
0, NULL);
|
||||
sgsn_delete_pdp_ctx(pdp);
|
||||
} else {
|
||||
gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL, true);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data)
|
||||
static int sgsn_ranap_iu_event_mmctx(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data)
|
||||
{
|
||||
struct sgsn_mm_ctx *mm;
|
||||
int rc = -1;
|
||||
|
||||
if (!ctx) {
|
||||
LOGIUP(ctx, LOGL_ERROR, "NULL ctx given for IU event %s\n",
|
||||
iu_client_event_type_str(type));
|
||||
return rc;
|
||||
}
|
||||
|
||||
mm = sgsn_mm_ctx_by_ue_ctx(ctx);
|
||||
if (!mm) {
|
||||
LOGIUP(ctx, LOGL_NOTICE, "Cannot find mm ctx for IU event %s\n",
|
||||
ranap_iu_event_type_str(type));
|
||||
ranap_iu_free_ue(ctx);
|
||||
iu_client_event_type_str(type));
|
||||
sgsn_ranap_iu_free_ue(ctx);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -138,10 +190,10 @@ int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type
|
||||
/* fall thru */
|
||||
case RANAP_IU_EVENT_LINK_INVALIDATED:
|
||||
/* Clean up ranap_ue_conn_ctx here */
|
||||
LOGMMCTXP(LOGL_INFO, mm, "IU release (cause=%s)\n", ranap_iu_event_type_str(type));
|
||||
LOGMMCTXP(LOGL_INFO, mm, "IU release (cause=%s)\n", iu_client_event_type_str(type));
|
||||
rc = osmo_fsm_inst_dispatch(mm->iu.mm_state_fsm, E_PMM_PS_CONN_RELEASE, NULL);
|
||||
if (rc < 0)
|
||||
sgsn_ranap_iu_free(mm);
|
||||
sgsn_mm_ctx_iu_ranap_free(mm);
|
||||
|
||||
/* TODO: move this into FSM */
|
||||
if (mm->ran_type == MM_CTX_T_UTRAN_Iu && mm->gmm_att_req.fsm->state != ST_INIT)
|
||||
@@ -156,7 +208,7 @@ int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type
|
||||
*/
|
||||
/* Continue authentication here */
|
||||
mm->iu.ue_ctx->integrity_active = 1;
|
||||
ranap_iu_tx_common_id(mm->iu.ue_ctx, mm->imsi);
|
||||
sgsn_ranap_iu_tx_common_id(mm->iu.ue_ctx, mm->imsi);
|
||||
|
||||
/* FIXME: remove gmm_authorize */
|
||||
if (mm->pending_req != GSM48_MT_GMM_ATTACH_REQ)
|
||||
@@ -166,105 +218,617 @@ int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type
|
||||
rc = 0;
|
||||
break;
|
||||
default:
|
||||
LOGMMCTXP(LOGL_NOTICE, mm, "Unknown event received: %i\n", type);
|
||||
LOGMMCTXP(LOGL_NOTICE, mm, "Unknown event received: %d\n", type);
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void sgsn_ranap_iu_free(struct sgsn_mm_ctx *ctx)
|
||||
|
||||
int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
struct ranap_iu_event_new_area *new_area;
|
||||
|
||||
if (!ctx->iu.ue_ctx)
|
||||
return;
|
||||
switch (type) {
|
||||
case RANAP_IU_EVENT_RAB_ASSIGN:
|
||||
case RANAP_IU_EVENT_IU_RELEASE:
|
||||
case RANAP_IU_EVENT_LINK_INVALIDATED:
|
||||
case RANAP_IU_EVENT_SECURITY_MODE_COMPLETE:
|
||||
return sgsn_ranap_iu_event_mmctx(ctx, type, data);
|
||||
case RANAP_IU_EVENT_NEW_AREA:
|
||||
/* inform the Routing Area code about a new RA for Iu */
|
||||
new_area = data;
|
||||
|
||||
ranap_iu_free_ue(ctx->iu.ue_ctx);
|
||||
ctx->iu.ue_ctx = NULL;
|
||||
/* Only interesting in Routing Area changes, but not Location Area */
|
||||
if (new_area->cell_type != RANAP_IU_NEW_RAC)
|
||||
return 0;
|
||||
|
||||
return sgsn_ra_utran_register(new_area->u.rai, new_area->rnc_id);
|
||||
default:
|
||||
LOGP(DRANAP, LOGL_NOTICE, "Iu: Unknown event received: type: %d\n", type);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void sgsn_ranap_iu_release_free(struct sgsn_mm_ctx *ctx,
|
||||
const struct RANAP_Cause *cause)
|
||||
{
|
||||
unsigned long X1001;
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
if (!ctx->iu.ue_ctx)
|
||||
return;
|
||||
|
||||
X1001 = osmo_tdef_get(sgsn->cfg.T_defs, -1001, OSMO_TDEF_S, -1);
|
||||
|
||||
ranap_iu_tx_release_free(ctx->iu.ue_ctx,
|
||||
cause,
|
||||
(int) X1001);
|
||||
ctx->iu.ue_ctx = NULL;
|
||||
}
|
||||
|
||||
int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp)
|
||||
int sgsn_ranap_iu_tx_rab_ps_ass_req(struct ranap_ue_conn_ctx *ue_ctx,
|
||||
uint8_t rab_id, uint32_t gtp_ip, uint32_t gtp_tei)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct sgsn_mm_ctx *mm = pdp->mm;
|
||||
struct ranap_ue_conn_ctx *uectx;
|
||||
uint32_t ggsn_ip;
|
||||
bool use_x213_nsap;
|
||||
bool use_x213_nsap = (ue_ctx->rab_assign_addr_enc == RANAP_NSAP_ADDR_ENC_X213);
|
||||
|
||||
uectx = mm->iu.ue_ctx;
|
||||
use_x213_nsap = (uectx->rab_assign_addr_enc == RANAP_NSAP_ADDR_ENC_X213);
|
||||
LOGP(DRANAP, LOGL_DEBUG,
|
||||
"Assigning RAB: rab_id=%u, ggsn_ip=%x, teid_gn=%x, use_x213_nsap=%d\n",
|
||||
rab_id, gtp_ip, gtp_tei, use_x213_nsap);
|
||||
|
||||
/* Get the IP address for ggsn user plane */
|
||||
memcpy(&ggsn_ip, pdp->lib->gsnru.v, pdp->lib->gsnru.l);
|
||||
ggsn_ip = htonl(ggsn_ip);
|
||||
|
||||
LOGP(DRANAP, LOGL_DEBUG, "Assigning RAB: rab_id=%d, ggsn_ip=%x,"
|
||||
" teid_gn=%x, use_x213_nsap=%d\n",
|
||||
rab_id, ggsn_ip, pdp->lib->teid_gn, use_x213_nsap);
|
||||
|
||||
msg = ranap_new_msg_rab_assign_data(rab_id, ggsn_ip,
|
||||
pdp->lib->teid_gn, use_x213_nsap);
|
||||
msg->l2h = msg->data;
|
||||
return ranap_iu_rab_act(uectx, msg);
|
||||
msg = ranap_new_msg_rab_assign_data(rab_id, gtp_ip, gtp_tei, use_x213_nsap);
|
||||
return sgsn_scu_iups_tx_data_req(ue_ctx->rnc->scu_iups, ue_ctx->conn_id, msg);
|
||||
}
|
||||
|
||||
|
||||
/* Main entry point for incoming 04.08 GPRS messages from Iu */
|
||||
int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id,
|
||||
uint16_t *sai)
|
||||
int sgsn_ranap_iu_tx_sec_mode_cmd(struct ranap_ue_conn_ctx *uectx, struct osmo_auth_vector *vec,
|
||||
int send_ck, int new_key)
|
||||
{
|
||||
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg);
|
||||
uint8_t pdisc = gsm48_hdr_pdisc(gh);
|
||||
struct sgsn_mm_ctx *mmctx;
|
||||
int rc = -EINVAL;
|
||||
struct msgb *msg;
|
||||
|
||||
mmctx = sgsn_mm_ctx_by_ue_ctx(MSG_IU_UE_CTX(msg));
|
||||
if (mmctx) {
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PKTS_SIG_IN));
|
||||
if (ra_id)
|
||||
memcpy(&mmctx->ra, ra_id, sizeof(mmctx->ra));
|
||||
/* create RANAP message */
|
||||
msg = ranap_new_msg_sec_mod_cmd(vec->ik, send_ck ? vec->ck : NULL,
|
||||
new_key ? RANAP_KeyStatus_new : RANAP_KeyStatus_old);
|
||||
return sgsn_scu_iups_tx_data_req(uectx->rnc->scu_iups, uectx->conn_id, msg);
|
||||
}
|
||||
|
||||
int sgsn_ranap_iu_tx_common_id(struct ranap_ue_conn_ctx *uectx, const char *imsi)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
LOGP(DRANAP, LOGL_INFO, "Transmitting RANAP CommonID (SCCP conn_id %u)\n",
|
||||
uectx->conn_id);
|
||||
|
||||
msg = ranap_new_msg_common_id(imsi);
|
||||
return sgsn_scu_iups_tx_data_req(uectx->rnc->scu_iups, uectx->conn_id, msg);
|
||||
}
|
||||
|
||||
int sgsn_ranap_iu_tx(struct msgb *msg_nas, uint8_t sapi)
|
||||
{
|
||||
struct ranap_ue_conn_ctx *uectx = msg_nas->dst;
|
||||
struct msgb *msg;
|
||||
|
||||
if (!uectx) {
|
||||
LOGP(DRANAP, LOGL_ERROR,
|
||||
"Discarding to-be-transmitted L3 Message as RANAP DT with unset dst SCCP conn_id!\n");
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
/* MMCTX can be NULL */
|
||||
LOGP(DRANAP, LOGL_INFO, "Transmitting L3 Message as RANAP DT (SCCP conn_id %u)\n",
|
||||
uectx->conn_id);
|
||||
|
||||
switch (pdisc) {
|
||||
case GSM48_PDISC_MM_GPRS:
|
||||
rc = gsm0408_rcv_gmm(mmctx, msg, NULL, false);
|
||||
#pragma message "set drop_cipherable arg for gsm0408_rcv_gmm() from IuPS?"
|
||||
break;
|
||||
case GSM48_PDISC_SM_GPRS:
|
||||
rc = gsm0408_rcv_gsm(mmctx, msg, NULL);
|
||||
break;
|
||||
default:
|
||||
LOGMMCTXP(LOGL_NOTICE, mmctx,
|
||||
"Unknown GSM 04.08 discriminator 0x%02x: %s\n",
|
||||
pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg)));
|
||||
/* FIXME: return status message */
|
||||
break;
|
||||
msg = ranap_new_msg_dt(sapi, msg_nas->data, msgb_length(msg_nas));
|
||||
msgb_free(msg_nas);
|
||||
|
||||
return sgsn_scu_iups_tx_data_req(uectx->rnc->scu_iups, uectx->conn_id, msg);
|
||||
}
|
||||
|
||||
/* Send CL RANAP message over SCCP: */
|
||||
int sgsn_ranap_iu_tx_cl(struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_sccp_addr *dst_addr,
|
||||
struct msgb *msg)
|
||||
{
|
||||
msg->l2h = msg->data;
|
||||
return osmo_sccp_tx_unitdata_msg(scu_iups->scu, &scu_iups->local_sccp_addr, dst_addr, msg);
|
||||
}
|
||||
|
||||
/* Send RANAP Error Indication */
|
||||
int sgsn_ranap_iu_tx_error_ind(struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_sccp_addr *dst_addr,
|
||||
const RANAP_Cause_t *cause)
|
||||
{
|
||||
RANAP_CN_DomainIndicator_t domain = RANAP_CN_DomainIndicator_ps_domain;
|
||||
struct msgb *ranap_msg;
|
||||
|
||||
ranap_msg = ranap_new_msg_error_ind(cause, NULL, &domain, NULL);
|
||||
if (!ranap_msg)
|
||||
return -ENOMEM;
|
||||
|
||||
return sgsn_ranap_iu_tx_cl(scu_iups, dst_addr, ranap_msg);
|
||||
}
|
||||
|
||||
/* Send Iu Release for the given UE connection.
|
||||
* If cause is NULL, Normal Release cause is sent, otherwise
|
||||
* the provided cause. */
|
||||
int sgsn_ranap_iu_tx_release(struct ranap_ue_conn_ctx *uectx, const struct RANAP_Cause *cause)
|
||||
{
|
||||
struct msgb *msg;
|
||||
static const struct RANAP_Cause default_cause = {
|
||||
.present = RANAP_Cause_PR_nAS,
|
||||
.choice.radioNetwork = RANAP_CauseNAS_normal_release,
|
||||
};
|
||||
|
||||
if (!cause)
|
||||
cause = &default_cause;
|
||||
|
||||
msg = ranap_new_msg_iu_rel_cmd(cause);
|
||||
return sgsn_scu_iups_tx_data_req(uectx->rnc->scu_iups, uectx->conn_id, msg);
|
||||
}
|
||||
|
||||
void sgsn_ranap_iu_tx_release_free(struct ranap_ue_conn_ctx *ctx,
|
||||
const struct RANAP_Cause *cause,
|
||||
int timeout)
|
||||
{
|
||||
ctx->notification = false;
|
||||
ctx->free_on_release = true;
|
||||
int ret = sgsn_ranap_iu_tx_release(ctx, cause);
|
||||
/* On Tx failure, trigger timeout immediately, as the response will never arrive */
|
||||
if (ret)
|
||||
timeout = 0;
|
||||
|
||||
osmo_timer_schedule(&ctx->release_timeout, timeout, 0);
|
||||
}
|
||||
|
||||
static int ranap_handle_co_initial_ue(struct ranap_iu_rnc *rnc,
|
||||
uint32_t conn_id,
|
||||
const RANAP_InitialUE_MessageIEs_t *ies)
|
||||
{
|
||||
struct gprs_ra_id ra_id = {};
|
||||
struct osmo_routing_area_id ra_id2 = {};
|
||||
struct osmo_rnc_id rnc_id = {};
|
||||
uint16_t sai;
|
||||
struct ranap_ue_conn_ctx *ue;
|
||||
struct msgb *msg = msgb_alloc(256, "RANAP->NAS");
|
||||
|
||||
if (ranap_parse_lai(&ra_id, &ies->lai) != 0) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Failed to parse RANAP LAI IE\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* MMCTX can be invalid */
|
||||
if (!(ies->presenceMask & INITIALUE_MESSAGEIES_RANAP_RAC_PRESENT)) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Rejecting InitialUE msg without RAC IE\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ra_id.rac = asn1str_to_u8(&ies->rac);
|
||||
if (ra_id.rac == OSMO_RESERVED_RAC) {
|
||||
LOGP(DRANAP, LOGL_ERROR,
|
||||
"Rejecting RNC with invalid/internally used RAC 0x%02x\n", ra_id.rac);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (iu_grnc_id_parse(&rnc_id, &ies->globalRNC_ID) != 0) {
|
||||
LOGP(DRANAP, LOGL_ERROR,
|
||||
"Failed to parse RANAP Global-RNC-ID IE\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sai = asn1str_to_u16(&ies->sai.sAC);
|
||||
msgb_gmmh(msg) = msgb_put(msg, ies->nas_pdu.size);
|
||||
memcpy(msgb_gmmh(msg), ies->nas_pdu.buf, ies->nas_pdu.size);
|
||||
|
||||
gprs_rai_to_osmo(&ra_id2, &ra_id);
|
||||
|
||||
/* Make sure we update LAC+RAC coming in on this connection. */
|
||||
iu_rnc_update_rai_seen(rnc, &ra_id2);
|
||||
|
||||
ue = ue_conn_ctx_alloc(rnc, conn_id);
|
||||
OSMO_ASSERT(ue);
|
||||
ue->ra_id = ra_id;
|
||||
|
||||
/* Feed into the MM layer */
|
||||
msg->dst = ue;
|
||||
gsm0408_gprs_rcvmsg_iu(msg, &ra_id, &sai);
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sgsn_ranap_iu_handle_co_initial(struct ranap_iu_rnc *iu_rnc,
|
||||
uint32_t conn_id,
|
||||
const ranap_message *message)
|
||||
{
|
||||
int rc;
|
||||
|
||||
LOGP(DRANAP, LOGL_NOTICE, "handle_co_initial(dir=%u, proc=%u)\n", message->direction, message->procedureCode);
|
||||
|
||||
if (message->direction != RANAP_RANAP_PDU_PR_initiatingMessage
|
||||
|| message->procedureCode != RANAP_ProcedureCode_id_InitialUE_Message) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Expected direction 'InitiatingMessage',"
|
||||
" procedureCode 'InitialUE_Message', instead got %u and %u\n",
|
||||
message->direction, message->procedureCode);
|
||||
rc = -1;
|
||||
} else
|
||||
rc = ranap_handle_co_initial_ue(iu_rnc, conn_id, &message->msg.initialUE_MessageIEs);
|
||||
|
||||
if (rc) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Error in %s (%d)\n", __func__, rc);
|
||||
/* TODO handling of the error? */
|
||||
}
|
||||
}
|
||||
|
||||
int sgsn_ranap_iu_rx_co_initial_msg(struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_sccp_addr *rem_sccp_addr,
|
||||
uint32_t conn_id,
|
||||
const uint8_t *data, size_t len)
|
||||
{
|
||||
struct iu_rnc_ev_msg_up_co_initial_ctx ev_ctx = {
|
||||
.conn_id = conn_id,
|
||||
};
|
||||
RANAP_Cause_t cause;
|
||||
int rc;
|
||||
|
||||
rc = ranap_cn_rx_co_decode2(&ev_ctx.message, data, len);
|
||||
if (rc != 0) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Not calling cn_ranap_handle_co_initial() due to rc=%d\n", rc);
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
ev_ctx.rnc = iu_rnc_find_by_addr(rem_sccp_addr);
|
||||
if (!ev_ctx.rnc)
|
||||
goto tx_err_ind;
|
||||
|
||||
rc = osmo_fsm_inst_dispatch(ev_ctx.rnc->fi, IU_RNC_EV_MSG_UP_CO_INITIAL, &ev_ctx);
|
||||
if (rc != 0)
|
||||
goto tx_err_ind;
|
||||
|
||||
goto free_ret;
|
||||
|
||||
tx_err_ind:
|
||||
cause = (RANAP_Cause_t){
|
||||
.present = RANAP_Cause_PR_protocol,
|
||||
.choice.protocol = RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
|
||||
};
|
||||
sgsn_ranap_iu_tx_error_ind(scu_iups, rem_sccp_addr, &cause);
|
||||
free_ret:
|
||||
/* Free the asn1 structs in message */
|
||||
ranap_cn_rx_co_free(&ev_ctx.message);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ranap_handle_co_dt(struct ranap_ue_conn_ctx *ue_ctx, const RANAP_DirectTransferIEs_t *ies)
|
||||
{
|
||||
struct gprs_ra_id _ra_id, *ra_id = NULL;
|
||||
uint16_t _sai, *sai = NULL;
|
||||
struct msgb *msg = msgb_alloc(256, "RANAP->NAS");
|
||||
|
||||
if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_LAI_PRESENT) {
|
||||
if (ranap_parse_lai(&_ra_id, &ies->lai) != 0) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Failed to parse RANAP LAI IE\n");
|
||||
return -1;
|
||||
}
|
||||
ra_id = &_ra_id;
|
||||
if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_RAC_PRESENT)
|
||||
_ra_id.rac = asn1str_to_u8(&ies->rac);
|
||||
|
||||
if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_SAI_PRESENT) {
|
||||
_sai = asn1str_to_u16(&ies->sai.sAC);
|
||||
sai = &_sai;
|
||||
}
|
||||
}
|
||||
|
||||
msgb_gmmh(msg) = msgb_put(msg, ies->nas_pdu.size);
|
||||
memcpy(msgb_gmmh(msg), ies->nas_pdu.buf, ies->nas_pdu.size);
|
||||
|
||||
/* Feed into the MM/CC/SMS-CP layer */
|
||||
msg->dst = ue_ctx;
|
||||
gsm0408_gprs_rcvmsg_iu(msg, ra_id, sai);
|
||||
|
||||
msgb_free(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ranap_handle_co_err_ind(struct ranap_ue_conn_ctx *ue_ctx, const RANAP_ErrorIndicationIEs_t *ies)
|
||||
{
|
||||
if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CAUSE_PRESENT)
|
||||
LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication (%s)\n",
|
||||
ranap_cause_str(&ies->cause));
|
||||
else
|
||||
LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ranap_handle_co_iu_rel_req(struct ranap_ue_conn_ctx *ue_ctx, const RANAP_Iu_ReleaseRequestIEs_t *ies)
|
||||
{
|
||||
LOGP(DRANAP, LOGL_INFO, "Received Iu Release Request, Sending Release Command\n");
|
||||
sgsn_ranap_iu_tx_release(ue_ctx, &ies->cause);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ranap_handle_co_rab_ass_resp(struct ranap_ue_conn_ctx *ue_ctx, const RANAP_RAB_AssignmentResponseIEs_t *ies)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
LOGP(DRANAP, LOGL_INFO,
|
||||
"Rx RAB Assignment Response for UE conn_id %u\n", ue_ctx->conn_id);
|
||||
if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_SETUPORMODIFIEDLIST_PRESENT) {
|
||||
/* TODO: Iterate over list of SetupOrModifiedList IEs and handle each one */
|
||||
RANAP_IE_t *ranap_ie = ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[0];
|
||||
RANAP_RAB_SetupOrModifiedItemIEs_t setup_ies;
|
||||
|
||||
rc = ranap_decode_rab_setupormodifieditemies_fromlist(&setup_ies, &ranap_ie->value);
|
||||
if (rc) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Error in ranap_decode_rab_setupormodifieditemies()\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = global_iu_event(ue_ctx, RANAP_IU_EVENT_RAB_ASSIGN, &setup_ies);
|
||||
|
||||
ranap_free_rab_setupormodifieditemies(&setup_ies);
|
||||
}
|
||||
/* FIXME: handle RAB Ass failure? */
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Entry point for connection-oriented RANAP message */
|
||||
void sgsn_ranap_iu_handle_co(struct ranap_ue_conn_ctx *ue_ctx, const ranap_message *message)
|
||||
{
|
||||
int rc;
|
||||
|
||||
LOGP(DRANAP, LOGL_NOTICE, "handle_co(dir=%u, proc=%u)\n", message->direction, message->procedureCode);
|
||||
|
||||
switch (message->direction) {
|
||||
case RANAP_RANAP_PDU_PR_initiatingMessage:
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_InitialUE_Message:
|
||||
LOGP(DRANAP, LOGL_ERROR, "Got InitialUE_Message but this is not a new conn\n");
|
||||
rc = -1;
|
||||
break;
|
||||
case RANAP_ProcedureCode_id_DirectTransfer:
|
||||
rc = ranap_handle_co_dt(ue_ctx, &message->msg.directTransferIEs);
|
||||
break;
|
||||
case RANAP_ProcedureCode_id_ErrorIndication:
|
||||
rc = ranap_handle_co_err_ind(ue_ctx, &message->msg.errorIndicationIEs);
|
||||
break;
|
||||
case RANAP_ProcedureCode_id_Iu_ReleaseRequest:
|
||||
/* Iu Release Request */
|
||||
rc = ranap_handle_co_iu_rel_req(ue_ctx, &message->msg.iu_ReleaseRequestIEs);
|
||||
break;
|
||||
default:
|
||||
LOGP(DRANAP, LOGL_ERROR, "Received Initiating Message: unknown Procedure Code %d\n",
|
||||
message->procedureCode);
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case RANAP_RANAP_PDU_PR_successfulOutcome:
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_SecurityModeControl:
|
||||
/* Security Mode Complete */
|
||||
rc = global_iu_event(ue_ctx, RANAP_IU_EVENT_SECURITY_MODE_COMPLETE, NULL);
|
||||
break;
|
||||
case RANAP_ProcedureCode_id_Iu_Release:
|
||||
/* Iu Release Complete */
|
||||
rc = global_iu_event(ue_ctx, RANAP_IU_EVENT_IU_RELEASE, NULL);
|
||||
if (rc) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Iu Release event: Iu Event callback returned %d\n",
|
||||
rc);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGP(DRANAP, LOGL_ERROR, "Received Successful Outcome: unknown Procedure Code %d\n",
|
||||
message->procedureCode);
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case RANAP_RANAP_PDU_PR_outcome:
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_RAB_Assignment:
|
||||
/* RAB Assignment Response */
|
||||
rc = ranap_handle_co_rab_ass_resp(ue_ctx, &message->msg.raB_AssignmentResponseIEs);
|
||||
break;
|
||||
default:
|
||||
LOGP(DRANAP, LOGL_ERROR, "Received Outcome: unknown Procedure Code %d\n",
|
||||
message->procedureCode);
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
|
||||
default:
|
||||
LOGP(DRANAP, LOGL_ERROR, "Received Unsuccessful Outcome: Procedure Code %d\n",
|
||||
message->procedureCode);
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Error in %s (%d)\n", __func__, rc);
|
||||
/* TODO handling of the error? */
|
||||
}
|
||||
}
|
||||
|
||||
int sgsn_ranap_iu_rx_co_msg(struct ranap_ue_conn_ctx *ue_ctx, const uint8_t *data, size_t len)
|
||||
{
|
||||
struct iu_rnc_ev_msg_up_co_ctx ev_ctx = {
|
||||
.ue_ctx = ue_ctx,
|
||||
};
|
||||
RANAP_Cause_t cause;
|
||||
int rc;
|
||||
|
||||
rc = ranap_cn_rx_co_decode2(&ev_ctx.message, data, len);
|
||||
if (rc != 0) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Not calling cn_ranap_handle_co() due to rc=%d\n", rc);
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
rc = osmo_fsm_inst_dispatch(ue_ctx->rnc->fi, IU_RNC_EV_MSG_UP_CO, &ev_ctx);
|
||||
if (rc != 0)
|
||||
goto tx_err_ind;
|
||||
|
||||
goto free_ret;
|
||||
|
||||
tx_err_ind:
|
||||
cause = (RANAP_Cause_t){
|
||||
.present = RANAP_Cause_PR_protocol,
|
||||
.choice.protocol = RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
|
||||
};
|
||||
sgsn_ranap_iu_tx_error_ind(ue_ctx->rnc->scu_iups, &ue_ctx->rnc->sccp_addr, &cause);
|
||||
|
||||
free_ret:
|
||||
/* Free the asn1 structs in message */
|
||||
ranap_cn_rx_co_free(&ev_ctx.message);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ranap_handle_cl_reset_req(struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_scu_unitdata_param *ud_prim,
|
||||
const RANAP_ResetIEs_t *ies)
|
||||
{
|
||||
const RANAP_GlobalRNC_ID_t *grnc_id = NULL;
|
||||
RANAP_Cause_t cause;
|
||||
struct osmo_rnc_id rnc_id = {};
|
||||
struct ranap_iu_rnc *rnc;
|
||||
int rc;
|
||||
|
||||
if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CN_DOMAININDICATOR_PRESENT) {
|
||||
if (ies->cN_DomainIndicator != RANAP_CN_DomainIndicator_ps_domain) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Rx RESET: Unexpected CN Domain Indicator %d\n",
|
||||
(int)ies->cN_DomainIndicator);
|
||||
cause = (RANAP_Cause_t){
|
||||
.present = RANAP_Cause_PR_protocol,
|
||||
.choice.protocol = RANAP_CauseProtocol_semantic_error,
|
||||
};
|
||||
return sgsn_ranap_iu_tx_error_ind(scu_iups, &ud_prim->calling_addr, &cause);
|
||||
}
|
||||
} /* else: assume PS */
|
||||
|
||||
/* FIXME: support handling Extended RNC-ID instead of Global RNC-ID */
|
||||
|
||||
if (!(ies->presenceMask & RESETIES_RANAP_GLOBALRNC_ID_PRESENT)) {
|
||||
LOGP(DRANAP, LOGL_ERROR,
|
||||
"Rx RESET: Missing RANAP Global-RNC-ID IE\n");
|
||||
cause = (RANAP_Cause_t){
|
||||
.present = RANAP_Cause_PR_protocol,
|
||||
.choice.protocol = RANAP_CauseProtocol_transfer_syntax_error,
|
||||
};
|
||||
return sgsn_ranap_iu_tx_error_ind(scu_iups, &ud_prim->calling_addr, &cause);
|
||||
}
|
||||
grnc_id = &ies->globalRNC_ID;
|
||||
|
||||
if (iu_grnc_id_parse(&rnc_id, grnc_id) != 0) {
|
||||
LOGP(DRANAP, LOGL_ERROR,
|
||||
"Rx RESET: Failed to parse RANAP Global-RNC-ID IE\n");
|
||||
cause = (RANAP_Cause_t){
|
||||
.present = RANAP_Cause_PR_protocol,
|
||||
.choice.protocol = RANAP_CauseProtocol_transfer_syntax_error,
|
||||
};
|
||||
return sgsn_ranap_iu_tx_error_ind(scu_iups, &ud_prim->calling_addr, &cause);
|
||||
}
|
||||
|
||||
rnc = iu_rnc_find_or_create(&rnc_id, scu_iups, &ud_prim->calling_addr);
|
||||
OSMO_ASSERT(rnc);
|
||||
rc = osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_RX_RESET, NULL);
|
||||
if (rc != 0) {
|
||||
cause = (RANAP_Cause_t){
|
||||
.present = RANAP_Cause_PR_protocol,
|
||||
.choice.protocol = RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
|
||||
};
|
||||
return sgsn_ranap_iu_tx_error_ind(scu_iups, &ud_prim->calling_addr, &cause);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ranap_handle_cl_reset_ack(struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_scu_unitdata_param *ud_prim,
|
||||
const RANAP_ResetAcknowledgeIEs_t *ies)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc;
|
||||
RANAP_Cause_t cause;
|
||||
int rc;
|
||||
|
||||
rnc = iu_rnc_find_by_addr(&ud_prim->calling_addr);
|
||||
if (!rnc)
|
||||
goto tx_err_ind;
|
||||
|
||||
rc = osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_RX_RESET_ACK, NULL);
|
||||
if (rc != 0)
|
||||
goto tx_err_ind;
|
||||
|
||||
return 0;
|
||||
|
||||
tx_err_ind:
|
||||
cause = (RANAP_Cause_t){
|
||||
.present = RANAP_Cause_PR_protocol,
|
||||
.choice.protocol = RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
|
||||
};
|
||||
return sgsn_ranap_iu_tx_error_ind(scu_iups, &ud_prim->calling_addr, &cause);
|
||||
}
|
||||
|
||||
static int ranap_handle_cl_err_ind(struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_scu_unitdata_param *ud_prim,
|
||||
const RANAP_ErrorIndicationIEs_t *ies)
|
||||
{
|
||||
if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CAUSE_PRESENT)
|
||||
LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication (%s)\n",
|
||||
ranap_cause_str(&ies->cause));
|
||||
else
|
||||
LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Entry point for connection-less RANAP message */
|
||||
static void cn_ranap_handle_cl(struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_scu_unitdata_param *ud_prim,
|
||||
const ranap_message *message)
|
||||
{
|
||||
int rc;
|
||||
|
||||
switch (message->direction) {
|
||||
case RANAP_RANAP_PDU_PR_initiatingMessage:
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_Reset:
|
||||
/* received reset.req, send reset.resp */
|
||||
rc = ranap_handle_cl_reset_req(scu_iups, ud_prim, &message->msg.resetIEs);
|
||||
break;
|
||||
case RANAP_ProcedureCode_id_ErrorIndication:
|
||||
rc = ranap_handle_cl_err_ind(scu_iups, ud_prim, &message->msg.errorIndicationIEs);
|
||||
break;
|
||||
default:
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case RANAP_RANAP_PDU_PR_successfulOutcome:
|
||||
switch (message->procedureCode) {
|
||||
case RANAP_ProcedureCode_id_Reset:
|
||||
rc = ranap_handle_cl_reset_ack(scu_iups, ud_prim, &message->msg.resetAcknowledgeIEs);
|
||||
break;
|
||||
default:
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
|
||||
case RANAP_RANAP_PDU_PR_outcome:
|
||||
default:
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Error in %s (%d)\n", __func__, rc);
|
||||
/* TODO handling of the error? */
|
||||
}
|
||||
}
|
||||
|
||||
int sgsn_ranap_iu_rx_cl_msg(struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_scu_unitdata_param *ud_prim,
|
||||
const uint8_t *data, size_t len)
|
||||
{
|
||||
ranap_message message;
|
||||
int rc;
|
||||
|
||||
rc = ranap_cn_rx_cl_decode2(&message, data, len);
|
||||
if (rc != 0) {
|
||||
LOGP(DRANAP, LOGL_ERROR, "Not calling cn_ranap_handle_cl() due to rc=%d\n", rc);
|
||||
goto free_ret;
|
||||
}
|
||||
|
||||
cn_ranap_handle_cl(scu_iups, ud_prim, &message);
|
||||
|
||||
free_ret:
|
||||
/* Free the asn1 structs in message */
|
||||
ranap_cn_rx_cl_free(&message);
|
||||
return rc;
|
||||
}
|
||||
|
||||
510
src/sgsn/gprs_routing_area.c
Normal file
510
src/sgsn/gprs_routing_area.c
Normal file
@@ -0,0 +1,510 @@
|
||||
/* SGSN Routing Area for 2G */
|
||||
|
||||
/* (C) 2024 sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* Author: Alexander Couzens <lynxis@fe80.eu>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/gprs_bssgp.h>
|
||||
#include <osmocom/sgsn/mmctx.h>
|
||||
#include <osmocom/sgsn/sgsn.h>
|
||||
|
||||
#include <osmocom/sgsn/gprs_routing_area.h>
|
||||
|
||||
const struct value_string sgsn_ra_ran_type_names[] = {
|
||||
{ RA_TYPE_GERAN_Gb, "GERAN_Gb" },
|
||||
{ RA_TYPE_UTRAN_Iu, "UTRAN_Iu" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
static void _sgsn_ra_cell_free(struct sgsn_ra_cell *cell, bool drop_empty_ra)
|
||||
{
|
||||
struct sgsn_ra *ra;
|
||||
|
||||
if (!cell)
|
||||
return;
|
||||
|
||||
llist_del(&cell->list);
|
||||
/* to prevent double free of the Cell when freeing a Routing Area */
|
||||
if (!drop_empty_ra) {
|
||||
talloc_free(cell);
|
||||
return;
|
||||
}
|
||||
|
||||
ra = cell->ra;
|
||||
talloc_free(cell);
|
||||
|
||||
if (llist_empty(&ra->cells_alive_list))
|
||||
sgsn_ra_free(ra);
|
||||
}
|
||||
|
||||
void sgsn_ra_cell_free(struct sgsn_ra_cell *cell)
|
||||
{
|
||||
_sgsn_ra_cell_free(cell, true);
|
||||
}
|
||||
|
||||
void sgsn_ra_free(struct sgsn_ra *ra)
|
||||
{
|
||||
struct sgsn_ra_cell *cell, *cell2;
|
||||
|
||||
if (!ra)
|
||||
return;
|
||||
|
||||
llist_for_each_entry_safe(cell, cell2, &ra->cells_alive_list, list) {
|
||||
_sgsn_ra_cell_free(cell, false);
|
||||
}
|
||||
|
||||
llist_del(&ra->list);
|
||||
talloc_free(ra);
|
||||
}
|
||||
|
||||
struct sgsn_ra *sgsn_ra_alloc(const struct osmo_routing_area_id *rai, enum sgsn_ra_ran_type ran_type)
|
||||
{
|
||||
struct sgsn_ra *ra;
|
||||
ra = talloc_zero(sgsn->routing_area, struct sgsn_ra);
|
||||
if (!ra)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&ra->cells_alive_list);
|
||||
ra->rai = *rai;
|
||||
ra->ran_type = ran_type;
|
||||
llist_add(&ra->list, &sgsn->routing_area->ra_list);
|
||||
return ra;
|
||||
}
|
||||
|
||||
struct sgsn_ra *sgsn_ra_find_or_create(const struct osmo_routing_area_id *rai, enum sgsn_ra_ran_type ran_type)
|
||||
{
|
||||
struct sgsn_ra *ra = sgsn_ra_get_ra(rai);
|
||||
|
||||
if (ra) {
|
||||
if (ra->ran_type == ran_type) {
|
||||
LOGRAI(LOGL_ERROR, rai, "Trying to create an already existing RA with the same ran type %s. Using old RA.\n",
|
||||
get_value_string(sgsn_ra_ran_type_names, ra->ran_type));
|
||||
|
||||
return ra;
|
||||
}
|
||||
|
||||
LOGRAI(LOGL_ERROR, rai, "Failed to create an already existing RA with a different ran type %s.\n",
|
||||
get_value_string(sgsn_ra_ran_type_names, ra->ran_type));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sgsn_ra_alloc(rai, ran_type);
|
||||
}
|
||||
|
||||
struct sgsn_ra_cell *sgsn_ra_cell_alloc_geran(struct sgsn_ra *ra, uint16_t cell_id, uint16_t nsei, uint16_t bvci)
|
||||
{
|
||||
struct sgsn_ra_cell *cell;
|
||||
|
||||
cell = talloc_zero(ra, struct sgsn_ra_cell);
|
||||
if (!cell)
|
||||
return NULL;
|
||||
|
||||
cell->ra = ra;
|
||||
cell->ran_type = RA_TYPE_GERAN_Gb;
|
||||
cell->u.geran.cell_id = cell_id;
|
||||
cell->u.geran.bvci = bvci;
|
||||
cell->u.geran.nsei = nsei;
|
||||
|
||||
llist_add(&cell->list, &ra->cells_alive_list);
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
struct sgsn_ra *sgsn_ra_get_ra(const struct osmo_routing_area_id *rai)
|
||||
{
|
||||
struct sgsn_ra *ra;
|
||||
|
||||
llist_for_each_entry(ra, &sgsn->routing_area->ra_list, list)
|
||||
if (osmo_rai_cmp(&ra->rai, rai) == 0)
|
||||
return ra;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct sgsn_ra *sgsn_ra_geran_get_ra(const struct osmo_routing_area_id *rai)
|
||||
{
|
||||
struct sgsn_ra *ra = sgsn_ra_get_ra(rai);
|
||||
|
||||
if (!ra)
|
||||
return ra;
|
||||
|
||||
if (ra->ran_type == RA_TYPE_GERAN_Gb)
|
||||
return ra;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_gb(uint16_t nsei, uint16_t bvci)
|
||||
{
|
||||
struct sgsn_ra *ra;
|
||||
struct sgsn_ra_cell *cell;
|
||||
|
||||
/* BVCI = 0 is invalid, only valid for signalling within the BSSGP, not for a single cell */
|
||||
if (bvci == 0)
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(ra, &sgsn->routing_area->ra_list, list) {
|
||||
if (ra->ran_type != RA_TYPE_GERAN_Gb)
|
||||
continue;
|
||||
|
||||
llist_for_each_entry(cell, &ra->cells_alive_list, list) {
|
||||
if (cell->ran_type != RA_TYPE_GERAN_Gb)
|
||||
continue;
|
||||
|
||||
if (cell->u.geran.bvci == bvci && cell->u.geran.nsei == nsei)
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct sgsn_ra *sgsn_ra_utran_get_ra(const struct osmo_routing_area_id *ra_id)
|
||||
{
|
||||
struct sgsn_ra *ra = sgsn_ra_get_ra(ra_id);
|
||||
|
||||
if (!ra)
|
||||
return ra;
|
||||
|
||||
if (ra->ran_type == RA_TYPE_UTRAN_Iu)
|
||||
return ra;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int sgsn_ra_foreach_cell(struct sgsn_ra *ra, sgsn_ra_cb_t *cb, void *cb_data)
|
||||
{
|
||||
struct sgsn_ra_cell *cell, *tmp;
|
||||
int ret = -ENOENT;
|
||||
|
||||
OSMO_ASSERT(cb);
|
||||
|
||||
llist_for_each_entry_safe(cell, tmp, &ra->cells_alive_list, list) {
|
||||
ret = cb(cell, cb_data);
|
||||
switch (ret) {
|
||||
case SGSN_RA_CB_CONT:
|
||||
continue;
|
||||
case SGSN_RA_CB_STOP:
|
||||
return 0;
|
||||
case SGSN_RA_CB_ERROR:
|
||||
return -1;
|
||||
default:
|
||||
OSMO_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sgsn_ra_foreach_cell2(struct osmo_routing_area_id *rai, sgsn_ra_cb_t *cb, void *cb_data)
|
||||
{
|
||||
struct sgsn_ra *ra;
|
||||
OSMO_ASSERT(rai);
|
||||
OSMO_ASSERT(cb);
|
||||
|
||||
ra = sgsn_ra_get_ra(rai);
|
||||
if (!ra)
|
||||
return -ENOENT;
|
||||
|
||||
return sgsn_ra_foreach_cell(ra, cb, cb_data);
|
||||
}
|
||||
|
||||
/* valid for GERAN */
|
||||
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_ra(const struct sgsn_ra *ra, uint16_t cell_id)
|
||||
{
|
||||
struct sgsn_ra_cell *cell;
|
||||
|
||||
if (ra->ran_type != RA_TYPE_GERAN_Gb)
|
||||
return NULL;
|
||||
|
||||
llist_for_each_entry(cell, &ra->cells_alive_list, list) {
|
||||
if (cell->ran_type != RA_TYPE_GERAN_Gb)
|
||||
continue;
|
||||
|
||||
if (cell->u.geran.cell_id == cell_id)
|
||||
return cell;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* valid for GERAN */
|
||||
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_lai(const struct osmo_location_area_id *lai, uint16_t cell_id)
|
||||
{
|
||||
struct sgsn_ra *ra;
|
||||
struct sgsn_ra_cell *cell;
|
||||
|
||||
/* This is a little bit in-efficient. A more performance way, but more complex would
|
||||
* adding a llist for LAC on top of the routing areas */
|
||||
llist_for_each_entry(ra, &sgsn->routing_area->ra_list, list) {
|
||||
if (ra->ran_type != RA_TYPE_GERAN_Gb)
|
||||
continue;
|
||||
|
||||
if (osmo_lai_cmp(&ra->rai.lac, lai) != 0)
|
||||
continue;
|
||||
|
||||
llist_for_each_entry(cell, &ra->cells_alive_list, list) {
|
||||
if (cell->ran_type != RA_TYPE_GERAN_Gb)
|
||||
continue;
|
||||
|
||||
if (cell->u.geran.cell_id == cell_id)
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Return the GERAN cell by searching for the RA, when found, search the cell within the RA
|
||||
*
|
||||
* \param cgi_ps
|
||||
* \return the cell or NULL if not found
|
||||
*/
|
||||
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_cgi_ps(const struct osmo_cell_global_id_ps *cgi_ps)
|
||||
{
|
||||
struct sgsn_ra *ra;
|
||||
|
||||
OSMO_ASSERT(cgi_ps);
|
||||
|
||||
ra = sgsn_ra_geran_get_ra(&cgi_ps->rai);
|
||||
if (!ra)
|
||||
return NULL;
|
||||
|
||||
return sgsn_ra_geran_get_cell_by_ra(ra, cgi_ps->cell_identity);
|
||||
}
|
||||
|
||||
struct sgsn_ra_cell *sgsn_ra_geran_get_cell_by_cgi(const struct osmo_cell_global_id *cgi)
|
||||
{
|
||||
OSMO_ASSERT(cgi);
|
||||
|
||||
return sgsn_ra_geran_get_cell_by_lai(&cgi->lai, cgi->cell_identity);
|
||||
}
|
||||
|
||||
/*! Callback from the BSSGP layer on NM RESET IND for a cell (PtP BVCI).
|
||||
*
|
||||
* \param nsei
|
||||
* \param bvci
|
||||
* \param cgi_ps
|
||||
* \return 0 on success or -ENOMEM
|
||||
*/
|
||||
int sgsn_ra_geran_bvc_cell_reset_ind(uint16_t nsei, uint16_t bvci, struct osmo_cell_global_id_ps *cgi_ps)
|
||||
{
|
||||
struct sgsn_ra *ra;
|
||||
struct sgsn_ra_cell *cell;
|
||||
bool ra_created = false;
|
||||
OSMO_ASSERT(cgi_ps);
|
||||
|
||||
/* TODO: do we have to move all MS to GMM IDLE state when this happens for a alive cell which got reseted? */
|
||||
ra = sgsn_ra_geran_get_ra(&cgi_ps->rai);
|
||||
if (!ra) {
|
||||
/* TODO: check for UTRAN rai when introducing UTRAN support */
|
||||
ra = sgsn_ra_find_or_create(&cgi_ps->rai, RA_TYPE_GERAN_Gb);
|
||||
if (!ra)
|
||||
return -ENOMEM;
|
||||
ra_created = true;
|
||||
}
|
||||
|
||||
if (!ra_created) {
|
||||
cell = sgsn_ra_geran_get_cell_by_ra(ra, cgi_ps->cell_identity);
|
||||
if (cell && cell->ran_type == RA_TYPE_GERAN_Gb) {
|
||||
/* Cell already exist, update NSEI/BVCI */
|
||||
if (cell->u.geran.bvci != bvci || cell->u.geran.nsei != nsei) {
|
||||
LOGRA(LOGL_INFO, ra, "GERAN Cell changed DLCI. Old: nsei/bvci %05u/%05u New: nsei/bvci %05u/%05u\n",
|
||||
cell->u.geran.nsei, cell->u.geran.bvci, nsei, bvci);
|
||||
cell->u.geran.bvci = bvci;
|
||||
cell->u.geran.nsei = nsei;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cell && cell->ran_type != RA_TYPE_GERAN_Gb) {
|
||||
/* How can we have here a RA change? Must be a configuration error. */
|
||||
LOGRAI(LOGL_INFO, &cgi_ps->rai, "CGI %s: RAN change detected to GERAN!", osmo_cgi_ps_name(cgi_ps));
|
||||
_sgsn_ra_cell_free(cell, false);
|
||||
cell = NULL;
|
||||
}
|
||||
|
||||
if (!cell) {
|
||||
char old_ra[32];
|
||||
char new_ra[32];
|
||||
/* check for the same cell id within the location area. The cell id is also unique for the cell within the LAC
|
||||
* This should only happen when a Cell is changing routing areas */
|
||||
cell = sgsn_ra_geran_get_cell_by_lai(&cgi_ps->rai.lac, cgi_ps->cell_identity);
|
||||
if (cell) {
|
||||
LOGRAI(LOGL_INFO, &cgi_ps->rai, "CGI %s: changed Routing Area. Old: %s, New: %s\n",
|
||||
osmo_cgi_ps_name(cgi_ps),
|
||||
osmo_rai_name2_buf(old_ra, sizeof(old_ra), &cell->ra->rai),
|
||||
osmo_rai_name2_buf(new_ra, sizeof(new_ra), &cgi_ps->rai));
|
||||
|
||||
OSMO_ASSERT(cell->ra != ra);
|
||||
|
||||
/* the old RA is definitive not our ra! Drop the old ra */
|
||||
_sgsn_ra_cell_free(cell, true);
|
||||
cell = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cell = sgsn_ra_cell_alloc_geran(ra, cgi_ps->cell_identity, nsei, bvci);
|
||||
if (!cell)
|
||||
return -ENOMEM;
|
||||
|
||||
LOGRA(LOGL_INFO, ra, "New cell registered %s via nsei/bvci %05u/%05u\n", osmo_cgi_ps_name(cgi_ps), nsei, bvci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: call it on BSSGP BLOCK + unavailable with BVCI */
|
||||
int sgsn_ra_geran_nsei_failure_ind(uint16_t nsei)
|
||||
{
|
||||
struct sgsn_ra *ra, *ra2;
|
||||
struct sgsn_ra_cell *cell, *cell2;
|
||||
bool found = false;
|
||||
|
||||
llist_for_each_entry_safe(ra, ra2, &sgsn->routing_area->ra_list, list) {
|
||||
if (ra->ran_type != RA_TYPE_GERAN_Gb)
|
||||
continue;
|
||||
|
||||
llist_for_each_entry_safe(cell, cell2, &ra->cells_alive_list, list) {
|
||||
if (cell->ran_type != RA_TYPE_GERAN_Gb)
|
||||
continue;
|
||||
|
||||
if (cell->u.geran.nsei == nsei) {
|
||||
found = true;
|
||||
_sgsn_ra_cell_free(cell, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (llist_empty(&ra->cells_alive_list))
|
||||
sgsn_ra_free(ra);
|
||||
}
|
||||
|
||||
return found ? 0 : -ENOENT;
|
||||
}
|
||||
|
||||
void sgsn_ra_geran_bvc_sign_reset_ind(uint16_t nsei)
|
||||
{
|
||||
sgsn_ra_geran_nsei_failure_ind(nsei);
|
||||
}
|
||||
|
||||
int sgsn_ra_geran_page_ra(const struct osmo_routing_area_id *rai, struct sgsn_mm_ctx *mmctx)
|
||||
{
|
||||
struct sgsn_ra *ra;
|
||||
struct sgsn_ra_cell *cell;
|
||||
int ret = -ENOENT;
|
||||
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PAGING_PS));
|
||||
|
||||
ra = sgsn_ra_geran_get_ra(rai);
|
||||
if (!ra)
|
||||
return -ENOENT;
|
||||
|
||||
llist_for_each_entry(cell, &ra->cells_alive_list, list) {
|
||||
if (cell->ran_type == RA_TYPE_GERAN_Gb) {
|
||||
sgsn_bssgp_page_ps_bvci(mmctx, cell->u.geran.nsei, cell->u.geran.bvci);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef BUILD_IU
|
||||
/* Register a new UTRAN Routing Area if possible.
|
||||
* Return 0 on success and < 0 on failure. */
|
||||
int sgsn_ra_utran_register(const struct osmo_routing_area_id *rai, const struct osmo_rnc_id *rnc_id)
|
||||
{
|
||||
struct sgsn_ra *ra = sgsn_ra_get_ra(rai);
|
||||
if (!ra) {
|
||||
ra = sgsn_ra_alloc(rai, RA_TYPE_UTRAN_Iu);
|
||||
if (!ra) {
|
||||
LOGP(DRA, LOGL_ERROR, "Couldn't create new RA for %s ran type %s\n",
|
||||
osmo_rai_name2(rai), get_value_string(sgsn_ra_ran_type_names, ra->ran_type));
|
||||
return -ENOMEM;
|
||||
}
|
||||
ra->u.utran.rnc_id = *rnc_id;
|
||||
LOGRA(LOGL_INFO, ra, "New UTRAN RA by RNC %s\n", osmo_rnc_id_name(&ra->u.utran.rnc_id));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ra->ran_type == RA_TYPE_GERAN_Gb) {
|
||||
LOGRA(LOGL_ERROR, ra, "rejecting new RA of type %s, because already present RA has ran type %s\n",
|
||||
get_value_string(sgsn_ra_ran_type_names, RA_TYPE_UTRAN_Iu),
|
||||
get_value_string(sgsn_ra_ran_type_names, ra->ran_type));
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* RA already known */
|
||||
if (osmo_rnc_id_cmp(&ra->u.utran.rnc_id, rnc_id) == 0)
|
||||
return 0;
|
||||
|
||||
/* RNC id changed */
|
||||
char new_rnc_id_name[32];
|
||||
osmo_rnc_id_name_buf(new_rnc_id_name, sizeof(new_rnc_id_name), rnc_id);
|
||||
LOGRA(LOGL_INFO, ra, "RNC Id changed from %s to %s\n",
|
||||
osmo_rnc_id_name(&ra->u.utran.rnc_id), new_rnc_id_name);
|
||||
|
||||
ra->u.utran.rnc_id = *rnc_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sgsn_ra_utran_page_ra(const struct osmo_routing_area_id *ra_id, const struct sgsn_mm_ctx *mmctx)
|
||||
{
|
||||
struct sgsn_ra *ra;
|
||||
rate_ctr_inc(rate_ctr_group_get_ctr(mmctx->ctrg, GMM_CTR_PAGING_PS));
|
||||
|
||||
ra = sgsn_ra_utran_get_ra(ra_id);
|
||||
if (!ra)
|
||||
return -ENOENT;
|
||||
|
||||
/* Try to page by TMSI if possible */
|
||||
if (mmctx->p_tmsi != GSM_RESERVED_TMSI)
|
||||
return ranap_iu_page_ps2(mmctx->imsi, &mmctx->p_tmsi, ra_id);
|
||||
if (mmctx->p_tmsi_old != GSM_RESERVED_TMSI)
|
||||
return ranap_iu_page_ps2(mmctx->imsi, &mmctx->p_tmsi_old, ra_id);
|
||||
|
||||
/* Page by IMSI */
|
||||
return ranap_iu_page_ps2(mmctx->imsi, NULL, ra_id);
|
||||
}
|
||||
#else
|
||||
int sgsn_ra_utran_page_ra(const struct osmo_routing_area_id *ra_id, const struct sgsn_mm_ctx *mmctx)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
int sgsn_ra_utran_register(const struct osmo_routing_area_id *rai, const struct osmo_rnc_id *rnc_id)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif /* BUILD_IU */
|
||||
|
||||
void sgsn_ra_init(struct sgsn_instance *inst)
|
||||
{
|
||||
inst->routing_area = talloc_zero(inst, struct sgsn_ra_global);
|
||||
OSMO_ASSERT(inst->routing_area);
|
||||
|
||||
INIT_LLIST_HEAD(&inst->routing_area->ra_list);
|
||||
}
|
||||
@@ -114,9 +114,14 @@ static void pdpctx_timer_start(struct sgsn_pdp_ctx *pdp, unsigned int T)
|
||||
|
||||
static void pdpctx_timer_stop(struct sgsn_pdp_ctx *pdp, unsigned int T)
|
||||
{
|
||||
if (pdp->T != T)
|
||||
if (!osmo_timer_pending(&pdp->timer)) {
|
||||
LOGPDPCTXP(LOGL_NOTICE, pdp, "Stopping *inactive* PDP timer %u\n", T);
|
||||
return;
|
||||
}
|
||||
if (pdp->T != T) {
|
||||
LOGPDPCTXP(LOGL_ERROR, pdp, "Stopping PDP timer %u but "
|
||||
"%u is running\n", T, pdp->T);
|
||||
}
|
||||
osmo_timer_del(&pdp->timer);
|
||||
}
|
||||
|
||||
|
||||
@@ -834,9 +834,9 @@ int sndcp_ll_unitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle,
|
||||
return -EIO;
|
||||
}
|
||||
/* FIXME: move this RA_ID up to the LLME or even higher */
|
||||
bssgp_parse_cell_id(&sne->ra_id, msgb_bcid(msg));
|
||||
bssgp_parse_cell_id2(&sne->rai, NULL, msgb_bcid(msg), 8);
|
||||
|
||||
mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &sne->ra_id);
|
||||
mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &sne->rai);
|
||||
if (!mmctx) {
|
||||
LOGP(DSNDCP, LOGL_ERROR, "Message for non-existing MM ctx "
|
||||
"(lle=%p, TLLI=%08x, SAPI=%u, NSAPI=%u)\n",
|
||||
@@ -895,7 +895,7 @@ int sndcp_sn_unitdata_ind(struct gprs_sndcp_entity *sne,
|
||||
struct msgb *msg, uint32_t npdu_len, uint8_t *npdu)
|
||||
{
|
||||
/* Hand it off N-PDU to the correct GTP tunnel + GGSN: */
|
||||
return sgsn_gtp_data_req(&sne->ra_id, sne->lle->llme->tlli,
|
||||
return sgsn_gtp_data_req(&sne->rai, sne->lle->llme->tlli,
|
||||
sne->nsapi, msg, npdu_len, npdu);
|
||||
}
|
||||
|
||||
|
||||
@@ -376,7 +376,8 @@ static void gprs_subscr_gsup_insert_data(struct gprs_subscr *subscr,
|
||||
}
|
||||
|
||||
OSMO_ASSERT(pdp_data != NULL);
|
||||
pdp_data->pdp_type = pdp_info->pdp_type;
|
||||
pdp_data->pdp_type_org = pdp_info->pdp_type_org;
|
||||
pdp_data->pdp_type_nr = pdp_info->pdp_type_nr;
|
||||
osmo_apn_to_str(pdp_data->apn_str,
|
||||
pdp_info->apn_enc, pdp_info->apn_enc_len);
|
||||
|
||||
@@ -876,8 +877,8 @@ struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx(struct sgsn_mm_ctx *mmctx
|
||||
|
||||
osmo_strlcpy(subscr->imei, mmctx->imei, sizeof(subscr->imei));
|
||||
|
||||
if (subscr->lac != mmctx->ra.lac)
|
||||
subscr->lac = mmctx->ra.lac;
|
||||
if (subscr->lac != mmctx->ra.lac.lac)
|
||||
subscr->lac = mmctx->ra.lac.lac;
|
||||
|
||||
subscr->sgsn_data->mm = mmctx;
|
||||
mmctx->subscr = gprs_subscr_get(subscr);
|
||||
|
||||
@@ -131,6 +131,16 @@ struct sgsn_mme_ctx *sgsn_mme_ctx_by_route(const struct sgsn_instance *sgsn, con
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct sgsn_mme_ctx *sgsn_mme_ctx_by_gummei(const struct sgsn_instance *sgsn, const struct osmo_gummei *gummei)
|
||||
{
|
||||
struct sgsn_mme_ctx *mme;
|
||||
llist_for_each_entry(mme, &sgsn->mme_list, list) {
|
||||
if (mme->gummei_valid && !osmo_gummei_cmp(&mme->gummei, gummei))
|
||||
return mme;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct sgsn_mme_ctx *sgsn_mme_ctx_by_default_route(const struct sgsn_instance *sgsn)
|
||||
{
|
||||
struct sgsn_mme_ctx *mme;
|
||||
|
||||
295
src/sgsn/iu_client.c
Normal file
295
src/sgsn/iu_client.c
Normal file
@@ -0,0 +1,295 @@
|
||||
/* Common parts of IuCS and IuPS interfaces implementation */
|
||||
|
||||
/* (C) 2016-2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/crypt/auth.h>
|
||||
#include <osmocom/gprs/gprs_msgb.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/gprs_ranap.h>
|
||||
#include <osmocom/sgsn/iu_client.h>
|
||||
#include <osmocom/sgsn/iu_rnc.h>
|
||||
#include <osmocom/sgsn/sccp.h>
|
||||
#include <osmocom/sgsn/sgsn.h>
|
||||
|
||||
const struct value_string iu_client_event_type_names[] = {
|
||||
OSMO_VALUE_STRING(RANAP_IU_EVENT_RAB_ASSIGN),
|
||||
OSMO_VALUE_STRING(RANAP_IU_EVENT_SECURITY_MODE_COMPLETE),
|
||||
OSMO_VALUE_STRING(RANAP_IU_EVENT_IU_RELEASE),
|
||||
OSMO_VALUE_STRING(RANAP_IU_EVENT_LINK_INVALIDATED),
|
||||
OSMO_VALUE_STRING(RANAP_IU_EVENT_NEW_AREA),
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
int global_iu_event(struct ranap_ue_conn_ctx *ue_ctx,
|
||||
enum ranap_iu_event_type type,
|
||||
void *data)
|
||||
{
|
||||
|
||||
if (ue_ctx && !ue_ctx->notification)
|
||||
return 0;
|
||||
|
||||
LOGP(DRANAP, LOGL_DEBUG, "Submit Iu event to upper layer: %s\n", iu_client_event_type_str(type));
|
||||
|
||||
return sgsn_ranap_iu_event(ue_ctx, type, data);
|
||||
}
|
||||
|
||||
|
||||
static void ue_conn_ctx_release_timeout_cb(void *ctx_)
|
||||
{
|
||||
struct ranap_ue_conn_ctx *ctx = (struct ranap_ue_conn_ctx *)ctx_;
|
||||
global_iu_event(ctx, RANAP_IU_EVENT_IU_RELEASE, NULL);
|
||||
}
|
||||
|
||||
struct ranap_ue_conn_ctx *ue_conn_ctx_alloc(struct ranap_iu_rnc *rnc, uint32_t conn_id)
|
||||
{
|
||||
struct ranap_ue_conn_ctx *ctx = talloc_zero(sgsn, struct ranap_ue_conn_ctx);
|
||||
|
||||
ctx->rnc = rnc;
|
||||
ctx->conn_id = conn_id;
|
||||
ctx->notification = true;
|
||||
ctx->free_on_release = false;
|
||||
osmo_timer_setup(&ctx->release_timeout,
|
||||
ue_conn_ctx_release_timeout_cb,
|
||||
ctx);
|
||||
llist_add(&ctx->list, &rnc->scu_iups->ue_conn_ctx_list);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void sgsn_ranap_iu_free_ue(struct ranap_ue_conn_ctx *ue_ctx)
|
||||
{
|
||||
if (!ue_ctx)
|
||||
return;
|
||||
|
||||
osmo_timer_del(&ue_ctx->release_timeout);
|
||||
osmo_sccp_tx_disconn(ue_ctx->rnc->scu_iups->scu, ue_ctx->conn_id, NULL, 0);
|
||||
llist_del(&ue_ctx->list);
|
||||
talloc_free(ue_ctx);
|
||||
}
|
||||
|
||||
void ue_conn_ctx_link_invalidated_free(struct ranap_ue_conn_ctx *ue)
|
||||
{
|
||||
uint32_t conn_id = ue->conn_id;
|
||||
struct sgsn_sccp_user_iups *scu_iups = ue->rnc->scu_iups;
|
||||
|
||||
global_iu_event(ue, RANAP_IU_EVENT_LINK_INVALIDATED, NULL);
|
||||
|
||||
/* A RANAP_IU_EVENT_LINK_INVALIDATED, can lead to a free */
|
||||
ue = sgsn_scu_iups_ue_conn_ctx_find(scu_iups, conn_id);
|
||||
if (!ue)
|
||||
return;
|
||||
if (ue->free_on_release)
|
||||
sgsn_ranap_iu_free_ue(ue);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* Paging
|
||||
***********************************************************************/
|
||||
|
||||
/* legacy, do a first match with ignoring PLMN */
|
||||
static struct ranap_iu_rnc *iu_rnc_lac_rac_find_legacy(uint16_t lac, uint8_t rac)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc;
|
||||
struct iu_lac_rac_entry *e;
|
||||
|
||||
llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
|
||||
llist_for_each_entry(e, &rnc->lac_rac_list, entry) {
|
||||
if (e->rai.lac.lac == lac && e->rai.rac == rac)
|
||||
return rnc;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Old paging() doesn't use PLMN and transmit paging command only to the first RNC */
|
||||
int ranap_iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc;
|
||||
char log_msg[32] = {};
|
||||
int rc;
|
||||
|
||||
if (tmsi)
|
||||
snprintf(log_msg, sizeof(log_msg), "TMSI %08x\n", *tmsi);
|
||||
else
|
||||
snprintf(log_msg, sizeof(log_msg), "IMSI %s\n", imsi);
|
||||
|
||||
rnc = iu_rnc_lac_rac_find_legacy(lac, 0);
|
||||
if (!rnc) {
|
||||
LOGP(DRANAP, LOGL_INFO, "Found no RNC to Page CS on LAC %u for %s",
|
||||
lac, log_msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = iu_rnc_tx_paging_cmd(rnc, imsi, tmsi, false, 0);
|
||||
if (rc != 0) {
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Failed to tx Paging CS for LAC %u for %s",
|
||||
lac, log_msg);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*! Old paging() doesn't use PLMN and transmit paging command only to the first RNC */
|
||||
int ranap_iu_page_ps(const char *imsi, const uint32_t *ptmsi, uint16_t lac, uint8_t rac)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc;
|
||||
char log_msg[32] = {};
|
||||
int rc;
|
||||
|
||||
if (ptmsi)
|
||||
snprintf(log_msg, sizeof(log_msg), "P-TMSI %08x\n", *ptmsi);
|
||||
else
|
||||
snprintf(log_msg, sizeof(log_msg), "IMSI %s\n", imsi);
|
||||
|
||||
rnc = iu_rnc_lac_rac_find_legacy(lac, rac);
|
||||
if (!rnc) {
|
||||
LOGP(DRANAP, LOGL_INFO, "Found no RNC to Page PS on LAC %u RAC %u for %s",
|
||||
lac, rac, log_msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = iu_rnc_tx_paging_cmd(rnc, imsi, ptmsi, true, 0);
|
||||
if (rc != 0) {
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Failed to tx Paging PS for LAC %u RAC %u for %s",
|
||||
lac, rac, log_msg);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*! Transmit a single page request towards all RNCs serving the specific LAI (no page retransmission).
|
||||
*
|
||||
* \param imsi the imsi as human readable string
|
||||
* \param tmsi NULL or pointer to the tmsi
|
||||
* \param lai full Location Area Identifier
|
||||
* \return amount of paged RNCs. 0 when no RNC found.
|
||||
*/
|
||||
int ranap_iu_page_cs2(const char *imsi, const uint32_t *tmsi, const struct osmo_location_area_id *lai)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc;
|
||||
struct iu_lac_rac_entry *entry;
|
||||
char log_msg[32] = {};
|
||||
unsigned int paged = 0;
|
||||
int rc;
|
||||
|
||||
if (tmsi)
|
||||
snprintf(log_msg, sizeof(log_msg), "TMSI %08x\n", *tmsi);
|
||||
else
|
||||
snprintf(log_msg, sizeof(log_msg), "IMSI %s\n", imsi);
|
||||
|
||||
/* find all RNCs which are serving this LA */
|
||||
llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
|
||||
llist_for_each_entry(entry, &rnc->lac_rac_list, entry) {
|
||||
if (osmo_lai_cmp(&entry->rai.lac, lai))
|
||||
continue;
|
||||
|
||||
rc = iu_rnc_tx_paging_cmd(rnc, imsi, tmsi, false, 0);
|
||||
if (rc != 0) {
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Failed to tx Paging CS for LAI %s for %s",
|
||||
osmo_lai_name(lai), log_msg);
|
||||
} else {
|
||||
paged++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (paged)
|
||||
LOGP(DRANAP, LOGL_DEBUG, "Paged CS %u RNCs on LAI %s for %s",
|
||||
paged, osmo_lai_name(lai), log_msg);
|
||||
else
|
||||
LOGP(DRANAP, LOGL_INFO, "Found no RNC to Page CS on LAI %s for %s",
|
||||
osmo_lai_name(lai), log_msg);
|
||||
|
||||
return paged;
|
||||
}
|
||||
|
||||
/*! Transmit a single page request towards all RNCs serving the specific RAI (no page retransmission).
|
||||
*
|
||||
* \param imsi the imsi as human readable string
|
||||
* \param ptmsi NULL or pointer to the ptmsi
|
||||
* \param rai full Location Area Identifier
|
||||
* \return amount of paged RNCs. 0 when no RNC found.
|
||||
*/
|
||||
int ranap_iu_page_ps2(const char *imsi, const uint32_t *ptmsi, const struct osmo_routing_area_id *rai)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc;
|
||||
struct iu_lac_rac_entry *entry;
|
||||
char log_msg[32] = {};
|
||||
unsigned int paged = 0;
|
||||
int rc;
|
||||
|
||||
if (ptmsi)
|
||||
snprintf(log_msg, sizeof(log_msg), "P-TMSI %08x\n", *ptmsi);
|
||||
else
|
||||
snprintf(log_msg, sizeof(log_msg), "IMSI %s\n", imsi);
|
||||
|
||||
/* find all RNCs which are serving this RAC */
|
||||
llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
|
||||
llist_for_each_entry(entry, &rnc->lac_rac_list, entry) {
|
||||
if (osmo_rai_cmp(&entry->rai, rai))
|
||||
continue;
|
||||
|
||||
rc = iu_rnc_tx_paging_cmd(rnc, imsi, ptmsi, true, 0);
|
||||
if (rc != 0) {
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Failed to tx Paging PS for RAI %s for %s",
|
||||
osmo_rai_name2(rai), log_msg);
|
||||
} else {
|
||||
paged++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (paged)
|
||||
LOGP(DRANAP, LOGL_DEBUG, "Paged PS %u RNCs on RAI %s for %s",
|
||||
paged, osmo_rai_name2(rai), log_msg);
|
||||
else
|
||||
LOGP(DRANAP, LOGL_INFO, "Found no RNC to Page PS on RAI %s for %s",
|
||||
osmo_rai_name2(rai), log_msg);
|
||||
|
||||
return paged;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
int ranap_iu_init(void *ctx)
|
||||
{
|
||||
talloc_asn1_ctx = talloc_named_const(ctx, 1, "asn1");
|
||||
return 0;
|
||||
}
|
||||
255
src/sgsn/iu_rnc.c
Normal file
255
src/sgsn/iu_rnc.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/* A remote RNC (Radio Network Controller), connected over IuPS */
|
||||
|
||||
/* (C) 2016-2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/crypt/auth.h>
|
||||
#include <osmocom/gprs/gprs_msgb.h>
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/gprs_ranap.h>
|
||||
#include <osmocom/sgsn/iu_client.h>
|
||||
#include <osmocom/sgsn/iu_rnc.h>
|
||||
#include <osmocom/sgsn/iu_rnc_fsm.h>
|
||||
#include <osmocom/sgsn/sccp.h>
|
||||
#include <osmocom/sgsn/sgsn.h>
|
||||
|
||||
static struct ranap_iu_rnc *iu_rnc_alloc(const struct osmo_rnc_id *rnc_id,
|
||||
struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_sccp_addr *rnc_sccp_addr)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc;
|
||||
char *addr_str, *pos;
|
||||
|
||||
rnc = talloc_zero(sgsn, struct ranap_iu_rnc);
|
||||
OSMO_ASSERT(rnc);
|
||||
|
||||
INIT_LLIST_HEAD(&rnc->lac_rac_list);
|
||||
|
||||
rnc->rnc_id = *rnc_id;
|
||||
rnc->scu_iups = scu_iups;
|
||||
rnc->sccp_addr = *rnc_sccp_addr;
|
||||
|
||||
rnc->fi = osmo_fsm_inst_alloc(&iu_rnc_fsm, rnc, rnc, LOGL_INFO, NULL);
|
||||
OSMO_ASSERT(rnc->fi);
|
||||
|
||||
/* Unfortunately, osmo_sccp_inst_addr_name() returns "RI=SSN_PC,PC=0.24.1,SSN=BSSAP" but neither commas nor
|
||||
* full-stops are allowed as FSM inst id. Make it "RI-SSN_PC:PC-0-24-1:SSN-BSSAP". */
|
||||
addr_str = osmo_sccp_addr_dump(rnc_sccp_addr);
|
||||
for (pos = addr_str; *pos; pos++) {
|
||||
if (*pos == ',')
|
||||
*pos = ':';
|
||||
else if (*pos == '.' || *pos == '=')
|
||||
*pos = '-';
|
||||
}
|
||||
osmo_fsm_inst_update_id_f(rnc->fi, "RNC_ID-%s:%s",
|
||||
osmo_rnc_id_name(rnc_id), addr_str);
|
||||
|
||||
llist_add(&rnc->entry, &sgsn->rnc_list);
|
||||
sgsn_stat_inc(SGSN_STAT_IU_PEERS_TOTAL, 1);
|
||||
|
||||
LOGP(DRANAP, LOGL_NOTICE, "New RNC %s at %s\n",
|
||||
osmo_rnc_id_name(&rnc->rnc_id), osmo_sccp_addr_dump(rnc_sccp_addr));
|
||||
|
||||
return rnc;
|
||||
}
|
||||
|
||||
static struct ranap_iu_rnc *iu_rnc_find_by_id(const struct osmo_rnc_id *rnc_id)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc;
|
||||
llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
|
||||
if (!osmo_rnc_id_cmp(&rnc->rnc_id, rnc_id))
|
||||
return rnc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ranap_iu_rnc *iu_rnc_find_by_addr(const struct osmo_sccp_addr *rnc_sccp_addr)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc;
|
||||
llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
|
||||
if (osmo_sccp_addr_ri_cmp(rnc_sccp_addr, &rnc->sccp_addr))
|
||||
continue;
|
||||
return rnc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ranap_iu_rnc *iu_rnc_find_or_create(const struct osmo_rnc_id *rnc_id,
|
||||
struct sgsn_sccp_user_iups *scu_iups,
|
||||
const struct osmo_sccp_addr *addr)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc;
|
||||
|
||||
/* Make sure we know this rnc_id and that this SCCP address is in our records */
|
||||
rnc = iu_rnc_find_by_id(rnc_id);
|
||||
if (rnc) {
|
||||
if (!osmo_sccp_addr_ri_cmp(&rnc->sccp_addr, addr)) {
|
||||
LOGP(DRANAP, LOGL_NOTICE, "RNC %s changed its SCCP addr to %s\n",
|
||||
osmo_rnc_id_name(&rnc->rnc_id), osmo_sccp_addr_dump(addr));
|
||||
rnc->sccp_addr = *addr;
|
||||
}
|
||||
} else {
|
||||
rnc = iu_rnc_alloc(rnc_id, scu_iups, addr);
|
||||
}
|
||||
return rnc;
|
||||
}
|
||||
|
||||
/* Find a match for the given LAC (and RAC). For CS, pass rac as 0.
|
||||
* If rnc and lre pointers are not NULL, *rnc / *lre are set to NULL if no match is found, or to the
|
||||
* match if a match is found. Return true if a match is found. */
|
||||
static bool iu_rnc_lac_rac_find(struct ranap_iu_rnc **rnc, struct iu_lac_rac_entry **lre,
|
||||
const struct osmo_routing_area_id *ra_id)
|
||||
{
|
||||
struct ranap_iu_rnc *r;
|
||||
struct iu_lac_rac_entry *e;
|
||||
|
||||
if (rnc)
|
||||
*rnc = NULL;
|
||||
if (lre)
|
||||
*lre = NULL;
|
||||
|
||||
llist_for_each_entry(r, &sgsn->rnc_list, entry) {
|
||||
llist_for_each_entry(e, &r->lac_rac_list, entry) {
|
||||
if (!osmo_rai_cmp(&e->rai, ra_id)) {
|
||||
if (rnc)
|
||||
*rnc = r;
|
||||
if (lre)
|
||||
*lre = e;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void global_iu_event_new_area(const struct osmo_rnc_id *rnc_id, const struct osmo_routing_area_id *rai)
|
||||
{
|
||||
struct ranap_iu_event_new_area new_area = (struct ranap_iu_event_new_area) {
|
||||
.rnc_id = rnc_id,
|
||||
.cell_type = RANAP_IU_NEW_RAC
|
||||
};
|
||||
|
||||
if (rai->rac == OSMO_RESERVED_RAC) {
|
||||
new_area.cell_type = RANAP_IU_NEW_LAC;
|
||||
new_area.u.lai = &rai->lac;
|
||||
} else {
|
||||
new_area.cell_type = RANAP_IU_NEW_RAC;
|
||||
new_area.u.rai = rai;
|
||||
}
|
||||
|
||||
global_iu_event(NULL, RANAP_IU_EVENT_NEW_AREA, &new_area);
|
||||
}
|
||||
|
||||
|
||||
void iu_rnc_update_rai_seen(struct ranap_iu_rnc *rnc, const struct osmo_routing_area_id *rai)
|
||||
{
|
||||
struct ranap_iu_rnc *old_rnc;
|
||||
struct iu_lac_rac_entry *lre;
|
||||
|
||||
/* Detect whether the LAC,RAC is already recorded in another RNC */
|
||||
iu_rnc_lac_rac_find(&old_rnc, &lre, rai);
|
||||
|
||||
if (old_rnc && old_rnc != rnc) {
|
||||
/* LAC, RAC already exists in a different RNC */
|
||||
LOGP(DRANAP, LOGL_NOTICE, "LAC/RAC %s moved from RNC %s %s",
|
||||
osmo_rai_name2(rai),
|
||||
osmo_rnc_id_name(&old_rnc->rnc_id), osmo_sccp_addr_dump(&old_rnc->sccp_addr));
|
||||
LOGPC(DRANAP, LOGL_NOTICE, " to RNC %s %s\n",
|
||||
osmo_rnc_id_name(&rnc->rnc_id), osmo_sccp_addr_dump(&rnc->sccp_addr));
|
||||
|
||||
llist_del(&lre->entry);
|
||||
llist_add(&lre->entry, &rnc->lac_rac_list);
|
||||
global_iu_event_new_area(&rnc->rnc_id, rai);
|
||||
} else if (!old_rnc) {
|
||||
/* LAC, RAC not recorded yet */
|
||||
LOGP(DRANAP, LOGL_NOTICE, "RNC %s: new LAC/RAC %s\n",
|
||||
osmo_rnc_id_name(&rnc->rnc_id), osmo_rai_name2(rai));
|
||||
lre = talloc_zero(rnc, struct iu_lac_rac_entry);
|
||||
lre->rai = *rai;
|
||||
llist_add(&lre->entry, &rnc->lac_rac_list);
|
||||
global_iu_event_new_area(&rnc->rnc_id, rai);
|
||||
}
|
||||
/* else, LAC,RAC already recorded with the current RNC. */
|
||||
}
|
||||
|
||||
void iu_rnc_discard_all_ue_ctx(struct ranap_iu_rnc *rnc)
|
||||
{
|
||||
struct ranap_ue_conn_ctx *ue_ctx, *ue_ctx_tmp;
|
||||
|
||||
llist_for_each_entry_safe(ue_ctx, ue_ctx_tmp, &sgsn->sccp.scu_iups->ue_conn_ctx_list, list) {
|
||||
if (ue_ctx->rnc != rnc)
|
||||
continue;
|
||||
ue_conn_ctx_link_invalidated_free(ue_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send a paging command down a given SCCP User. tmsi_or_ptmsi and paging_cause are
|
||||
* optional and may be passed NULL and 0, respectively, to disable their use.
|
||||
* See enum RANAP_PagingCause.
|
||||
*
|
||||
* If tmsi_or_ptmsi is given, the imsi is not sent over the air interface.
|
||||
* Nevertheless, the IMSI is still required for resolution in the HNB-GW
|
||||
* and/or(?) RNC.
|
||||
*
|
||||
* returns negative if paging couldn't be sent (eg. because RNC is currently
|
||||
* unreachable in lower layers).
|
||||
**/
|
||||
int iu_rnc_tx_paging_cmd(struct ranap_iu_rnc *rnc,
|
||||
const char *imsi,
|
||||
const uint32_t *tmsi_or_ptmsi,
|
||||
bool is_ps,
|
||||
uint32_t paging_cause)
|
||||
{
|
||||
struct msgb *ranap_msg;
|
||||
int rc;
|
||||
|
||||
/* rnc is not ready for paging (link not ready). */
|
||||
if (rnc->fi->state != IU_RNC_ST_READY)
|
||||
return -ENOLINK;
|
||||
|
||||
LOG_RNC(rnc, LOGL_DEBUG, "Paging %s for %s=%08x IMSI=%s\n",
|
||||
is_ps ? "PS" : "CS",
|
||||
is_ps ? "P-TMSI" : "TMSI",
|
||||
tmsi_or_ptmsi ? *tmsi_or_ptmsi : GSM_RESERVED_TMSI,
|
||||
imsi);
|
||||
|
||||
ranap_msg = ranap_new_msg_paging_cmd(imsi, tmsi_or_ptmsi, is_ps ? 1 : 0, paging_cause);
|
||||
if (!ranap_msg)
|
||||
return -EINVAL;
|
||||
|
||||
rc = osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_MSG_DOWN_CL, ranap_msg);
|
||||
if (rc != 0)
|
||||
msgb_free(ranap_msg);
|
||||
return rc;
|
||||
}
|
||||
374
src/sgsn/iu_rnc_fsm.c
Normal file
374
src/sgsn/iu_rnc_fsm.c
Normal file
@@ -0,0 +1,374 @@
|
||||
/* A remote RNC (Radio Network Controller) FSM */
|
||||
|
||||
/* (C) 2025 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/fsm.h>
|
||||
#include <osmocom/core/tdef.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/gprs_ranap.h>
|
||||
#include <osmocom/sgsn/iu_rnc_fsm.h>
|
||||
#include <osmocom/sgsn/iu_rnc.h>
|
||||
#include <osmocom/sgsn/sgsn.h>
|
||||
|
||||
#define S(x) (1 << (x))
|
||||
|
||||
struct osmo_fsm iu_rnc_fsm;
|
||||
|
||||
|
||||
static const struct osmo_tdef_state_timeout iu_rnc_fsm_timeouts[32] = {
|
||||
[IU_RNC_ST_WAIT_RX_RESET_ACK] = { .T = -1002 },
|
||||
[IU_RNC_ST_DISCARDING] = { .T = -1002 },
|
||||
};
|
||||
|
||||
#define iu_rnc_state_chg(iu_rnc, next_st) \
|
||||
osmo_tdef_fsm_inst_state_chg((iu_rnc)->fi, next_st, iu_rnc_fsm_timeouts, sgsn_T_defs, 5)
|
||||
|
||||
static const struct value_string iu_rnc_fsm_event_names[] = {
|
||||
OSMO_VALUE_STRING(IU_RNC_EV_MSG_UP_CO_INITIAL),
|
||||
OSMO_VALUE_STRING(IU_RNC_EV_MSG_UP_CO),
|
||||
OSMO_VALUE_STRING(IU_RNC_EV_RX_RESET),
|
||||
OSMO_VALUE_STRING(IU_RNC_EV_RX_RESET_ACK),
|
||||
OSMO_VALUE_STRING(IU_RNC_EV_MSG_DOWN_CL),
|
||||
OSMO_VALUE_STRING(IU_RNC_EV_AVAILABLE),
|
||||
OSMO_VALUE_STRING(IU_RNC_EV_UNAVAILABLE),
|
||||
{}
|
||||
};
|
||||
|
||||
/* Drop all SCCP connections for this iu_rnc, respond with RESET ACKNOWLEDGE and move to READY state. */
|
||||
static void iu_rnc_rx_reset(struct ranap_iu_rnc *rnc)
|
||||
{
|
||||
struct msgb *reset_ack;
|
||||
struct iu_grnc_id grnc_id;
|
||||
sgsn_ranap_iu_grnc_id_compose(&grnc_id, &rnc->rnc_id);
|
||||
|
||||
iu_rnc_discard_all_ue_ctx(rnc);
|
||||
|
||||
reset_ack = ranap_new_msg_reset_ack(RANAP_CN_DomainIndicator_ps_domain, &grnc_id.grnc_id);
|
||||
if (!reset_ack) {
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Failed to compose RESET ACKNOWLEDGE message\n");
|
||||
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
|
||||
return;
|
||||
}
|
||||
if (sgsn_ranap_iu_tx_cl(rnc->scu_iups, &rnc->sccp_addr, reset_ack) < 0) {
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Failed to send RESET ACKNOWLEDGE message\n");
|
||||
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_RNC(rnc, LOGL_INFO, "Sent RESET ACKNOWLEDGE\n");
|
||||
iu_rnc_state_chg(rnc, IU_RNC_ST_READY);
|
||||
}
|
||||
|
||||
static void iu_rnc_reset(struct ranap_iu_rnc *rnc)
|
||||
{
|
||||
struct msgb *reset;
|
||||
const RANAP_Cause_t cause = {
|
||||
.present = RANAP_Cause_PR_protocol,
|
||||
.choice = {
|
||||
.protocol = RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
|
||||
},
|
||||
};
|
||||
|
||||
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET_ACK);
|
||||
iu_rnc_discard_all_ue_ctx(rnc);
|
||||
|
||||
reset = ranap_new_msg_reset(RANAP_CN_DomainIndicator_ps_domain, &cause);
|
||||
if (!reset) {
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Failed to compose RESET message\n");
|
||||
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sgsn_ranap_iu_tx_cl(rnc->scu_iups, &rnc->sccp_addr, reset) < 0) {
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Failed to send RESET message\n");
|
||||
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void iu_rnc_st_wait_rx_reset(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc = fi->priv;
|
||||
switch (event) {
|
||||
|
||||
case IU_RNC_EV_MSG_UP_CO:
|
||||
case IU_RNC_EV_MSG_UP_CO_INITIAL:
|
||||
OSMO_ASSERT(data);
|
||||
|
||||
#define LEGACY_BEHAVIOR
|
||||
#ifdef LEGACY_BEHAVIOR
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Receiving CO message on RAN peer that has not done a proper RESET yet."
|
||||
" Accepting RAN peer implicitly (legacy compat)\n");
|
||||
iu_rnc_state_chg(rnc, IU_RNC_ST_READY);
|
||||
osmo_fsm_inst_dispatch(rnc->fi, event, data);
|
||||
return;
|
||||
#else
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Receiving CO message on RAN peer that has not done a proper RESET yet."
|
||||
" Disconnecting on incoming message, sending RESET to RAN peer.\n");
|
||||
/* No valid RESET procedure has happened here yet. Usually, we're expecting the RAN peer (BSC,
|
||||
* RNC) to first send a RESET message before sending Connection Oriented messages. So if we're
|
||||
* getting a CO message, likely we've just restarted or something. Send a RESET to the peer. */
|
||||
|
||||
/* Make sure the MS / UE properly disconnects. */
|
||||
clear_and_disconnect(rnc, ctx->conn_id);
|
||||
|
||||
iu_rnc_reset(rnc);
|
||||
return;
|
||||
#endif
|
||||
|
||||
case IU_RNC_EV_RX_RESET:
|
||||
iu_rnc_rx_reset(rnc);
|
||||
return;
|
||||
|
||||
case IU_RNC_EV_AVAILABLE:
|
||||
/* Send a RESET to the peer. */
|
||||
iu_rnc_reset(rnc);
|
||||
return;
|
||||
|
||||
case IU_RNC_EV_UNAVAILABLE:
|
||||
/* Do nothing, wait for peer to come up again. */
|
||||
return;
|
||||
|
||||
default:
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&iu_rnc_fsm, event));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void iu_rnc_st_wait_rx_reset_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc = fi->priv;
|
||||
struct iu_rnc_ev_msg_up_co_initial_ctx *ev_msg_up_co_initial_ctx;
|
||||
struct iu_rnc_ev_msg_up_co_ctx *ev_msg_up_co_ctx;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case IU_RNC_EV_RX_RESET_ACK:
|
||||
iu_rnc_state_chg(rnc, IU_RNC_ST_READY);
|
||||
return;
|
||||
|
||||
|
||||
case IU_RNC_EV_MSG_UP_CO_INITIAL:
|
||||
ev_msg_up_co_initial_ctx = data;
|
||||
OSMO_ASSERT(ev_msg_up_co_initial_ctx);
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Receiving CO Initial message on RAN peer that has not done a proper RESET yet."
|
||||
" Disconnecting on incoming message, sending RESET to RAN peer.\n");
|
||||
osmo_sccp_tx_disconn(ev_msg_up_co_initial_ctx->rnc->scu_iups->scu,
|
||||
ev_msg_up_co_initial_ctx->conn_id, NULL, 0);
|
||||
/* No valid RESET procedure has happened here yet. */
|
||||
iu_rnc_reset(rnc);
|
||||
return;
|
||||
return;
|
||||
case IU_RNC_EV_MSG_UP_CO:
|
||||
ev_msg_up_co_ctx = data;
|
||||
OSMO_ASSERT(ev_msg_up_co_ctx);
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Receiving CO message on RAN peer that has not done a proper RESET yet."
|
||||
" Disconnecting on incoming message, sending RESET to RAN peer.\n");
|
||||
ue_conn_ctx_link_invalidated_free(ev_msg_up_co_ctx->ue_ctx);
|
||||
/* No valid RESET procedure has happened here yet. */
|
||||
iu_rnc_reset(rnc);
|
||||
return;
|
||||
|
||||
case IU_RNC_EV_RX_RESET:
|
||||
iu_rnc_rx_reset(rnc);
|
||||
return;
|
||||
|
||||
case IU_RNC_EV_AVAILABLE:
|
||||
/* Send a RESET to the peer. */
|
||||
iu_rnc_reset(rnc);
|
||||
return;
|
||||
|
||||
case IU_RNC_EV_UNAVAILABLE:
|
||||
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
|
||||
return;
|
||||
|
||||
default:
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&iu_rnc_fsm, event));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void iu_rnc_st_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
|
||||
{
|
||||
if (prev_state != IU_RNC_ST_READY)
|
||||
sgsn_stat_inc(SGSN_STAT_IU_PEERS_ACTIVE, 1);
|
||||
}
|
||||
|
||||
static void iu_rnc_st_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc = fi->priv;
|
||||
struct iu_rnc_ev_msg_up_co_initial_ctx *ev_msg_up_co_initial_ctx;
|
||||
struct iu_rnc_ev_msg_up_co_ctx *ev_msg_up_co_ctx;
|
||||
|
||||
switch (event) {
|
||||
|
||||
case IU_RNC_EV_MSG_UP_CO_INITIAL:
|
||||
ev_msg_up_co_initial_ctx = data;
|
||||
OSMO_ASSERT(ev_msg_up_co_initial_ctx);
|
||||
OSMO_ASSERT(ev_msg_up_co_initial_ctx->rnc);
|
||||
|
||||
sgsn_ranap_iu_handle_co_initial(ev_msg_up_co_initial_ctx->rnc,
|
||||
ev_msg_up_co_initial_ctx->conn_id,
|
||||
&ev_msg_up_co_initial_ctx->message);
|
||||
return;
|
||||
|
||||
case IU_RNC_EV_MSG_UP_CO:
|
||||
ev_msg_up_co_ctx = data;
|
||||
OSMO_ASSERT(ev_msg_up_co_ctx);
|
||||
OSMO_ASSERT(ev_msg_up_co_ctx->ue_ctx);
|
||||
|
||||
sgsn_ranap_iu_handle_co(ev_msg_up_co_ctx->ue_ctx, &ev_msg_up_co_ctx->message);
|
||||
return;
|
||||
|
||||
case IU_RNC_EV_RX_RESET:
|
||||
iu_rnc_rx_reset(rnc);
|
||||
return;
|
||||
|
||||
case IU_RNC_EV_MSG_DOWN_CL:
|
||||
OSMO_ASSERT(data);
|
||||
sgsn_ranap_iu_tx_cl(rnc->scu_iups, &rnc->sccp_addr, (struct msgb *)data);
|
||||
return;
|
||||
|
||||
case IU_RNC_EV_AVAILABLE:
|
||||
/* Do nothing, we were already up. */
|
||||
return;
|
||||
|
||||
case IU_RNC_EV_UNAVAILABLE:
|
||||
iu_rnc_discard_all_ue_ctx(rnc);
|
||||
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
|
||||
return;
|
||||
|
||||
default:
|
||||
LOG_RNC(rnc, LOGL_ERROR, "Unhandled event: %s\n", osmo_fsm_event_name(&iu_rnc_fsm, event));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void iu_rnc_st_ready_onleave(struct osmo_fsm_inst *fi, uint32_t next_state)
|
||||
{
|
||||
if (next_state != IU_RNC_ST_READY)
|
||||
sgsn_stat_dec(SGSN_STAT_IU_PEERS_ACTIVE, 1);
|
||||
}
|
||||
|
||||
static int iu_rnc_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc = fi->priv;
|
||||
iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iu_rnc_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc = fi->priv;
|
||||
|
||||
iu_rnc_discard_all_ue_ctx(rnc);
|
||||
|
||||
if (rnc->fi->state == IU_RNC_ST_READY)
|
||||
sgsn_stat_dec(SGSN_STAT_IU_PEERS_ACTIVE, 1);
|
||||
sgsn_stat_dec(SGSN_STAT_IU_PEERS_TOTAL, 1);
|
||||
}
|
||||
|
||||
static const struct osmo_fsm_state iu_rnc_fsm_states[] = {
|
||||
[IU_RNC_ST_WAIT_RX_RESET] = {
|
||||
.name = "WAIT_RX_RESET",
|
||||
.action = iu_rnc_st_wait_rx_reset,
|
||||
.in_event_mask = 0
|
||||
| S(IU_RNC_EV_RX_RESET)
|
||||
| S(IU_RNC_EV_MSG_UP_CO_INITIAL)
|
||||
| S(IU_RNC_EV_MSG_UP_CO)
|
||||
| S(IU_RNC_EV_AVAILABLE)
|
||||
| S(IU_RNC_EV_UNAVAILABLE)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(IU_RNC_ST_WAIT_RX_RESET)
|
||||
| S(IU_RNC_ST_WAIT_RX_RESET_ACK)
|
||||
| S(IU_RNC_ST_READY)
|
||||
| S(IU_RNC_ST_DISCARDING)
|
||||
,
|
||||
},
|
||||
[IU_RNC_ST_WAIT_RX_RESET_ACK] = {
|
||||
.name = "WAIT_RX_RESET_ACK",
|
||||
.action = iu_rnc_st_wait_rx_reset_ack,
|
||||
.in_event_mask = 0
|
||||
| S(IU_RNC_EV_RX_RESET)
|
||||
| S(IU_RNC_EV_RX_RESET_ACK)
|
||||
| S(IU_RNC_EV_MSG_UP_CO_INITIAL)
|
||||
| S(IU_RNC_EV_MSG_UP_CO)
|
||||
| S(IU_RNC_EV_AVAILABLE)
|
||||
| S(IU_RNC_EV_UNAVAILABLE)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(IU_RNC_ST_WAIT_RX_RESET)
|
||||
| S(IU_RNC_ST_WAIT_RX_RESET_ACK)
|
||||
| S(IU_RNC_ST_READY)
|
||||
| S(IU_RNC_ST_DISCARDING)
|
||||
,
|
||||
},
|
||||
[IU_RNC_ST_READY] = {
|
||||
.name = "READY",
|
||||
.action = iu_rnc_st_ready,
|
||||
.onenter = iu_rnc_st_ready_onenter,
|
||||
.onleave = iu_rnc_st_ready_onleave,
|
||||
.in_event_mask = 0
|
||||
| S(IU_RNC_EV_RX_RESET)
|
||||
| S(IU_RNC_EV_MSG_UP_CO_INITIAL)
|
||||
| S(IU_RNC_EV_MSG_UP_CO)
|
||||
| S(IU_RNC_EV_MSG_DOWN_CL)
|
||||
| S(IU_RNC_EV_AVAILABLE)
|
||||
| S(IU_RNC_EV_UNAVAILABLE)
|
||||
,
|
||||
.out_state_mask = 0
|
||||
| S(IU_RNC_ST_WAIT_RX_RESET)
|
||||
| S(IU_RNC_ST_WAIT_RX_RESET_ACK)
|
||||
| S(IU_RNC_ST_READY)
|
||||
| S(IU_RNC_ST_DISCARDING)
|
||||
,
|
||||
},
|
||||
[IU_RNC_ST_DISCARDING] = {
|
||||
.name = "DISCARDING",
|
||||
},
|
||||
};
|
||||
|
||||
struct osmo_fsm iu_rnc_fsm = {
|
||||
.name = "iu_rnc",
|
||||
.states = iu_rnc_fsm_states,
|
||||
.num_states = ARRAY_SIZE(iu_rnc_fsm_states),
|
||||
.log_subsys = DRANAP,
|
||||
.event_names = iu_rnc_fsm_event_names,
|
||||
.timer_cb = iu_rnc_fsm_timer_cb,
|
||||
.cleanup = iu_rnc_fsm_cleanup,
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void iu_rnc_init(void)
|
||||
{
|
||||
OSMO_ASSERT(osmo_fsm_register(&iu_rnc_fsm) == 0);
|
||||
}
|
||||
@@ -19,6 +19,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
@@ -36,6 +38,8 @@
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
#include <osmocom/sgsn/gprs_subscriber.h>
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/mmctx.h>
|
||||
@@ -49,17 +53,14 @@
|
||||
#include <osmocom/sgsn/gprs_mm_state_iu_fsm.h>
|
||||
#include <osmocom/sgsn/gprs_gmm_fsm.h>
|
||||
#include <osmocom/sgsn/gprs_llc.h>
|
||||
#include <osmocom/sgsn/gprs_ranap.h>
|
||||
#include <osmocom/sgsn/gprs_sndcp.h>
|
||||
#include <osmocom/sgsn/gtp_ggsn.h>
|
||||
#include <osmocom/sgsn/gtp.h>
|
||||
#include <osmocom/sgsn/pdpctx.h>
|
||||
|
||||
#include <pdp.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "../../config.h"
|
||||
|
||||
const struct value_string sgsn_ran_type_names[] = {
|
||||
{ MM_CTX_T_GERAN_Gb, "GPRS/EDGE via Gb" },
|
||||
{ MM_CTX_T_UTRAN_Iu, "UMTS via Iu" },
|
||||
@@ -105,15 +106,30 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* look-up an SGSN MM context based on Gb LLME context (struct gprs_llc_llme)*/
|
||||
struct sgsn_mm_ctx *sgsn_mm_ctx_by_llme(const struct gprs_llc_llme *llme)
|
||||
{
|
||||
struct sgsn_mm_ctx *ctx;
|
||||
|
||||
llist_for_each_entry (ctx, &sgsn->mm_list, list) {
|
||||
if (ctx->ran_type == MM_CTX_T_GERAN_Gb
|
||||
&& llme == ctx->gb.llme)
|
||||
return ctx;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look-up a SGSN MM context based on TLLI + RAI */
|
||||
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
|
||||
const struct gprs_ra_id *raid)
|
||||
const struct osmo_routing_area_id *raid)
|
||||
{
|
||||
struct sgsn_mm_ctx *ctx;
|
||||
|
||||
llist_for_each_entry(ctx, &sgsn->mm_list, list) {
|
||||
if ((tlli == ctx->gb.tlli || tlli == ctx->gb.tlli_new) &&
|
||||
gprs_ra_id_equals(raid, &ctx->ra))
|
||||
!osmo_rai_cmp(raid, &ctx->ra))
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@@ -121,7 +137,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
|
||||
}
|
||||
|
||||
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli,
|
||||
const struct gprs_ra_id *raid)
|
||||
const struct osmo_routing_area_id *raid)
|
||||
{
|
||||
struct sgsn_mm_ctx *ctx;
|
||||
int tlli_type;
|
||||
@@ -138,7 +154,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli,
|
||||
llist_for_each_entry(ctx, &sgsn->mm_list, list) {
|
||||
if ((gprs_tmsi2tlli(ctx->p_tmsi, tlli_type) == tlli ||
|
||||
gprs_tmsi2tlli(ctx->p_tmsi_old, tlli_type) == tlli) &&
|
||||
gprs_ra_id_equals(raid, &ctx->ra))
|
||||
!osmo_rai_cmp(raid, &ctx->ra))
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@@ -224,7 +240,7 @@ out:
|
||||
}
|
||||
/* Allocate a new SGSN MM context for GERAN_Gb */
|
||||
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli,
|
||||
const struct gprs_ra_id *raid)
|
||||
const struct osmo_routing_area_id *raid)
|
||||
{
|
||||
struct sgsn_mm_ctx *ctx;
|
||||
|
||||
@@ -252,7 +268,7 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx)
|
||||
return NULL;
|
||||
|
||||
/* Need to get RAID from IU conn */
|
||||
ctx->ra = ue_ctx->ra_id;
|
||||
gprs_rai_to_osmo(&ctx->ra, &ue_ctx->ra_id);
|
||||
ctx->ran_type = MM_CTX_T_UTRAN_Iu;
|
||||
ctx->iu.ue_ctx = ue_ctx;
|
||||
ctx->iu.ue_ctx->rab_assign_addr_enc = sgsn->cfg.iu.rab_assign_addr_enc;
|
||||
@@ -582,3 +598,57 @@ struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx,
|
||||
|
||||
return ggsn;
|
||||
}
|
||||
|
||||
|
||||
/* determine if the MS/UE supports R99 or later */
|
||||
bool sgsn_mm_ctx_is_r99(const struct sgsn_mm_ctx *mm)
|
||||
{
|
||||
if (mm->ms_network_capa.len < 1)
|
||||
return false;
|
||||
if (mm->ms_network_capa.buf[0] & 0x01)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if BUILD_IU
|
||||
/* Send RAB activation requests for all PDP contexts */
|
||||
void sgsn_mm_ctx_iu_activate_rabs(struct sgsn_mm_ctx *ctx)
|
||||
{
|
||||
struct sgsn_pdp_ctx *pdp;
|
||||
OSMO_ASSERT(ctx->ran_type == MM_CTX_T_UTRAN_Iu);
|
||||
|
||||
llist_for_each_entry(pdp, &ctx->pdp_list, list)
|
||||
sgsn_pdp_ctx_iu_rab_activate(pdp, pdp->nsapi);
|
||||
}
|
||||
|
||||
/* send a Iu Release Command and free afterwards the UE context */
|
||||
void sgsn_mm_ctx_iu_ranap_release_free(struct sgsn_mm_ctx *mmctx,
|
||||
const struct RANAP_Cause *cause)
|
||||
{
|
||||
unsigned long X1001;
|
||||
|
||||
if (!mmctx)
|
||||
return;
|
||||
|
||||
if (!mmctx->iu.ue_ctx)
|
||||
return;
|
||||
|
||||
X1001 = osmo_tdef_get(sgsn->cfg.T_defs, -1001, OSMO_TDEF_S, -1);
|
||||
|
||||
sgsn_ranap_iu_tx_release_free(mmctx->iu.ue_ctx, cause, (int) X1001);
|
||||
mmctx->iu.ue_ctx = NULL;
|
||||
}
|
||||
|
||||
/* free the Iu UE context */
|
||||
void sgsn_mm_ctx_iu_ranap_free(struct sgsn_mm_ctx *mmctx)
|
||||
{
|
||||
if (!mmctx)
|
||||
return;
|
||||
|
||||
if (!mmctx->iu.ue_ctx)
|
||||
return;
|
||||
|
||||
sgsn_ranap_iu_free_ue(mmctx->iu.ue_ctx);
|
||||
mmctx->iu.ue_ctx = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
@@ -36,6 +38,7 @@
|
||||
#include <osmocom/sgsn/gprs_llc_xid.h>
|
||||
#include <osmocom/sgsn/gprs_sndcp.h>
|
||||
#include <osmocom/sgsn/gprs_llc.h>
|
||||
#include <osmocom/sgsn/gprs_ranap.h>
|
||||
#include <osmocom/sgsn/gprs_sm.h>
|
||||
#include <osmocom/sgsn/gtp.h>
|
||||
|
||||
@@ -156,3 +159,36 @@ void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
|
||||
|
||||
talloc_free(pdp);
|
||||
}
|
||||
|
||||
#ifdef BUILD_IU
|
||||
int sgsn_pdp_ctx_iu_rab_activate(struct sgsn_pdp_ctx *pdp, uint8_t rab_id)
|
||||
{
|
||||
struct sgsn_mm_ctx *mm = pdp->mm;
|
||||
struct ranap_ue_conn_ctx *ue_ctx;
|
||||
uint32_t ggsn_ip;
|
||||
|
||||
OSMO_ASSERT(mm->ran_type == MM_CTX_T_UTRAN_Iu);
|
||||
ue_ctx = mm->iu.ue_ctx;
|
||||
|
||||
/* Get the IP address for ggsn user plane */
|
||||
memcpy(&ggsn_ip, pdp->lib->gsnru.v, pdp->lib->gsnru.l);
|
||||
ggsn_ip = htonl(ggsn_ip);
|
||||
|
||||
LOGPDPCTXP(LOGL_INFO, pdp, "Activate RAB: rab_id=%u, ggsn_ip=%x, teid_gn=%x\n",
|
||||
rab_id, ggsn_ip, pdp->lib->teid_gn);
|
||||
|
||||
return sgsn_ranap_iu_tx_rab_ps_ass_req(ue_ctx, rab_id, ggsn_ip, pdp->lib->teid_gn);
|
||||
}
|
||||
|
||||
int sgsn_pdp_ctx_iu_rab_deactivate(struct sgsn_pdp_ctx *pdp, uint8_t rab_id)
|
||||
{
|
||||
struct sgsn_mm_ctx *mm = pdp->mm;
|
||||
|
||||
OSMO_ASSERT(mm->ran_type == MM_CTX_T_UTRAN_Iu);
|
||||
|
||||
LOGPDPCTXP(LOGL_NOTICE, pdp, "Release RAB: rab_id=%u not supported!\n", rab_id);
|
||||
//struct ranap_ue_conn_ctx *ue_ctx = mm->iu.ue_ctx;
|
||||
// TODO: add new function similar to sgsn_ranap_iu_tx_rab_ps_ass_req() but requesting relese of RAB.
|
||||
return -ENOTSUP;
|
||||
}
|
||||
#endif /* ifdef BUILD_IU */
|
||||
|
||||
424
src/sgsn/sccp.c
Normal file
424
src/sgsn/sccp.c
Normal file
@@ -0,0 +1,424 @@
|
||||
/* SCCP Handling */
|
||||
/* (C) 2025 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <osmocom/sigtran/sccp_sap.h>
|
||||
#include <osmocom/sigtran/sccp_helpers.h>
|
||||
#include <osmocom/sccp/sccp_types.h>
|
||||
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/iu_client.h>
|
||||
#include <osmocom/sgsn/iu_rnc.h>
|
||||
#include <osmocom/sgsn/iu_rnc_fsm.h>
|
||||
#include <osmocom/sgsn/gprs_ranap.h>
|
||||
#include <osmocom/sgsn/sccp.h>
|
||||
#include <osmocom/sgsn/sgsn.h>
|
||||
|
||||
/* Entry to cache conn_id <-> sccp_addr mapping in case we receive an empty CR */
|
||||
struct iu_new_ctx_entry {
|
||||
struct llist_head list;
|
||||
|
||||
uint32_t conn_id;
|
||||
struct osmo_sccp_addr sccp_addr;
|
||||
};
|
||||
|
||||
static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu);
|
||||
|
||||
struct sgsn_sccp_user_iups *sgsn_scu_iups_inst_alloc(struct sgsn_instance *sgsn, struct osmo_sccp_instance *sccp)
|
||||
{
|
||||
struct sgsn_sccp_user_iups *scu_iups;
|
||||
|
||||
scu_iups = talloc_zero(sgsn, struct sgsn_sccp_user_iups);
|
||||
OSMO_ASSERT(scu_iups);
|
||||
|
||||
scu_iups->sgsn = sgsn;
|
||||
scu_iups->sccp = sccp;
|
||||
|
||||
INIT_LLIST_HEAD(&scu_iups->ue_conn_ctx_list);
|
||||
INIT_LLIST_HEAD(&scu_iups->ue_conn_sccp_addr_list);
|
||||
|
||||
osmo_sccp_local_addr_by_instance(&scu_iups->local_sccp_addr, scu_iups->sccp, OSMO_SCCP_SSN_RANAP);
|
||||
scu_iups->scu = osmo_sccp_user_bind(scu_iups->sccp, "OsmoSGSN-IuPS", sccp_sap_up, OSMO_SCCP_SSN_RANAP);
|
||||
osmo_sccp_user_set_priv(scu_iups->scu, scu_iups);
|
||||
|
||||
return scu_iups;
|
||||
}
|
||||
|
||||
void sgsn_scu_iups_free(struct sgsn_sccp_user_iups *scu_iups)
|
||||
{
|
||||
if (!scu_iups)
|
||||
return;
|
||||
|
||||
if (scu_iups->scu)
|
||||
osmo_sccp_user_unbind(scu_iups->scu);
|
||||
talloc_free(scu_iups);
|
||||
}
|
||||
|
||||
/* wrap RANAP message in SCCP N-DATA.req
|
||||
* ranap_msg becomes owned by the callee. */
|
||||
int sgsn_scu_iups_tx_data_req(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id, struct msgb *ranap_msg)
|
||||
{
|
||||
struct osmo_scu_prim *prim;
|
||||
int rc;
|
||||
|
||||
if (!scu_iups) {
|
||||
LOGP(DSUA, LOGL_ERROR, "Failed to send SCCP N-DATA.req(%u): no SCCP User\n", conn_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ranap_msg->l2h = ranap_msg->data;
|
||||
prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, ranap_msg);
|
||||
prim->u.data.conn_id = conn_id;
|
||||
|
||||
rc = osmo_sccp_user_sap_down(scu_iups->scu, &prim->oph);
|
||||
if (rc)
|
||||
LOGP(DSUA, LOGL_ERROR, "Failed to send SCCP N-DATA.req(%u)\n", conn_id);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct ranap_ue_conn_ctx *sgsn_scu_iups_ue_conn_ctx_find(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id)
|
||||
{
|
||||
struct ranap_ue_conn_ctx *ctx;
|
||||
|
||||
llist_for_each_entry(ctx, &scu_iups->ue_conn_ctx_list, list) {
|
||||
if (ctx->conn_id == conn_id)
|
||||
return ctx;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ue_conn_sccp_addr_add(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id, const struct osmo_sccp_addr *calling_addr)
|
||||
{
|
||||
struct iu_new_ctx_entry *entry = talloc_zero(scu_iups, struct iu_new_ctx_entry);
|
||||
|
||||
entry->conn_id = conn_id;
|
||||
entry->sccp_addr = *calling_addr;
|
||||
|
||||
llist_add(&entry->list, &scu_iups->ue_conn_sccp_addr_list);
|
||||
}
|
||||
|
||||
static const struct osmo_sccp_addr *ue_conn_sccp_addr_find(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id)
|
||||
{
|
||||
struct iu_new_ctx_entry *entry;
|
||||
llist_for_each_entry(entry, &scu_iups->ue_conn_sccp_addr_list, list) {
|
||||
if (entry->conn_id == conn_id)
|
||||
return &entry->sccp_addr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void ue_conn_sccp_addr_del(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id)
|
||||
{
|
||||
struct iu_new_ctx_entry *entry;
|
||||
llist_for_each_entry(entry, &scu_iups->ue_conn_sccp_addr_list, list) {
|
||||
if (entry->conn_id == conn_id) {
|
||||
llist_del(&entry->list);
|
||||
talloc_free(entry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_notice_ind(struct sgsn_sccp_user_iups *scu_iups, const struct osmo_scu_notice_param *ni)
|
||||
{
|
||||
struct ranap_iu_rnc *rnc;
|
||||
|
||||
rnc = iu_rnc_find_by_addr(&ni->calling_addr);
|
||||
|
||||
if (!rnc) {
|
||||
LOGP(DSUA, LOGL_DEBUG,
|
||||
"(calling_addr=%s) N-NOTICE.ind cause=%u='%s' importance=%u didn't match any RNC, ignoring\n",
|
||||
osmo_sccp_addr_dump(&ni->calling_addr),
|
||||
ni->cause, osmo_sccp_return_cause_name(ni->cause),
|
||||
ni->importance);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_RNC(rnc, LOGL_NOTICE,
|
||||
"N-NOTICE.ind cause=%u='%s' importance=%u\n",
|
||||
ni->cause, osmo_sccp_return_cause_name(ni->cause),
|
||||
ni->importance);
|
||||
|
||||
switch (ni->cause) {
|
||||
case SCCP_RETURN_CAUSE_SUBSYSTEM_CONGESTION:
|
||||
case SCCP_RETURN_CAUSE_NETWORK_CONGESTION:
|
||||
/* Transient failures (hopefully), keep going. */
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Messages are not arriving to rnc. Signal it is unavailable to update local state. */
|
||||
osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_UNAVAILABLE, NULL);
|
||||
}
|
||||
|
||||
static void handle_pcstate_ind(struct sgsn_sccp_user_iups *scu_iups, const struct osmo_scu_pcstate_param *pcst)
|
||||
{
|
||||
struct osmo_ss7_instance *cs7 = osmo_sccp_get_ss7(scu_iups->sccp);
|
||||
struct osmo_sccp_addr rem_addr;
|
||||
struct ranap_iu_rnc *rnc;
|
||||
bool connected;
|
||||
bool disconnected;
|
||||
|
||||
LOGP(DSUA, LOGL_DEBUG, "N-PCSTATE ind: affected_pc=%u=%s sp_status=%s remote_sccp_status=%s\n",
|
||||
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc),
|
||||
osmo_sccp_sp_status_name(pcst->sp_status),
|
||||
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
|
||||
|
||||
osmo_sccp_make_addr_pc_ssn(&rem_addr, pcst->affected_pc, OSMO_SCCP_SSN_RANAP);
|
||||
|
||||
rnc = iu_rnc_find_by_addr(&rem_addr);
|
||||
if (!rnc) {
|
||||
LOGP(DSUA, LOGL_DEBUG, "No RNC found under pc=%u=s%s\n",
|
||||
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc));
|
||||
return;
|
||||
}
|
||||
|
||||
/* See if this marks the point code to have become available, or to have been lost.
|
||||
*
|
||||
* I want to detect two events:
|
||||
* - connection event (both indicators say PC is reachable).
|
||||
* - disconnection event (at least one indicator says the PC is not reachable).
|
||||
*
|
||||
* There are two separate incoming indicators with various possible values -- the incoming events can be:
|
||||
*
|
||||
* - neither connection nor disconnection indicated -- just indicating congestion
|
||||
* connected == false, disconnected == false --> do nothing.
|
||||
* - both incoming values indicate that we are connected
|
||||
* --> trigger connected
|
||||
* - both indicate we are disconnected
|
||||
* --> trigger disconnected
|
||||
* - one value indicates 'connected', the other indicates 'disconnected'
|
||||
* --> trigger disconnected
|
||||
*
|
||||
* Congestion could imply that we're connected, but it does not indicate
|
||||
* that a PC's reachability changed, so no need to trigger on that.
|
||||
*/
|
||||
connected = false;
|
||||
disconnected = false;
|
||||
|
||||
switch (pcst->sp_status) {
|
||||
case OSMO_SCCP_SP_S_ACCESSIBLE:
|
||||
connected = true;
|
||||
break;
|
||||
case OSMO_SCCP_SP_S_INACCESSIBLE:
|
||||
disconnected = true;
|
||||
break;
|
||||
default:
|
||||
case OSMO_SCCP_SP_S_CONGESTED:
|
||||
/* Neither connecting nor disconnecting */
|
||||
break;
|
||||
}
|
||||
|
||||
switch (pcst->remote_sccp_status) {
|
||||
case OSMO_SCCP_REM_SCCP_S_AVAILABLE:
|
||||
if (!disconnected)
|
||||
connected = true;
|
||||
break;
|
||||
case OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN:
|
||||
case OSMO_SCCP_REM_SCCP_S_UNEQUIPPED:
|
||||
case OSMO_SCCP_REM_SCCP_S_INACCESSIBLE:
|
||||
disconnected = true;
|
||||
connected = false;
|
||||
break;
|
||||
default:
|
||||
case OSMO_SCCP_REM_SCCP_S_CONGESTED:
|
||||
/* Neither connecting nor disconnecting */
|
||||
break;
|
||||
}
|
||||
|
||||
if (disconnected) {
|
||||
LOG_RNC(rnc, LOGL_NOTICE,
|
||||
"now unreachable: N-PCSTATE ind: pc=%u=%s sp_status=%s remote_sccp_status=%s\n",
|
||||
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc),
|
||||
osmo_sccp_sp_status_name(pcst->sp_status),
|
||||
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
|
||||
osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_UNAVAILABLE, NULL);
|
||||
} else if (connected) {
|
||||
LOG_RNC(rnc, LOGL_NOTICE,
|
||||
"now available: N-PCSTATE ind: pc=%u=%s sp_status=%s remote_sccp_status=%s\n",
|
||||
pcst->affected_pc, osmo_ss7_pointcode_print(cs7, pcst->affected_pc),
|
||||
osmo_sccp_sp_status_name(pcst->sp_status),
|
||||
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
|
||||
osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_AVAILABLE, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static struct osmo_prim_hdr *make_conn_resp(struct osmo_scu_connect_param *param)
|
||||
{
|
||||
struct msgb *msg = msgb_alloc(1024, "conn_resp");
|
||||
struct osmo_scu_prim *prim;
|
||||
|
||||
prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim));
|
||||
osmo_prim_init(&prim->oph, SCCP_SAP_USER,
|
||||
OSMO_SCU_PRIM_N_CONNECT,
|
||||
PRIM_OP_RESPONSE, msg);
|
||||
memcpy(&prim->u.connect, param, sizeof(prim->u.connect));
|
||||
return &prim->oph;
|
||||
}
|
||||
|
||||
static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
|
||||
{
|
||||
struct osmo_sccp_user *scu = _scu;
|
||||
struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph;
|
||||
struct sgsn_sccp_user_iups *scu_iups = osmo_sccp_user_get_priv(scu);
|
||||
struct osmo_prim_hdr *resp = NULL;
|
||||
int rc = -1;
|
||||
struct ranap_ue_conn_ctx *ue;
|
||||
uint32_t conn_id;
|
||||
|
||||
LOGP(DSUA, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph));
|
||||
|
||||
switch (OSMO_PRIM_HDR(oph)) {
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
|
||||
/* confirmation of outbound connection */
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):
|
||||
/* indication of new inbound connection request*/
|
||||
conn_id = prim->u.connect.conn_id;
|
||||
LOGP(DSUA, LOGL_DEBUG, "N-CONNECT.ind(X->%u)\n", conn_id);
|
||||
|
||||
/* first ensure the local SCCP socket is ACTIVE */
|
||||
resp = make_conn_resp(&prim->u.connect);
|
||||
osmo_sccp_user_sap_down(scu, resp);
|
||||
/* then handle the RANAP payload */
|
||||
if (/* prim->u.connect.called_addr.ssn != OSMO_SCCP_SSN_RANAP || */
|
||||
!msgb_l2(oph->msg) || msgb_l2len(oph->msg) == 0) {
|
||||
LOGP(DSUA, LOGL_DEBUG,
|
||||
"Received N-CONNECT.ind without data\n");
|
||||
ue_conn_sccp_addr_add(scu_iups, conn_id, &prim->u.connect.calling_addr);
|
||||
} else {
|
||||
rc = sgsn_ranap_iu_rx_co_initial_msg(scu_iups, &prim->u.connect.calling_addr,
|
||||
conn_id,
|
||||
msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
}
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
|
||||
/* indication of disconnect */
|
||||
conn_id = prim->u.disconnect.conn_id;
|
||||
LOGP(DSUA, LOGL_DEBUG, "N-DISCONNECT.ind(%u)\n", conn_id);
|
||||
|
||||
ue_conn_sccp_addr_del(scu_iups, conn_id);
|
||||
ue = sgsn_scu_iups_ue_conn_ctx_find(scu_iups, conn_id);
|
||||
if (!ue)
|
||||
break;
|
||||
|
||||
rc = 0;
|
||||
if (msgb_l2len(oph->msg) > 0)
|
||||
rc = sgsn_ranap_iu_rx_co_msg(ue, msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
|
||||
/* A Iu Release event might be used to free the UE in cn_ranap_handle_co(). */
|
||||
ue = sgsn_scu_iups_ue_conn_ctx_find(scu_iups, conn_id);
|
||||
if (!ue)
|
||||
break;
|
||||
ue_conn_ctx_link_invalidated_free(ue);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
|
||||
/* connection-oriented data received */
|
||||
conn_id = prim->u.data.conn_id;
|
||||
LOGP(DSUA, LOGL_DEBUG, "N-DATA.ind(%u, %s)\n", conn_id,
|
||||
osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
|
||||
|
||||
/* resolve UE context */
|
||||
ue = sgsn_scu_iups_ue_conn_ctx_find(scu_iups, conn_id);
|
||||
if (!ue) {
|
||||
/* Could be an InitialUE-Message after an empty CR, recreate new_ctx */
|
||||
const struct osmo_sccp_addr *sccp_addr = ue_conn_sccp_addr_find(scu_iups, conn_id);
|
||||
if (!sccp_addr) {
|
||||
LOGP(DSUA, LOGL_NOTICE,
|
||||
"N-DATA.ind for unknown conn_id (%u)\n", conn_id);
|
||||
break;
|
||||
}
|
||||
/* Hold copy of address before deleting it: */
|
||||
struct osmo_sccp_addr rem_sccp_addr = *sccp_addr;
|
||||
ue_conn_sccp_addr_del(scu_iups, conn_id);
|
||||
rc = sgsn_ranap_iu_rx_co_initial_msg(scu_iups, &rem_sccp_addr, conn_id,
|
||||
msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
break;
|
||||
}
|
||||
rc = sgsn_ranap_iu_rx_co_msg(ue, msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
|
||||
/* connection-less data received */
|
||||
LOGP(DSUA, LOGL_DEBUG, "N-UNITDATA.ind(%s)\n",
|
||||
osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
|
||||
rc = sgsn_ranap_iu_rx_cl_msg(scu_iups, &prim->u.unitdata, msgb_l2(oph->msg), msgb_l2len(oph->msg));
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_NOTICE, PRIM_OP_INDICATION):
|
||||
LOGP(DSUA, LOGL_DEBUG, "N-NOTICE.ind(%s)\n",
|
||||
osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
|
||||
handle_notice_ind(scu_iups, &prim->u.notice);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION):
|
||||
handle_pcstate_ind(scu_iups, &prim->u.pcstate);
|
||||
break;
|
||||
case OSMO_PRIM(OSMO_SCU_PRIM_N_STATE, PRIM_OP_INDICATION):
|
||||
LOGP(DSUA, LOGL_DEBUG, "SCCP-User-SAP: Ignoring %s.%s\n",
|
||||
osmo_scu_prim_type_name(oph->primitive),
|
||||
get_value_string(osmo_prim_op_names, oph->operation));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
msgb_free(oph->msg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sgsn_sccp_init(struct sgsn_instance *sgi)
|
||||
{
|
||||
/* Note that these are mostly defaults and can be overridden from the VTY */
|
||||
sgi->sccp.sccp = osmo_sccp_simple_client_on_ss7_id(tall_sgsn_ctx,
|
||||
sgi->cfg.iu.cs7_instance,
|
||||
"OsmoSGSN",
|
||||
(23 << 3) + 4,
|
||||
OSMO_SS7_ASP_PROT_M3UA,
|
||||
0, "localhost",
|
||||
0, "localhost");
|
||||
if (!sgi->sccp.sccp) {
|
||||
LOGP(DGPRS, LOGL_ERROR, "Setting up SCCP instance on cs7 instance %d failed!\n",
|
||||
sgi->cfg.iu.cs7_instance);
|
||||
return -EINVAL;
|
||||
}
|
||||
osmo_sccp_set_priv(sgi->sccp.sccp, sgsn);
|
||||
|
||||
sgi->sccp.scu_iups = sgsn_scu_iups_inst_alloc(sgsn, sgi->sccp.sccp);
|
||||
OSMO_ASSERT(sgi->sccp.scu_iups);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sgsn_sccp_release(struct sgsn_instance *sgi)
|
||||
{
|
||||
sgsn_scu_iups_free(sgi->sccp.scu_iups);
|
||||
sgi->sccp.scu_iups = NULL;
|
||||
if (sgi->sccp.sccp) {
|
||||
osmo_sccp_instance_destroy(sgi->sccp.sccp);
|
||||
sgi->sccp.sccp = NULL;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/timer.h>
|
||||
@@ -36,6 +38,11 @@
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
|
||||
#include <osmocom/crypt/gprs_cipher.h>
|
||||
#include <osmocom/crypt/utran_cipher.h>
|
||||
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
#include <osmocom/sgsn/gprs_subscriber.h>
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/sgsn.h>
|
||||
@@ -52,15 +59,16 @@
|
||||
#include <osmocom/sgsn/gtp_ggsn.h>
|
||||
#include <osmocom/sgsn/gtp.h>
|
||||
#include <osmocom/sgsn/pdpctx.h>
|
||||
|
||||
#include <pdp.h>
|
||||
#include <osmocom/sgsn/gprs_routing_area.h>
|
||||
#if BUILD_IU
|
||||
#include <osmocom/sgsn/iu_rnc_fsm.h>
|
||||
#include <osmocom/sgsn/sccp.h>
|
||||
#endif /* #if BUILD_IU */
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#define GPRS_LLME_CHECK_TICK 30
|
||||
|
||||
extern struct osmo_tdef sgsn_T_defs[];
|
||||
|
||||
static const struct rate_ctr_desc sgsn_ctr_description[] = {
|
||||
{ "llc:dl_bytes", "Count sent LLC bytes before giving it to the bssgp layer" },
|
||||
{ "llc:ul_bytes", "Count successful received LLC bytes (encrypt & fcs correct)" },
|
||||
@@ -95,6 +103,20 @@ static const struct rate_ctr_group_desc sgsn_ctrg_desc = {
|
||||
sgsn_ctr_description,
|
||||
};
|
||||
|
||||
|
||||
static const struct osmo_stat_item_desc sgsn_stat_item_description[] = {
|
||||
[SGSN_STAT_IU_PEERS_TOTAL] = { "iu_peers:total", "Total Iu peers (RNC, HNBGW) seen since startup", OSMO_STAT_ITEM_NO_UNIT, 4, 0},
|
||||
[SGSN_STAT_IU_PEERS_ACTIVE] = { "iu_peers:active", "Currently active Iu peers (RANAP ready)", OSMO_STAT_ITEM_NO_UNIT, 4, 0},
|
||||
};
|
||||
|
||||
static const struct osmo_stat_item_group_desc sgsn_statg_desc = {
|
||||
"sgsn",
|
||||
"serving GPRS support node statistics",
|
||||
OSMO_STATS_CLASS_GLOBAL,
|
||||
ARRAY_SIZE(sgsn_stat_item_description),
|
||||
sgsn_stat_item_description,
|
||||
};
|
||||
|
||||
static void sgsn_llme_cleanup_free(struct gprs_llc_llme *llme)
|
||||
{
|
||||
struct sgsn_mm_ctx *mmctx = NULL;
|
||||
@@ -148,8 +170,12 @@ static void sgsn_llme_check_cb(void *data_)
|
||||
static int sgsn_instance_talloc_destructor(struct sgsn_instance *sgi)
|
||||
{
|
||||
sgsn_cdr_release(sgi);
|
||||
#if BUILD_IU
|
||||
sgsn_sccp_release(sgi);
|
||||
#endif /* #if BUILD_IU */
|
||||
osmo_timer_del(&sgi->llme_timer);
|
||||
rate_ctr_group_free(sgi->rate_ctrs);
|
||||
osmo_stat_item_group_free(sgi->statg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -162,6 +188,8 @@ struct sgsn_instance *sgsn_instance_alloc(void *talloc_ctx)
|
||||
|
||||
inst->cfg.gtp_statedir = talloc_strdup(inst, "./");
|
||||
inst->cfg.auth_policy = SGSN_AUTH_POLICY_CLOSED;
|
||||
inst->cfg.gea_encryption_mask = (1 << GPRS_ALGO_GEA0); /* no encryption */
|
||||
inst->cfg.uea_encryption_mask = (1 << OSMO_UTRAN_UEA2) | (1 << OSMO_UTRAN_UEA1);
|
||||
inst->cfg.require_authentication = true; /* only applies if auth_policy is REMOTE */
|
||||
inst->cfg.gsup_server_port = OSMO_GSUP_PORT;
|
||||
|
||||
@@ -172,18 +200,24 @@ struct sgsn_instance *sgsn_instance_alloc(void *talloc_ctx)
|
||||
|
||||
inst->rate_ctrs = rate_ctr_group_alloc(inst, &sgsn_ctrg_desc, 0);
|
||||
OSMO_ASSERT(inst->rate_ctrs);
|
||||
inst->statg = osmo_stat_item_group_alloc(inst, &sgsn_statg_desc, 0);
|
||||
OSMO_ASSERT(inst->statg);
|
||||
|
||||
INIT_LLIST_HEAD(&inst->apn_list);
|
||||
INIT_LLIST_HEAD(&inst->ggsn_list);
|
||||
INIT_LLIST_HEAD(&inst->mme_list);
|
||||
INIT_LLIST_HEAD(&inst->mm_list);
|
||||
INIT_LLIST_HEAD(&inst->pdp_list);
|
||||
#if BUILD_IU
|
||||
INIT_LLIST_HEAD(&inst->rnc_list);
|
||||
#endif /* #if BUILD_IU */
|
||||
|
||||
osmo_timer_setup(&inst->llme_timer, sgsn_llme_check_cb, NULL);
|
||||
osmo_timer_schedule(&inst->llme_timer, GPRS_LLME_CHECK_TICK, 0);
|
||||
/* These are mostly setting up stuff not related to VTY cfg, so they can be set up here: */
|
||||
sgsn_auth_init(inst);
|
||||
sgsn_cdr_init(inst);
|
||||
sgsn_ra_init(inst);
|
||||
return inst;
|
||||
}
|
||||
|
||||
@@ -211,5 +245,13 @@ int sgsn_inst_init(struct sgsn_instance *sgsn)
|
||||
LOGP(DGPRS, LOGL_FATAL, "Cannot set up SGSN\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if BUILD_IU
|
||||
rc = sgsn_sccp_init(sgsn);
|
||||
if (rc < 0) {
|
||||
LOGP(DGPRS, LOGL_FATAL, "Cannot set up SGSN SCCP layer\n");
|
||||
return rc;
|
||||
}
|
||||
#endif /* #if BUILD_IU */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -133,9 +133,9 @@ enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mmctx)
|
||||
/* We simply assume that the IMSI exists, as long as it is part
|
||||
* of 'our' network */
|
||||
snprintf(mccmnc, sizeof(mccmnc), "%s%s",
|
||||
osmo_mcc_name(mmctx->ra.mcc),
|
||||
osmo_mnc_name(mmctx->ra.mnc, mmctx->ra.mnc_3_digits));
|
||||
if (strncmp(mccmnc, mmctx->imsi, mmctx->ra.mnc_3_digits ? 6 : 5) == 0)
|
||||
osmo_mcc_name(mmctx->ra.lac.plmn.mcc),
|
||||
osmo_mnc_name(mmctx->ra.lac.plmn.mnc, mmctx->ra.lac.plmn.mnc_3_digits));
|
||||
if (strncmp(mccmnc, mmctx->imsi, mmctx->ra.lac.plmn.mnc_3_digits ? 6 : 5) == 0)
|
||||
return SGSN_AUTH_ACCEPTED;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
#include <osmocom/sgsn/sgsn.h>
|
||||
#include <osmocom/sgsn/signal.h>
|
||||
#include <osmocom/sgsn/gprs_utils.h>
|
||||
@@ -31,9 +34,6 @@
|
||||
#include <osmocom/sgsn/pdpctx.h>
|
||||
#include <osmocom/sgsn/mmctx.h>
|
||||
|
||||
#include <gtp.h>
|
||||
#include <pdp.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <time.h>
|
||||
@@ -92,7 +92,7 @@ static int cdr_snprintf_mm(char *buf, size_t size, const char *ev,
|
||||
mmctx->imei,
|
||||
mmctx->msisdn,
|
||||
mmctx->gb.cell_id,
|
||||
mmctx->ra.lac,
|
||||
mmctx->ra.lac.lac,
|
||||
mmctx->hlr,
|
||||
ev);
|
||||
return ret;
|
||||
@@ -194,7 +194,7 @@ static int cdr_snprintf_pdp(char *buf, size_t size, const char *ev,
|
||||
pdp->mm ? pdp->mm->imei : "N/A",
|
||||
pdp->mm ? pdp->mm->msisdn : "N/A",
|
||||
pdp->mm ? pdp->mm->gb.cell_id : -1,
|
||||
pdp->mm ? pdp->mm->ra.lac : -1,
|
||||
pdp->mm ? pdp->mm->ra.lac.lac : -1,
|
||||
pdp->mm ? pdp->mm->hlr : "N/A",
|
||||
ev,
|
||||
(unsigned long ) duration,
|
||||
|
||||
@@ -21,12 +21,14 @@
|
||||
|
||||
#include <osmocom/ctrl/control_if.h>
|
||||
#include <osmocom/ctrl/control_cmd.h>
|
||||
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
#include <osmocom/sgsn/mmctx.h>
|
||||
#include <osmocom/sgsn/pdpctx.h>
|
||||
#include <osmocom/sgsn/sgsn.h>
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
|
||||
#include <pdp.h>
|
||||
|
||||
static int get_subscriber_list(struct ctrl_cmd *cmd, void *d)
|
||||
{
|
||||
|
||||
@@ -42,11 +42,15 @@
|
||||
#include <osmocom/gprs/gprs_bssgp.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
||||
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
#include <osmocom/sgsn/signal.h>
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/sgsn.h>
|
||||
#include <osmocom/sgsn/gprs_ns.h>
|
||||
#include <osmocom/sgsn/gprs_llc.h>
|
||||
#include <osmocom/sgsn/gprs_routing_area.h>
|
||||
#include <osmocom/sgsn/mmctx.h>
|
||||
#include <osmocom/sgsn/gprs_gmm.h>
|
||||
#include <osmocom/sgsn/gprs_sm.h>
|
||||
@@ -55,15 +59,13 @@
|
||||
#include <osmocom/sgsn/gprs_ranap.h>
|
||||
#include <osmocom/sgsn/gprs_gmm_fsm.h>
|
||||
#include <osmocom/sgsn/gprs_mm_state_gb_fsm.h>
|
||||
#include <osmocom/sgsn/gprs_mm_state_iu_fsm.h>
|
||||
#include <osmocom/sgsn/gtp_ggsn.h>
|
||||
#include <osmocom/sgsn/gtp_mme.h>
|
||||
#include <osmocom/sgsn/sgsn_rim.h>
|
||||
#include <osmocom/sgsn/gprs_bssgp.h>
|
||||
#include <osmocom/sgsn/pdpctx.h>
|
||||
|
||||
#include <gtp.h>
|
||||
#include <pdp.h>
|
||||
|
||||
/* TS 23.003: The MSISDN shall take the dummy MSISDN value composed of
|
||||
* 15 digits set to 0 (encoded as an E.164 international number) when
|
||||
* the MSISDN is not available in messages in which the presence of the
|
||||
@@ -142,7 +144,7 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
|
||||
uint16_t nsapi,
|
||||
struct tlv_parsed *tp)
|
||||
{
|
||||
struct gprs_ra_id raid;
|
||||
struct osmo_routing_area_id rai = {};
|
||||
struct sgsn_pdp_ctx *pctx;
|
||||
struct pdp_t *pdp;
|
||||
uint64_t imsi_ui64;
|
||||
@@ -166,6 +168,9 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
|
||||
pdp->priv = pctx;
|
||||
pctx->lib = pdp;
|
||||
|
||||
/* Back up our own local TEID in case we update the library one with RNC TEID when setting up Direct Tunnel: */
|
||||
pctx->sgsn_teid_own = pdp->teid_own;
|
||||
|
||||
//pdp->peer = /* sockaddr_in of GGSN (receive) */
|
||||
//pdp->ipif = /* not used by library */
|
||||
pdp->version = ggsn->gtp_version;
|
||||
@@ -268,10 +273,10 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
|
||||
|
||||
/* Routing Area Identifier with LAC and RAC fixed values, as
|
||||
* requested in 29.006 7.3.1 */
|
||||
raid = mmctx->ra;
|
||||
raid.lac = 0xFFFE;
|
||||
raid.rac = 0xFF;
|
||||
gsm48_encode_ra((struct gsm48_ra_id *)pdp->rai.v, &raid);
|
||||
rai = mmctx->ra;
|
||||
rai.lac.lac = 0xFFFE;
|
||||
rai.rac = 0xFF;
|
||||
osmo_routing_area_id_encode_buf(pdp->rai.v, pdp->rai.l, &rai);
|
||||
|
||||
/* Encode User Location Information accordint to TS 29.060 7.7.51 */
|
||||
pdp->userloc_given = 1;
|
||||
@@ -286,20 +291,23 @@ struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
|
||||
pdp->userloc_given = 1;
|
||||
pdp->userloc.l = 8;
|
||||
pdp->userloc.v[0] = 0; /* CGI for GERAN */
|
||||
bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->gb.cell_id);
|
||||
bssgp_create_cell_id2(&pdp->userloc.v[1], 8, &mmctx->ra, mmctx->gb.cell_id);
|
||||
break;
|
||||
case MM_CTX_T_UTRAN_Iu:
|
||||
pdp->userloc.v[0] = 1; /* SAI for UTRAN */
|
||||
/* SAI is like CGI but with SAC instead of CID, so we can abuse this function */
|
||||
bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->iu.sac);
|
||||
bssgp_create_cell_id2(&pdp->userloc.v[1], 8, &mmctx->ra, mmctx->iu.sac);
|
||||
break;
|
||||
}
|
||||
|
||||
/* include the IMEI(SV) */
|
||||
pdp->imeisv_given = 1;
|
||||
gsm48_encode_bcd_number(&pdp->imeisv.v[0], 8, 0, mmctx->imei);
|
||||
pdp->imeisv.l = pdp->imeisv.v[0];
|
||||
memmove(&pdp->imeisv.v[0], &pdp->imeisv.v[1], 8);
|
||||
/* optional include the IMEI(SV) */
|
||||
if (mmctx->imei[0] != '\0') {
|
||||
memset(&pdp->imeisv.v[0], 0, 8);
|
||||
pdp->imeisv_given = 1;
|
||||
gsm48_encode_bcd_number(&pdp->imeisv.v[0], 8, 0, mmctx->imei);
|
||||
pdp->imeisv.l = 8;
|
||||
memmove(&pdp->imeisv.v[0], &pdp->imeisv.v[1], 8);
|
||||
}
|
||||
|
||||
/* change pdp state to 'requested' */
|
||||
pctx->state = PDP_STATE_CR_REQ;
|
||||
@@ -378,6 +386,7 @@ int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx)
|
||||
rc = gsm48_tx_gsm_act_pdp_acc(pctx);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
pctx->ue_pdp_active = true;
|
||||
|
||||
if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) {
|
||||
/* Send SNDCP XID to MS */
|
||||
@@ -430,7 +439,7 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
||||
} else if (pctx->mm->ran_type == MM_CTX_T_UTRAN_Iu) {
|
||||
#ifdef BUILD_IU
|
||||
/* Activate a radio bearer */
|
||||
iu_rab_act_ps(pdp->nsapi, pctx);
|
||||
sgsn_pdp_ctx_iu_rab_activate(pctx, pdp->nsapi);
|
||||
return 0;
|
||||
#else
|
||||
return -ENOTSUP;
|
||||
@@ -464,13 +473,6 @@ reject:
|
||||
return EOF;
|
||||
}
|
||||
|
||||
void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen)
|
||||
{
|
||||
pdp->lib->gsnlu.l = alen;
|
||||
memcpy(pdp->lib->gsnlu.v, addr, alen);
|
||||
gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0);
|
||||
}
|
||||
|
||||
void sgsn_ggsn_echo_req(struct sgsn_ggsn_ctx *ggc)
|
||||
{
|
||||
LOGGGSN(ggc, LOGL_INFO, "GTP Tx Echo Request\n");
|
||||
@@ -562,14 +564,16 @@ static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
||||
} else {
|
||||
#ifdef BUILD_IU
|
||||
/* Deactivate radio bearer */
|
||||
ranap_iu_rab_deact(pctx->mm->iu.ue_ctx, 1);
|
||||
sgsn_pdp_ctx_iu_rab_deactivate(pctx, 1);
|
||||
#else
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Confirm deactivation of PDP context to MS */
|
||||
rc = gsm48_tx_gsm_deact_pdp_acc(pctx);
|
||||
if (pctx->ue_pdp_active) {
|
||||
/* Confirm deactivation of PDP context to MS */
|
||||
rc = gsm48_tx_gsm_deact_pdp_acc(pctx);
|
||||
pctx->ue_pdp_active = false;
|
||||
}
|
||||
} else {
|
||||
LOGPDPCTXP(LOGL_NOTICE, pctx,
|
||||
"Not deactivating SNDCP layer since the MM context "
|
||||
@@ -649,6 +653,52 @@ static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called whenever a PDP context is updated from the GGSN for any reason */
|
||||
static int cb_update_context_ind(struct pdp_t *pdp)
|
||||
{
|
||||
struct sgsn_pdp_ctx *pctx;
|
||||
struct sgsn_mm_ctx *mm;
|
||||
int rc;
|
||||
|
||||
LOGPDPX(DGPRS, LOGL_INFO, pdp, "Context %p was updated\n", pdp);
|
||||
|
||||
pctx = pdp->priv;
|
||||
if (!pctx) {
|
||||
LOGP(DGPRS, LOGL_NOTICE,
|
||||
"GTP DATA IND from GGSN for unknown PDP\n");
|
||||
return -EIO;
|
||||
}
|
||||
mm = pctx->mm;
|
||||
if (!mm) {
|
||||
LOGP(DGPRS, LOGL_ERROR,
|
||||
"PDP context (address=%u) without MM context!\n",
|
||||
pctx->address);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (mm->ran_type == MM_CTX_T_UTRAN_Iu) {
|
||||
#ifdef BUILD_IU
|
||||
if (pdp->dir_tun_flags.v[0] & 0x04) { /* EI bit set ? */
|
||||
/* GGSN informed us that it received an Error Indication when sending DL data to the RNC.
|
||||
* This probably means the RNC lost its state, aka crashed or was rebooted.
|
||||
* Page the UE so it re-creates the state at the RNC. */
|
||||
LOGMMCTXP(LOGL_INFO, mm,
|
||||
"GGSN received ErrorInd from RNC while tx DL data. Paging UE in state %s\n",
|
||||
osmo_fsm_inst_state_name(mm->gmm_fsm));
|
||||
rc = osmo_fsm_inst_dispatch(mm->iu.mm_state_fsm, E_PMM_RX_GGSN_GTPU_DT_EI, pctx);
|
||||
rc = gtp_update_context_resp(sgsn->gsn, pdp,
|
||||
GTPCAUSE_ACC_REQ);
|
||||
ranap_iu_page_ps(mm->imsi, &mm->p_tmsi, mm->ra.lac.lac, mm->ra.rac);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
rc = gtp_update_context_resp(sgsn->gsn, pdp,
|
||||
GTPCAUSE_ACC_REQ);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Called whenever a PDP context is deleted for any reason */
|
||||
static int cb_delete_context(struct pdp_t *pdp)
|
||||
{
|
||||
@@ -780,7 +830,9 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
|
||||
#ifdef BUILD_IU
|
||||
/* Ignore the packet for now and page the UE to get the RAB
|
||||
* reestablished */
|
||||
ranap_iu_page_ps(mm->imsi, &mm->p_tmsi, mm->ra.lac, mm->ra.rac);
|
||||
LOGMMCTXP(LOGL_INFO, mm, "Rx GTP for UE in PMM state %s, paging it\n",
|
||||
osmo_fsm_inst_state_name(mm->iu.mm_state_fsm));
|
||||
ranap_iu_page_ps(mm->imsi, &mm->p_tmsi, mm->ra.lac.lac, mm->ra.rac);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
@@ -816,7 +868,7 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
|
||||
LOGMMCTXP(LOGL_INFO, mm, "Paging MS in GMM state %s, MM state %s\n",
|
||||
osmo_fsm_inst_state_name(mm->gmm_fsm),
|
||||
osmo_fsm_inst_state_name(mm->gb.mm_state_fsm));
|
||||
sgsn_bssgp_page_ps_ra(mm);
|
||||
sgsn_ra_geran_page_ra(&mm->ra, mm);
|
||||
|
||||
/* FIXME: queue the packet we received from GTP */
|
||||
break;
|
||||
@@ -842,14 +894,14 @@ static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
|
||||
}
|
||||
|
||||
/* Called by SNDCP when it has received/re-assembled a N-PDU */
|
||||
int sgsn_gtp_data_req(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi,
|
||||
int sgsn_gtp_data_req(struct osmo_routing_area_id *rai, int32_t tlli, uint8_t nsapi,
|
||||
struct msgb *msg, uint32_t npdu_len, uint8_t *npdu)
|
||||
{
|
||||
struct sgsn_mm_ctx *mmctx;
|
||||
struct sgsn_pdp_ctx *pdp;
|
||||
|
||||
/* look-up the MM context for this message */
|
||||
mmctx = sgsn_mm_ctx_by_tlli(tlli, ra_id);
|
||||
mmctx = sgsn_mm_ctx_by_tlli(tlli, rai);
|
||||
if (!mmctx) {
|
||||
LOGP(DGPRS, LOGL_ERROR,
|
||||
"Cannot find MM CTX for TLLI %08x\n", tlli);
|
||||
@@ -943,6 +995,7 @@ int sgsn_gtp_init(struct sgsn_instance *sgi)
|
||||
}
|
||||
|
||||
/* Register callbackcs with libgtp */
|
||||
gtp_set_cb_update_context_ind(gsn, cb_update_context_ind);
|
||||
gtp_set_cb_delete_context(gsn, cb_delete_context);
|
||||
gtp_set_cb_conf(gsn, cb_conf);
|
||||
gtp_set_cb_recovery3(gsn, cb_recovery3);
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
#include <osmocom/gprs/gprs_bssgp.h>
|
||||
#include <osmocom/gprs/gprs_bssgp_bss.h>
|
||||
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
|
||||
#include <osmocom/vty/telnet_interface.h>
|
||||
#include <osmocom/vty/logging.h>
|
||||
#include <osmocom/vty/stats.h>
|
||||
@@ -64,10 +66,10 @@
|
||||
#include <osmocom/sgsn/gprs_ranap.h>
|
||||
#include <osmocom/sgsn/gprs_ns.h>
|
||||
#include <osmocom/sgsn/gprs_bssgp.h>
|
||||
#include <osmocom/sgsn/gprs_routing_area.h>
|
||||
#include <osmocom/sgsn/gprs_subscriber.h>
|
||||
#include <osmocom/sgsn/gtp.h>
|
||||
|
||||
#include <gtp.h>
|
||||
#include <osmocom/sgsn/sgsn_rim.h>
|
||||
|
||||
#include "../../config.h"
|
||||
@@ -75,7 +77,7 @@
|
||||
#if BUILD_IU
|
||||
#include <osmocom/sigtran/osmo_ss7.h>
|
||||
#include <osmocom/sigtran/protocol/m3ua.h>
|
||||
#include <osmocom/ranap/iu_client.h>
|
||||
#include <osmocom/sgsn/iu_client.h>
|
||||
#endif
|
||||
|
||||
#define _GNU_SOURCE
|
||||
@@ -348,6 +350,11 @@ static struct log_info_cat gprs_categories[] = {
|
||||
.description = "RAN Information Management (RIM)",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DRA] = {
|
||||
.name = "DRA",
|
||||
.description = "Routing Area",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct log_info gprs_log_info = {
|
||||
@@ -365,9 +372,6 @@ static bool file_exists(const char *path)
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc;
|
||||
#if BUILD_IU
|
||||
struct osmo_sccp_instance *sccp;
|
||||
#endif
|
||||
|
||||
srand(time(NULL));
|
||||
tall_sgsn_ctx = talloc_named_const(NULL, 0, "osmo_sgsn");
|
||||
@@ -470,20 +474,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
#if BUILD_IU
|
||||
/* Note that these are mostly defaults and can be overriden from the VTY */
|
||||
sccp = osmo_sccp_simple_client_on_ss7_id(tall_sgsn_ctx,
|
||||
sgsn->cfg.iu.cs7_instance,
|
||||
"OsmoSGSN",
|
||||
(23 << 3) + 4,
|
||||
OSMO_SS7_ASP_PROT_M3UA,
|
||||
0, "localhost",
|
||||
0, "localhost");
|
||||
if (!sccp) {
|
||||
printf("Setting up SCCP client failed.\n");
|
||||
return 8;
|
||||
}
|
||||
|
||||
ranap_iu_init(tall_sgsn_ctx, DRANAP, "OsmoSGSN-IuPS", sccp, gsm0408_gprs_rcvmsg_iu, sgsn_ranap_iu_event);
|
||||
ranap_iu_init(tall_sgsn_ctx);
|
||||
#endif
|
||||
|
||||
if (daemonize) {
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <time.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
@@ -32,6 +34,9 @@
|
||||
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
|
||||
#include <osmocom/gsm/apn.h>
|
||||
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
#include <osmocom/gtp/pdp.h>
|
||||
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/sgsn.h>
|
||||
#include <osmocom/gprs/gprs_ns2.h>
|
||||
@@ -39,6 +44,7 @@
|
||||
#include <osmocom/sgsn/gprs_gmm.h>
|
||||
#include <osmocom/sgsn/gprs_bssgp.h>
|
||||
#include <osmocom/sgsn/mmctx.h>
|
||||
#include <osmocom/sgsn/gprs_routing_area.h>
|
||||
#include <osmocom/sgsn/gtp_ggsn.h>
|
||||
#include <osmocom/sgsn/gtp_mme.h>
|
||||
#include <osmocom/sgsn/vty.h>
|
||||
@@ -51,17 +57,13 @@
|
||||
#include <osmocom/vty/misc.h>
|
||||
#include <osmocom/crypt/gprs_cipher.h>
|
||||
#include <osmocom/crypt/utran_cipher.h>
|
||||
#include <osmocom/abis/ipa.h>
|
||||
|
||||
#include <osmocom/gprs/gprs_bssgp.h>
|
||||
|
||||
#include <pdp.h>
|
||||
#include <gtp.h>
|
||||
|
||||
#include "../../config.h"
|
||||
|
||||
#ifdef BUILD_IU
|
||||
#include <osmocom/ranap/iu_client.h>
|
||||
#include <osmocom/sgsn/iu_client.h>
|
||||
#endif
|
||||
|
||||
static struct sgsn_config *g_cfg = NULL;
|
||||
@@ -96,7 +98,7 @@ const struct value_string sgsn_auth_pol_strs[] = {
|
||||
|
||||
/* Non spec timer */
|
||||
#define NONSPEC_X1001_SECS 5 /* wait for a RANAP Release Complete */
|
||||
|
||||
#define RANAP_TRafR_SECS 5 /* wait for a RANAP Release Complete */
|
||||
|
||||
struct osmo_tdef sgsn_T_defs[] = {
|
||||
{ .T=3312, .default_val=GSM0408_T3312_SECS, .desc="Periodic RA Update timer (s)" },
|
||||
@@ -114,6 +116,7 @@ struct osmo_tdef sgsn_T_defs[] = {
|
||||
/* non spec timers */
|
||||
{ .T=-1001, .default_val=NONSPEC_X1001_SECS, .desc="RANAP Release timeout. Wait for RANAP Release Complete."
|
||||
"On expiry release Iu connection (s)" },
|
||||
{ .T=-1002, .default_val=RANAP_TRafR_SECS, .desc="TRafR, Maximum time for Reset procedure in the CN (s)" },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -233,6 +236,12 @@ static void config_write_mme(struct vty *vty, const struct sgsn_mme_ctx *mme, co
|
||||
osmo_mcc_name(rt->tai.mcc), osmo_mnc_name(rt->tai.mnc, rt->tai.mnc_3_digits),
|
||||
rt->tai.tac, VTY_NEWLINE);
|
||||
}
|
||||
if (mme->gummei_valid)
|
||||
vty_out(vty, "%s gummei %s %s %d %d%s",
|
||||
prefix,
|
||||
osmo_mcc_name(mme->gummei.plmn.mcc),
|
||||
osmo_mnc_name(mme->gummei.plmn.mnc, mme->gummei.plmn.mnc_3_digits),
|
||||
mme->gummei.mme.group_id, mme->gummei.mme.code, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static int config_write_sgsn(struct vty *vty)
|
||||
@@ -404,6 +413,11 @@ DEFUN(cfg_sgsn_state_dir, cfg_sgsn_state_dir_cmd,
|
||||
"Set the directory for the GTP State file\n"
|
||||
"Local Directory\n")
|
||||
{
|
||||
if (mkdir(argv[0], 0755) == -1 && errno != EEXIST) {
|
||||
vty_out(vty, "%% Failed to create state-dir: %s%s", argv[0], VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
osmo_talloc_replace_string(sgsn, &sgsn->cfg.gtp_statedir, argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
@@ -644,7 +658,7 @@ static void vty_dump_mmctx(struct vty *vty, const char *pfx,
|
||||
pfx, mm->msisdn, id, mm->hlr, VTY_NEWLINE);
|
||||
vty_out(vty, "%s GMM State: %s, Routeing Area: %s, Cell ID: %u%s",
|
||||
pfx, osmo_fsm_inst_state_name(mm->gmm_fsm),
|
||||
osmo_rai_name(&mm->ra), mm->gb.cell_id, VTY_NEWLINE);
|
||||
osmo_rai_name2(&mm->ra), mm->gb.cell_id, VTY_NEWLINE);
|
||||
vty_out(vty, "%s MM State: %s, RAN Type: %s%s", pfx, mm_state_name,
|
||||
get_value_string(sgsn_ran_type_names, mm->ran_type), VTY_NEWLINE);
|
||||
|
||||
@@ -662,11 +676,10 @@ DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn",
|
||||
SHOW_STR "Display information about the SGSN")
|
||||
{
|
||||
if (sgsn->gsup_client) {
|
||||
struct ipa_client_conn *link = sgsn->gsup_client->link;
|
||||
vty_out(vty,
|
||||
" Remote authorization: %sconnected to %s:%d via GSUP%s",
|
||||
sgsn->gsup_client->is_connected ? "" : "not ",
|
||||
link->addr, link->port,
|
||||
vty_out(vty, " Remote authorization: %sconnected to %s:%d via GSUP%s",
|
||||
osmo_gsup_client_is_connected(sgsn->gsup_client) ? "" : "not ",
|
||||
osmo_gsup_client_get_rem_addr(sgsn->gsup_client),
|
||||
osmo_gsup_client_get_rem_port(sgsn->gsup_client),
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
if (sgsn->gsn)
|
||||
@@ -990,8 +1003,17 @@ static void subscr_dump_full_vty(struct vty *vty, struct gprs_subscr *gsub, int
|
||||
}
|
||||
|
||||
llist_for_each_entry(pdp, &gsub->sgsn_data->pdp_list, list) {
|
||||
vty_out(vty, " PDP info: Id: %d, Type: 0x%04x, APN: '%s'",
|
||||
pdp->context_id, pdp->pdp_type, pdp->apn_str);
|
||||
char ip_str[INET6_ADDRSTRLEN] = { 0 };
|
||||
|
||||
vty_out(vty, " PDP info: Id: %d, Addr(Org: 0x%02x Type: 0x%02x",
|
||||
pdp->context_id, pdp->pdp_type_org, pdp->pdp_type_nr);
|
||||
|
||||
if (pdp->pdp_address[0].u.sa.sa_family != AF_UNSPEC)
|
||||
vty_out(vty, " Addr[0]: %s", osmo_sockaddr_ntop(&pdp->pdp_address[0].u.sa, ip_str));
|
||||
if (pdp->pdp_address[0].u.sa.sa_family != AF_UNSPEC)
|
||||
vty_out(vty, " Addr[1]: %s", osmo_sockaddr_ntop(&pdp->pdp_address[1].u.sa, ip_str));
|
||||
|
||||
vty_out(vty, ") APN: '%s'", pdp->apn_str);
|
||||
|
||||
if (pdp->qos_subscribed_len)
|
||||
vty_out(vty, " QoS: %s", osmo_hexdump(pdp->qos_subscribed, pdp->qos_subscribed_len));
|
||||
@@ -1317,7 +1339,7 @@ DEFUN(page_subscr, page_subscr_info_cmd,
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
sgsn_bssgp_page_ps_ra(mm);
|
||||
sgsn_ra_geran_page_ra(&mm->ra, mm);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1766,6 +1788,44 @@ DEFUN(cfg_mme_no_ran_info_relay_default, cfg_mme_no_ran_info_relay_default_cmd,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mme_mmei, cfg_mme_mmei_cmd,
|
||||
"gummei <0-999> <0-999> <0-65535> <0-254>",
|
||||
"Configure the mme\n" "MCC\n" "MNC\n" "MME GroupId\n" "MME Code")
|
||||
{
|
||||
struct sgsn_mme_ctx *mme = (struct sgsn_mme_ctx *) vty->index;
|
||||
|
||||
const char *mcc = argv[0];
|
||||
const char *mnc = argv[1];
|
||||
const char *group_id = argv[2];
|
||||
const char *code = argv[3];
|
||||
|
||||
if (osmo_mcc_from_str(mcc, &mme->gummei.plmn.mcc)) {
|
||||
vty_out(vty, "%% Error decoding MCC: %s%s", mcc, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (osmo_mnc_from_str(mnc, &mme->gummei.plmn.mnc, &mme->gummei.plmn.mnc_3_digits)) {
|
||||
vty_out(vty, "%% Error decoding MNC: %s%s", mnc, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
mme->gummei.mme.code = atoi(code);
|
||||
mme->gummei.mme.group_id = atoi(group_id);
|
||||
mme->gummei_valid = true;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_no_mme_mmei, cfg_no_mme_mmei_cmd,
|
||||
"no gummei",
|
||||
NO_STR "Remove gummei")
|
||||
{
|
||||
struct sgsn_mme_ctx *mme = (struct sgsn_mme_ctx *) vty->index;
|
||||
mme->gummei_valid = false;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int sgsn_vty_init(struct sgsn_config *cfg)
|
||||
{
|
||||
g_cfg = cfg;
|
||||
@@ -1842,6 +1902,8 @@ int sgsn_vty_init(struct sgsn_config *cfg)
|
||||
install_element(MME_NODE, &cfg_mme_remote_ip_cmd);
|
||||
install_element(MME_NODE, &cfg_mme_ran_info_relay_default_cmd);
|
||||
install_element(MME_NODE, &cfg_mme_no_ran_info_relay_default_cmd);
|
||||
install_element(MME_NODE, &cfg_mme_mmei_cmd);
|
||||
install_element(MME_NODE, &cfg_no_mme_mmei_cmd);
|
||||
install_element(MME_NODE, &cfg_mme_ran_info_relay_tai_cmd);
|
||||
install_element(MME_NODE, &cfg_mme_no_ran_info_relay_tai_cmd);
|
||||
|
||||
@@ -1859,9 +1921,6 @@ int sgsn_parse_config(const char *config_file)
|
||||
/* make sure sgsn_vty_init() was called before this */
|
||||
OSMO_ASSERT(g_cfg);
|
||||
|
||||
g_cfg->gea_encryption_mask = 0x1; /* support GEA0 by default unless specific encryption config exists */
|
||||
g_cfg->uea_encryption_mask = (1 << OSMO_UTRAN_UEA0); /* support UEA0 by default unless specific encryption config exists */
|
||||
|
||||
rc = vty_read_config_file(config_file, NULL);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
|
||||
|
||||
@@ -388,7 +388,7 @@ found:
|
||||
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: th->doff != cs->cs_tcp.doff\n");
|
||||
if(ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) {
|
||||
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)\n");
|
||||
DEBUGP(DSLHC, "slhc_compress(): ip->ihl = %i\n", ip->ihl);
|
||||
DEBUGP(DSLHC, "slhc_compress(): ip->ihl = %d\n", ip->ihl);
|
||||
DEBUGP(DSLHC, "slhc_compress(): ip+1 = %s\n",
|
||||
osmo_hexdump_nospc((uint8_t*)(ip+1),((ip->ihl)-5)*4));
|
||||
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: cs->cs_ipopt = %s\n",
|
||||
@@ -396,7 +396,7 @@ found:
|
||||
}
|
||||
if(th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0) {
|
||||
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)\n");
|
||||
DEBUGP(DSLHC, "slhc_compress(): th->doff = %i\n", th->doff);
|
||||
DEBUGP(DSLHC, "slhc_compress(): th->doff = %d\n", th->doff);
|
||||
DEBUGP(DSLHC, "slhc_compress(): th+1 = %s\n",
|
||||
osmo_hexdump_nospc((uint8_t*)(th+1),((th->doff)-5)*4));
|
||||
DEBUGP(DSLHC, "slhc_compress(): cs->cs_tcpopt = %s\n",
|
||||
@@ -538,8 +538,8 @@ found:
|
||||
cp += 2;
|
||||
/* deltaS is now the size of the change section of the compressed header */
|
||||
|
||||
DEBUGP(DSLHC, "slhc_compress(): Delta-list length (deltaS) = %li\n",deltaS);
|
||||
DEBUGP(DSLHC, "slhc_compress(): Original header len (hlen) = %i\n",hlen);
|
||||
DEBUGP(DSLHC, "slhc_compress(): Delta-list length (deltaS) = %ld\n", deltaS);
|
||||
DEBUGP(DSLHC, "slhc_compress(): Original header len (hlen) = %d\n", hlen);
|
||||
|
||||
memcpy(cp,new_seq,deltaS); /* Write list of deltas */
|
||||
memcpy(cp+deltaS,icp+hlen,isize-hlen);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
SUBDIRS = \
|
||||
gprs \
|
||||
gtphub \
|
||||
gprs_routing_area \
|
||||
sgsn \
|
||||
xid \
|
||||
sndcp_xid \
|
||||
@@ -32,6 +33,8 @@ EXTRA_DIST = \
|
||||
$(TESTSUITE) \
|
||||
vty_test_runner.py \
|
||||
ctrl_test_runner.py \
|
||||
osmo-sgsn-accept-all.cfg \
|
||||
osmo-sgsn.cfg \
|
||||
osmo-sgsn_test-nodes.vty \
|
||||
$(NULL)
|
||||
|
||||
@@ -62,7 +65,7 @@ vty-python-test: $(top_builddir)/src/sgsn/osmo-sgsn
|
||||
vty-transcript-test: $(top_builddir)/src/sgsn/osmo-sgsn
|
||||
osmo_verify_transcript_vty.py -v \
|
||||
-n OsmoSGSN -p 4245 \
|
||||
-r "$(top_builddir)/src/sgsn/osmo-sgsn -c $(top_srcdir)/doc/examples/osmo-sgsn/osmo-sgsn.cfg" \
|
||||
-r "$(top_builddir)/src/sgsn/osmo-sgsn -c $(top_srcdir)/tests/osmo-sgsn.cfg" \
|
||||
$(U) $${T:-$(srcdir)/osmo-sgsn*.vty}
|
||||
rm -f $(builddir)/gsn_restart
|
||||
|
||||
|
||||
@@ -145,8 +145,7 @@ class TestCtrlBase(unittest.TestCase):
|
||||
|
||||
class TestCtrlSGSN(TestCtrlBase):
|
||||
def ctrl_command(self):
|
||||
return ["./src/sgsn/osmo-sgsn", "-c",
|
||||
"doc/examples/osmo-sgsn/osmo-sgsn.cfg"]
|
||||
return ["./src/sgsn/osmo-sgsn", "-c", "tests/osmo-sgsn.cfg"]
|
||||
|
||||
def ctrl_app(self):
|
||||
return (4251, "./src/sgsn/osmo-sgsn", "OsmoSGSN", "sgsn")
|
||||
|
||||
98
tests/gprs_routing_area/Makefile.am
Normal file
98
tests/gprs_routing_area/Makefile.am
Normal file
@@ -0,0 +1,98 @@
|
||||
AM_CPPFLAGS = \
|
||||
$(all_includes) \
|
||||
-I$(top_srcdir)/include \
|
||||
$(NULL)
|
||||
|
||||
AM_CFLAGS = \
|
||||
-Wall \
|
||||
-ggdb3 \
|
||||
$(LIBOSMOCORE_CFLAGS) \
|
||||
$(LIBOSMOCTRL_CFLAGS) \
|
||||
$(LIBOSMOABIS_CFLAGS) \
|
||||
$(LIBOSMOGSM_CFLAGS) \
|
||||
$(LIBOSMOGSUPCLIENT_CFLAGS) \
|
||||
$(LIBCARES_CFLAGS) \
|
||||
$(LIBGTP_CFLAGS) \
|
||||
$(NULL)
|
||||
if BUILD_IU
|
||||
AM_CFLAGS += \
|
||||
$(LIBASN1C_CFLAGS) \
|
||||
$(LIBOSMOSIGTRAN_CFLAGS) \
|
||||
$(LIBOSMORANAP_CFLAGS) \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
AM_LDFLAGS = -no-install
|
||||
|
||||
EXTRA_DIST = \
|
||||
gprs_routing_area_test.ok \
|
||||
$(NULL)
|
||||
|
||||
check_PROGRAMS = \
|
||||
gprs_routing_area_test \
|
||||
$(NULL)
|
||||
|
||||
gprs_routing_area_test_SOURCES = \
|
||||
gprs_routing_area_test.c \
|
||||
$(NULL)
|
||||
|
||||
gprs_routing_area_test_LDADD = \
|
||||
$(top_builddir)/src/sgsn/apn.o \
|
||||
$(top_builddir)/src/sgsn/gprs_bssgp.o \
|
||||
$(top_builddir)/src/sgsn/gprs_llc.o \
|
||||
$(top_builddir)/src/sgsn/gprs_ns.o \
|
||||
$(top_builddir)/src/sgsn/gprs_sndcp.o \
|
||||
$(top_builddir)/src/sgsn/gprs_gmm_attach.o \
|
||||
$(top_builddir)/src/sgsn/gprs_gmm.o \
|
||||
$(top_builddir)/src/sgsn/gprs_gmm_fsm.o \
|
||||
$(top_builddir)/src/sgsn/gprs_gmm_util.o \
|
||||
$(top_builddir)/src/sgsn/gprs_mm_state_gb_fsm.o \
|
||||
$(top_builddir)/src/sgsn/gprs_routing_area.o \
|
||||
$(top_builddir)/src/sgsn/gtp_ggsn.o \
|
||||
$(top_builddir)/src/sgsn/gtp_mme.o \
|
||||
$(top_builddir)/src/sgsn/mmctx.o \
|
||||
$(top_builddir)/src/sgsn/pdpctx.o \
|
||||
$(top_builddir)/src/sgsn/sgsn.o \
|
||||
$(top_builddir)/src/sgsn/sgsn_cdr.o \
|
||||
$(top_builddir)/src/sgsn/sgsn_ctrl.o \
|
||||
$(top_builddir)/src/sgsn/sgsn_vty.o \
|
||||
$(top_builddir)/src/sgsn/sgsn_libgtp.o \
|
||||
$(top_builddir)/src/sgsn/sgsn_auth.o \
|
||||
$(top_builddir)/src/sgsn/gprs_subscriber.o \
|
||||
$(top_builddir)/src/sgsn/gprs_llc_xid.o \
|
||||
$(top_builddir)/src/sgsn/gprs_sndcp_xid.o \
|
||||
$(top_builddir)/src/sgsn/slhc.o \
|
||||
$(top_builddir)/src/sgsn/gprs_sm.o \
|
||||
$(top_builddir)/src/sgsn/gprs_sndcp_comp.o \
|
||||
$(top_builddir)/src/sgsn/gprs_sndcp_pcomp.o \
|
||||
$(top_builddir)/src/sgsn/v42bis.o \
|
||||
$(top_builddir)/src/sgsn/gprs_sndcp_dcomp.o \
|
||||
$(top_builddir)/src/sgsn/sgsn_rim.o \
|
||||
$(top_builddir)/src/gprs/gprs_utils.o \
|
||||
$(top_builddir)/src/gprs/gprs_llc_parse.o \
|
||||
$(top_builddir)/src/gprs/crc24.o \
|
||||
$(top_builddir)/src/gprs/sgsn_ares.o \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBOSMOGSUPCLIENT_LIBS) \
|
||||
$(LIBCARES_LIBS) \
|
||||
$(LIBGTP_LIBS) \
|
||||
-lrt \
|
||||
-lm \
|
||||
$(NULL)
|
||||
|
||||
if BUILD_IU
|
||||
gprs_routing_area_test_LDADD += \
|
||||
$(top_builddir)/src/sgsn/gprs_ranap.o \
|
||||
$(top_builddir)/src/sgsn/gprs_mm_state_iu_fsm.o \
|
||||
$(top_builddir)/src/sgsn/iu_client.o \
|
||||
$(top_builddir)/src/sgsn/iu_rnc.o \
|
||||
$(top_builddir)/src/sgsn/iu_rnc_fsm.o \
|
||||
$(top_builddir)/src/sgsn/sccp.o \
|
||||
$(LIBOSMORANAP_LIBS) \
|
||||
$(LIBOSMOSIGTRAN_LIBS) \
|
||||
$(LIBASN1C_LIBS) \
|
||||
$(NULL)
|
||||
endif
|
||||
715
tests/gprs_routing_area/gprs_routing_area_test.c
Normal file
715
tests/gprs_routing_area/gprs_routing_area_test.c
Normal file
@@ -0,0 +1,715 @@
|
||||
/* Test the SGSN routing ares */
|
||||
/*
|
||||
* (C) 2024 by sysmocom s.f.m.c. GmbH
|
||||
* All Rights Reserved
|
||||
* Author: Alexander Couzens <lynxis@fe80.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/rate_ctr.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/gsm/apn.h>
|
||||
#include <osmocom/gsm/gsm23003.h>
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#include <osmocom/gsm/gsup.h>
|
||||
#include <osmocom/gprs/gprs_bssgp.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
|
||||
#include <osmocom/gsupclient/gsup_client.h>
|
||||
|
||||
#include <osmocom/sgsn/gprs_llc.h>
|
||||
#include <osmocom/sgsn/mmctx.h>
|
||||
#include <osmocom/sgsn/sgsn.h>
|
||||
#include <osmocom/sgsn/gprs_gmm.h>
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
#include <osmocom/sgsn/gprs_routing_area.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
void *tall_sgsn_ctx;
|
||||
struct sgsn_instance *sgsn;
|
||||
|
||||
struct paging_exp {
|
||||
uint16_t nsei;
|
||||
uint16_t bvci;
|
||||
/* paged when we send one paging request */
|
||||
bool paged;
|
||||
/* valid when this entry contains valid data */
|
||||
bool valid;
|
||||
};
|
||||
|
||||
struct paging_exp g_paging[4];
|
||||
|
||||
static void cleanup_test(void)
|
||||
{
|
||||
TALLOC_FREE(sgsn);
|
||||
}
|
||||
|
||||
/* Create RA, free RA */
|
||||
static void test_routing_area_create(void)
|
||||
{
|
||||
struct sgsn_ra *ra;
|
||||
struct osmo_routing_area_id rai = {
|
||||
.lac = {
|
||||
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
|
||||
.lac = 23
|
||||
},
|
||||
.rac = 42
|
||||
};
|
||||
|
||||
printf("Testing Routing Area create/free\n");
|
||||
|
||||
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
|
||||
ra = sgsn_ra_find_or_create(&rai, RA_TYPE_GERAN_Gb);
|
||||
OSMO_ASSERT(ra);
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
|
||||
sgsn_ra_free(ra);
|
||||
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
|
||||
|
||||
/* Cleanup */
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
static void test_routing_area_free_empty(void)
|
||||
{
|
||||
|
||||
struct sgsn_ra *ra;
|
||||
struct sgsn_ra_cell *cell_a;
|
||||
struct osmo_routing_area_id rai = {
|
||||
.lac = {
|
||||
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
|
||||
.lac = 24
|
||||
},
|
||||
.rac = 43
|
||||
};
|
||||
|
||||
uint16_t cell_id = 9999;
|
||||
uint16_t nsei = 2, bvci = 3;
|
||||
|
||||
printf("Testing Routing Area create/free\n");
|
||||
|
||||
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
|
||||
ra = sgsn_ra_find_or_create(&rai, RA_TYPE_GERAN_Gb);
|
||||
OSMO_ASSERT(ra);
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
|
||||
cell_a = sgsn_ra_cell_alloc_geran(ra, cell_id, nsei, bvci);
|
||||
OSMO_ASSERT(cell_a);
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
OSMO_ASSERT(llist_count(&ra->cells_alive_list) == 1);
|
||||
|
||||
sgsn_ra_free(ra);
|
||||
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
|
||||
|
||||
ra = sgsn_ra_find_or_create(&rai, RA_TYPE_GERAN_Gb);
|
||||
OSMO_ASSERT(ra);
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
|
||||
cell_a = sgsn_ra_cell_alloc_geran(ra, cell_id, nsei, bvci);
|
||||
OSMO_ASSERT(cell_a);
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
OSMO_ASSERT(llist_count(&ra->cells_alive_list) == 1);
|
||||
|
||||
sgsn_ra_free(ra);
|
||||
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
/* Create RA, use different find functiosn, free RA */
|
||||
static void test_routing_area_find(void)
|
||||
{
|
||||
struct sgsn_ra *ra_a, *ra_b;
|
||||
struct sgsn_ra_cell *cell_a, *cell_b;
|
||||
struct osmo_routing_area_id rai = {
|
||||
.lac = {
|
||||
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
|
||||
.lac = 24
|
||||
},
|
||||
.rac = 43
|
||||
};
|
||||
|
||||
uint16_t cell_id = 9999, cell_id_not_found = 44;
|
||||
struct osmo_cell_global_id_ps cgi_ps = {
|
||||
.rai = rai,
|
||||
.cell_identity = cell_id,
|
||||
};
|
||||
struct osmo_cell_global_id cgi = {
|
||||
.lai = rai.lac,
|
||||
.cell_identity = cell_id
|
||||
};
|
||||
|
||||
uint16_t nsei = 2, bvci = 3;
|
||||
|
||||
printf("Testing Routing Area find\n");
|
||||
|
||||
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
|
||||
ra_a = sgsn_ra_find_or_create(&rai, RA_TYPE_GERAN_Gb);
|
||||
OSMO_ASSERT(ra_a);
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
|
||||
ra_b = sgsn_ra_get_ra(&rai);
|
||||
OSMO_ASSERT(ra_a == ra_b);
|
||||
|
||||
cell_a = sgsn_ra_cell_alloc_geran(ra_a, cell_id, nsei, bvci);
|
||||
OSMO_ASSERT(cell_a);
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
|
||||
cell_b = sgsn_ra_geran_get_cell_by_cgi_ps(&cgi_ps);
|
||||
OSMO_ASSERT(cell_b);
|
||||
OSMO_ASSERT(cell_b == cell_a);
|
||||
|
||||
cell_b = sgsn_ra_geran_get_cell_by_ra(ra_a, cgi.cell_identity);
|
||||
OSMO_ASSERT(cell_b);
|
||||
OSMO_ASSERT(cell_b == cell_a);
|
||||
|
||||
cell_b = sgsn_ra_geran_get_cell_by_cgi(&cgi);
|
||||
OSMO_ASSERT(cell_b);
|
||||
OSMO_ASSERT(cell_b == cell_a);
|
||||
|
||||
cell_b = sgsn_ra_geran_get_cell_by_lai(&cgi.lai, cgi.cell_identity);
|
||||
OSMO_ASSERT(cell_b);
|
||||
OSMO_ASSERT(cell_b == cell_a);
|
||||
|
||||
sgsn_ra_free(ra_a);
|
||||
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
|
||||
|
||||
/* try to search for a cell id which isn't present */
|
||||
cgi.cell_identity = cell_id_not_found;
|
||||
cgi_ps.cell_identity = cell_id_not_found;
|
||||
|
||||
ra_a = sgsn_ra_find_or_create(&rai, RA_TYPE_GERAN_Gb);
|
||||
OSMO_ASSERT(ra_a);
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
|
||||
cell_a = sgsn_ra_cell_alloc_geran(ra_a, cell_id, nsei, bvci);
|
||||
OSMO_ASSERT(cell_a);
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
|
||||
cell_b = sgsn_ra_geran_get_cell_by_cgi_ps(&cgi_ps);
|
||||
OSMO_ASSERT(!cell_b);
|
||||
|
||||
cell_b = sgsn_ra_geran_get_cell_by_ra(ra_a, cgi_ps.cell_identity);
|
||||
OSMO_ASSERT(!cell_b);
|
||||
|
||||
cell_b = sgsn_ra_geran_get_cell_by_cgi(&cgi);
|
||||
OSMO_ASSERT(!cell_b);
|
||||
|
||||
cell_b = sgsn_ra_geran_get_cell_by_lai(&cgi.lai, cgi.cell_identity);
|
||||
OSMO_ASSERT(!cell_b);
|
||||
|
||||
/* try to find for a different RAC */
|
||||
cgi_ps.rai.rac = 45;
|
||||
rai.rac = 46;
|
||||
|
||||
cell_b = sgsn_ra_geran_get_cell_by_cgi_ps(&cgi_ps);
|
||||
OSMO_ASSERT(!cell_b);
|
||||
|
||||
ra_b = sgsn_ra_get_ra(&rai);
|
||||
OSMO_ASSERT(!ra_b);
|
||||
|
||||
/* try to find for different LAC */
|
||||
cgi.lai.lac = 46;
|
||||
cell_b = sgsn_ra_geran_get_cell_by_cgi(&cgi);
|
||||
OSMO_ASSERT(!cell_b);
|
||||
|
||||
sgsn_ra_free(ra_a);
|
||||
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
static void test_routing_area_reset_ind(void)
|
||||
{
|
||||
struct sgsn_ra *ra_a;
|
||||
struct sgsn_ra_cell *cell_a, *cell_b;
|
||||
struct osmo_routing_area_id rai = {
|
||||
.lac = {
|
||||
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
|
||||
.lac = 24
|
||||
},
|
||||
.rac = 43
|
||||
};
|
||||
|
||||
uint16_t cell_id = 9999;
|
||||
struct osmo_cell_global_id_ps cgi_ps = {
|
||||
.rai = rai,
|
||||
.cell_identity = cell_id,
|
||||
};
|
||||
struct osmo_cell_global_id cgi = {
|
||||
.lai = rai.lac,
|
||||
.cell_identity = cell_id
|
||||
};
|
||||
|
||||
uint16_t nsei = 2, bvci = 3;
|
||||
int rc;
|
||||
|
||||
printf("Testing Routing Area BSSGP BVC RESET IND\n");
|
||||
|
||||
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
|
||||
ra_a = sgsn_ra_find_or_create(&rai, RA_TYPE_GERAN_Gb);
|
||||
OSMO_ASSERT(ra_a);
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
OSMO_ASSERT(llist_count(&ra_a->cells_alive_list) == 0);
|
||||
|
||||
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
OSMO_ASSERT(llist_count(&ra_a->cells_alive_list) == 1);
|
||||
|
||||
cell_a = sgsn_ra_geran_get_cell_by_cgi(&cgi);
|
||||
OSMO_ASSERT(cell_a);
|
||||
|
||||
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
cell_b = sgsn_ra_geran_get_cell_by_cgi(&cgi);
|
||||
OSMO_ASSERT(cell_b);
|
||||
OSMO_ASSERT(cell_a == cell_b);
|
||||
|
||||
sgsn_ra_free(ra_a);
|
||||
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
|
||||
|
||||
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
|
||||
ra_a = sgsn_ra_get_ra(&cgi_ps.rai);
|
||||
sgsn_ra_free(ra_a);
|
||||
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
void test_routing_area_nsei_free(void)
|
||||
{
|
||||
struct sgsn_ra *ra_a;
|
||||
struct osmo_routing_area_id rai = {
|
||||
.lac = {
|
||||
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
|
||||
.lac = 24
|
||||
},
|
||||
.rac = 43
|
||||
};
|
||||
|
||||
uint16_t cell_id = 9999;
|
||||
struct osmo_cell_global_id_ps cgi_ps = {
|
||||
.rai = rai,
|
||||
.cell_identity = cell_id,
|
||||
};
|
||||
|
||||
uint16_t nsei = 2, bvci = 3;
|
||||
int rc;
|
||||
|
||||
printf("Testing Routing Area nsei failure\n");
|
||||
|
||||
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
|
||||
|
||||
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
ra_a = sgsn_ra_get_ra(&cgi_ps.rai);
|
||||
OSMO_ASSERT(llist_count(&ra_a->cells_alive_list) == 1);
|
||||
|
||||
rc = sgsn_ra_geran_nsei_failure_ind(nsei);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
|
||||
|
||||
rc = sgsn_ra_geran_nsei_failure_ind(nsei);
|
||||
OSMO_ASSERT(rc == -ENOENT);
|
||||
OSMO_ASSERT(llist_empty(&sgsn->routing_area->ra_list));
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
/* BSSGP Paging RA */
|
||||
int bssgp_tx_paging(uint16_t nsei, uint16_t _bvci,
|
||||
struct bssgp_paging_info *pinfo)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
OSMO_ASSERT(pinfo);
|
||||
fprintf(stderr, "Tx paging for nsei %05u / bvci %05u\n", nsei, pinfo->bvci);
|
||||
/* match against list of expect pagings */
|
||||
for (int i = 0; i < ARRAY_SIZE(g_paging); i++) {
|
||||
struct paging_exp *exp = &g_paging[i];
|
||||
if (exp->paged || !exp->valid)
|
||||
continue;
|
||||
|
||||
if (exp->nsei == nsei && exp->bvci == pinfo->bvci) {
|
||||
exp->paged = true;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
OSMO_ASSERT(found);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_paging(void)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(g_paging); i++) {
|
||||
struct paging_exp *exp = &g_paging[i];
|
||||
if (!exp->valid)
|
||||
continue;
|
||||
OSMO_ASSERT(exp->paged)
|
||||
}
|
||||
}
|
||||
|
||||
void test_routing_area_paging(void)
|
||||
{
|
||||
struct sgsn_mm_ctx *mmctx;
|
||||
struct osmo_routing_area_id rai = {
|
||||
.lac = {
|
||||
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
|
||||
.lac = 24
|
||||
},
|
||||
.rac = 43
|
||||
};
|
||||
|
||||
uint16_t cell_id = 9999;
|
||||
struct osmo_cell_global_id_ps cgi_ps = {
|
||||
.rai = rai,
|
||||
.cell_identity = cell_id,
|
||||
};
|
||||
|
||||
uint16_t nsei = 2, bvci = 3;
|
||||
int rc;
|
||||
|
||||
printf("Testing Routing Area paging\n");
|
||||
|
||||
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
|
||||
|
||||
memset(g_paging, 0, sizeof(g_paging));
|
||||
g_paging[0].bvci = bvci;
|
||||
g_paging[0].nsei = nsei;
|
||||
g_paging[0].valid = true;
|
||||
g_paging[0].paged = false;
|
||||
|
||||
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
cgi_ps.cell_identity++;
|
||||
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci+1, &cgi_ps);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
g_paging[1].bvci = bvci+1;
|
||||
g_paging[1].nsei = nsei;
|
||||
g_paging[1].valid = true;
|
||||
g_paging[1].paged = false;
|
||||
|
||||
mmctx = sgsn_mm_ctx_alloc_gb(0xc0001234, &rai);
|
||||
|
||||
sgsn_ra_geran_page_ra(&rai, mmctx);
|
||||
check_paging();
|
||||
|
||||
sgsn_mm_ctx_cleanup_free(mmctx);
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
/* check if a GERAN cell got removed when sending a Reset Ind on Sig BVCI */
|
||||
void test_routing_area_geran_geran_sig_reset(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* GERAN */
|
||||
struct osmo_routing_area_id geran_rai = {
|
||||
.lac = {
|
||||
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
|
||||
.lac = 24
|
||||
},
|
||||
.rac = 43
|
||||
};
|
||||
struct osmo_cell_global_id_ps cgi_ps = {
|
||||
.rai = geran_rai,
|
||||
.cell_identity = 9998,
|
||||
};
|
||||
uint16_t nsei = 2, bvci = 5;
|
||||
|
||||
struct sgsn_ra_cell *cell;
|
||||
|
||||
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
|
||||
|
||||
printf("Testing Routing Area GERAN BVCI Signalling Reset Ind\n");
|
||||
|
||||
printf(" Registering GERAN RA/cell via BVCI 5/BVC Reset Ind\n");
|
||||
sgsn_ra_geran_bvc_sign_reset_ind(nsei);
|
||||
|
||||
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
printf(" Checking cell on BVCI 5\n");
|
||||
cell = sgsn_ra_geran_get_cell_by_cgi_ps(&cgi_ps);
|
||||
OSMO_ASSERT(cell);
|
||||
OSMO_ASSERT(cell->ran_type == RA_TYPE_GERAN_Gb);
|
||||
OSMO_ASSERT(cell->u.geran.bvci == bvci);
|
||||
|
||||
printf(" Ensure only 1 RA is present\n");
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
|
||||
printf(" Ensure only 1 cell is present\n");
|
||||
OSMO_ASSERT(llist_count(&cell->ra->cells_alive_list) == 1);
|
||||
|
||||
printf(" Drop all cells via BVC Reset Ind on Signalling BVCI\n");
|
||||
sgsn_ra_geran_bvc_sign_reset_ind(nsei);
|
||||
|
||||
printf(" Ensure only 0 RAs are present\n");
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 0);
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
|
||||
/* check if a GERAN cell X can changed it's BVCI by BVC Reset Ind */
|
||||
void test_routing_area_geran_geran_bvci_change(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* GERAN */
|
||||
struct osmo_routing_area_id geran_rai = {
|
||||
.lac = {
|
||||
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
|
||||
.lac = 24
|
||||
},
|
||||
.rac = 43
|
||||
};
|
||||
struct osmo_cell_global_id_ps cgi_ps = {
|
||||
.rai = geran_rai,
|
||||
.cell_identity = 9998,
|
||||
};
|
||||
uint16_t nsei = 2, bvci_a = 3, bvci_b = 4;
|
||||
|
||||
struct sgsn_ra_cell *cell_a, *cell_b;
|
||||
|
||||
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
|
||||
|
||||
printf("Testing Routing Area GERAN to GERAN (BVCI change)\n");
|
||||
|
||||
printf(" Registering GERAN RA/cell via BVCI A/BVC Reset Ind\n");
|
||||
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci_a, &cgi_ps);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
printf(" Checking cell on BVCI A\n");
|
||||
cell_a = sgsn_ra_geran_get_cell_by_cgi_ps(&cgi_ps);
|
||||
OSMO_ASSERT(cell_a);
|
||||
OSMO_ASSERT(cell_a->ran_type == RA_TYPE_GERAN_Gb);
|
||||
OSMO_ASSERT(cell_a->u.geran.bvci == bvci_a);
|
||||
|
||||
printf(" Ensure only 1 RA is present\n");
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
|
||||
printf(" Ensure only 1 cell is present\n");
|
||||
OSMO_ASSERT(llist_count(&cell_a->ra->cells_alive_list) == 1);
|
||||
|
||||
printf(" Registering GERAN RA/cell via BVCI B/BVC Reset Ind\n");
|
||||
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci_b, &cgi_ps);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
printf(" Checking cell on BVCI B\n");
|
||||
cell_b = sgsn_ra_geran_get_cell_by_cgi_ps(&cgi_ps);
|
||||
OSMO_ASSERT(cell_b);
|
||||
OSMO_ASSERT(cell_b->ran_type == RA_TYPE_GERAN_Gb);
|
||||
OSMO_ASSERT(cell_b->u.geran.bvci == bvci_b);
|
||||
|
||||
printf(" Ensure only 1 RA is present\n");
|
||||
OSMO_ASSERT(llist_count(&sgsn->routing_area->ra_list) == 1);
|
||||
|
||||
printf(" Ensure only 1 cell is present\n");
|
||||
OSMO_ASSERT(llist_count(&cell_b->ra->cells_alive_list) == 1);
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
/* check if UTRAN RA gets rejected, if a GERAN RA/cell with the same LAC is already registered
|
||||
* The SGSN does not support the same LAC/RA for GERAN and UTRAN at the same time.
|
||||
*/
|
||||
void test_routing_area_mv_utran_geran_reject(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* GERAN */
|
||||
struct osmo_routing_area_id geran_rai = {
|
||||
.lac = {
|
||||
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
|
||||
.lac = 24
|
||||
},
|
||||
.rac = 43
|
||||
};
|
||||
struct osmo_cell_global_id_ps cgi_ps = {
|
||||
.rai = geran_rai,
|
||||
.cell_identity = 9998,
|
||||
};
|
||||
uint16_t nsei = 2, bvci = 3;
|
||||
|
||||
/* UTRAN */
|
||||
struct osmo_routing_area_id utran_rai = {
|
||||
.lac = {
|
||||
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
|
||||
.lac = 24
|
||||
},
|
||||
.rac = 43
|
||||
};
|
||||
struct osmo_rnc_id rnc_id = {
|
||||
.plmn = utran_rai.lac.plmn,
|
||||
.rnc_id = 2222
|
||||
};
|
||||
|
||||
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
|
||||
|
||||
/* Registering UTRAN RA */
|
||||
rc = sgsn_ra_utran_register(&utran_rai, &rnc_id);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
/* Registering GERAN RA/cell via BVC Reset Ind (should fail) */
|
||||
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
|
||||
OSMO_ASSERT(rc != 0);
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
/* check if a UTRAN RA with the same LAC as an already register GERAN RA gets rejected */
|
||||
void test_routing_area_mv_geran_utran_reject(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* GERAN */
|
||||
struct osmo_routing_area_id geran_rai = {
|
||||
.lac = {
|
||||
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
|
||||
.lac = 24
|
||||
},
|
||||
.rac = 43
|
||||
};
|
||||
struct osmo_cell_global_id_ps cgi_ps = {
|
||||
.rai = geran_rai,
|
||||
.cell_identity = 9998,
|
||||
};
|
||||
uint16_t nsei = 2, bvci = 3;
|
||||
|
||||
/* UTRAN */
|
||||
struct osmo_routing_area_id utran_rai = {
|
||||
.lac = {
|
||||
.plmn = { .mcc = 262, .mnc = 42, .mnc_3_digits = false },
|
||||
.lac = 24
|
||||
},
|
||||
.rac = 43
|
||||
};
|
||||
struct osmo_rnc_id rnc_id = {
|
||||
.plmn = utran_rai.lac.plmn,
|
||||
.rnc_id = 2222
|
||||
};
|
||||
|
||||
sgsn = sgsn_instance_alloc(tall_sgsn_ctx);
|
||||
|
||||
/* Registering GERAN RA/cell via BVC Reset Ind */
|
||||
rc = sgsn_ra_geran_bvc_cell_reset_ind(nsei, bvci, &cgi_ps);
|
||||
OSMO_ASSERT(rc == 0);
|
||||
|
||||
/* Registering UTRAN RA (should fail) */
|
||||
rc = sgsn_ra_utran_register(&utran_rai, &rnc_id);
|
||||
OSMO_ASSERT(rc != 0);
|
||||
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
static struct log_info_cat gprs_categories[] = {
|
||||
[DMM] = {
|
||||
.name = "DMM",
|
||||
.description = "Layer3 Mobility Management (MM)",
|
||||
.color = "\033[1;33m",
|
||||
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||
},
|
||||
[DPAG] = {
|
||||
.name = "DPAG",
|
||||
.description = "Paging Subsystem",
|
||||
.color = "\033[1;38m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DREF] = {
|
||||
.name = "DREF",
|
||||
.description = "Reference Counting",
|
||||
.enabled = 0, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DGPRS] = {
|
||||
.name = "DGPRS",
|
||||
.description = "GPRS Packet Service",
|
||||
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||
},
|
||||
[DLLC] = {
|
||||
.name = "DLLC",
|
||||
.description = "GPRS Logical Link Control Protocol (LLC)",
|
||||
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||
},
|
||||
[DRA] = {
|
||||
.name = "DRA",
|
||||
.description = "Routing Area",
|
||||
.enabled = 1, .loglevel = LOGL_DEBUG,
|
||||
},
|
||||
};
|
||||
|
||||
static struct log_info info = {
|
||||
.cat = gprs_categories,
|
||||
.num_cat = ARRAY_SIZE(gprs_categories),
|
||||
};
|
||||
|
||||
static struct vty_app_info vty_info = {
|
||||
.name = "testSGSN",
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
void *osmo_sgsn_ctx;
|
||||
void *msgb_ctx;
|
||||
|
||||
osmo_sgsn_ctx = talloc_named_const(NULL, 0, "osmo_sgsn");
|
||||
osmo_init_logging2(osmo_sgsn_ctx, &info);
|
||||
tall_sgsn_ctx = talloc_named_const(osmo_sgsn_ctx, 0, "sgsn");
|
||||
msgb_ctx = msgb_talloc_ctx_init(osmo_sgsn_ctx, 0);
|
||||
|
||||
vty_init(&vty_info);
|
||||
|
||||
test_routing_area_create();
|
||||
test_routing_area_find();
|
||||
test_routing_area_free_empty();
|
||||
test_routing_area_reset_ind();
|
||||
test_routing_area_nsei_free();
|
||||
test_routing_area_paging();
|
||||
test_routing_area_geran_geran_sig_reset();
|
||||
test_routing_area_geran_geran_bvci_change();
|
||||
#ifdef BUILD_IU
|
||||
test_routing_area_mv_geran_utran_reject();
|
||||
test_routing_area_mv_utran_geran_reject();
|
||||
#endif
|
||||
printf("Done\n");
|
||||
|
||||
talloc_report_full(osmo_sgsn_ctx, stderr);
|
||||
OSMO_ASSERT(talloc_total_blocks(msgb_ctx) == 1);
|
||||
OSMO_ASSERT(talloc_total_blocks(tall_sgsn_ctx) == 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* stubs */
|
||||
struct osmo_prim_hdr;
|
||||
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
23
tests/gprs_routing_area/gprs_routing_area_test.ok
Normal file
23
tests/gprs_routing_area/gprs_routing_area_test.ok
Normal file
@@ -0,0 +1,23 @@
|
||||
Testing Routing Area create/free
|
||||
Testing Routing Area find
|
||||
Testing Routing Area create/free
|
||||
Testing Routing Area BSSGP BVC RESET IND
|
||||
Testing Routing Area nsei failure
|
||||
Testing Routing Area paging
|
||||
Testing Routing Area GERAN BVCI Signalling Reset Ind
|
||||
Registering GERAN RA/cell via BVCI 5/BVC Reset Ind
|
||||
Checking cell on BVCI 5
|
||||
Ensure only 1 RA is present
|
||||
Ensure only 1 cell is present
|
||||
Drop all cells via BVC Reset Ind on Signalling BVCI
|
||||
Ensure only 0 RAs are present
|
||||
Testing Routing Area GERAN to GERAN (BVCI change)
|
||||
Registering GERAN RA/cell via BVCI A/BVC Reset Ind
|
||||
Checking cell on BVCI A
|
||||
Ensure only 1 RA is present
|
||||
Ensure only 1 cell is present
|
||||
Registering GERAN RA/cell via BVCI B/BVC Reset Ind
|
||||
Checking cell on BVCI B
|
||||
Ensure only 1 RA is present
|
||||
Ensure only 1 cell is present
|
||||
Done
|
||||
@@ -29,11 +29,12 @@
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/gsm/protocol/gsm_23_003.h>
|
||||
|
||||
#include <osmocom/gtp/gtp.h>
|
||||
#include <osmocom/gtp/gtpie.h>
|
||||
|
||||
#include <osmocom/sgsn/debug.h>
|
||||
|
||||
#include <osmocom/gtphub/gtphub.h>
|
||||
#include <gtp.h>
|
||||
#include <gtpie.h>
|
||||
|
||||
#define ZERO_STRUCT(struct_pointer) memset(struct_pointer, '\0', \
|
||||
sizeof(*(struct_pointer)))
|
||||
|
||||
38
tests/osmo-sgsn-accept-all.cfg
Normal file
38
tests/osmo-sgsn-accept-all.cfg
Normal file
@@ -0,0 +1,38 @@
|
||||
# Same as doc/examples/osmo-sgsn/osmo-sgsn-accept-all.cfg, but without 'gtp state-dir'
|
||||
!
|
||||
! Osmocom SGSN configuration
|
||||
!
|
||||
!
|
||||
log stderr
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
sgsn
|
||||
gtp local-ip 127.0.0.1
|
||||
ggsn 0 remote-ip 127.0.0.2
|
||||
ggsn 0 gtp-version 1
|
||||
ggsn 0 echo-interval 60
|
||||
authentication optional
|
||||
auth-policy accept-all
|
||||
!
|
||||
ns
|
||||
timer tns-block 3
|
||||
timer tns-block-retries 3
|
||||
timer tns-reset 3
|
||||
timer tns-reset-retries 3
|
||||
timer tns-test 30
|
||||
timer tns-alive 3
|
||||
timer tns-alive-retries 10
|
||||
bind udp local
|
||||
listen 127.0.0.1 23000
|
||||
accept-ipaccess
|
||||
!
|
||||
bssgp
|
||||
!
|
||||
40
tests/osmo-sgsn.cfg
Normal file
40
tests/osmo-sgsn.cfg
Normal file
@@ -0,0 +1,40 @@
|
||||
# Same as doc/examples/osmo-sgsn/osmo-sgsn.cfg, but without 'gtp state-dir'
|
||||
!
|
||||
! Osmocom SGSN configuration
|
||||
!
|
||||
!
|
||||
log stderr
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
sgsn
|
||||
gtp local-ip 127.0.0.1
|
||||
ggsn 0 remote-ip 127.0.0.2
|
||||
ggsn 0 gtp-version 1
|
||||
ggsn 0 echo-interval 60
|
||||
authentication required
|
||||
auth-policy remote
|
||||
gsup remote-ip 127.0.0.1
|
||||
gsup remote-port 4222
|
||||
!
|
||||
ns
|
||||
timer tns-block 3
|
||||
timer tns-block-retries 3
|
||||
timer tns-reset 3
|
||||
timer tns-reset-retries 3
|
||||
timer tns-test 30
|
||||
timer tns-alive 3
|
||||
timer tns-alive-retries 10
|
||||
bind udp local
|
||||
listen 127.0.0.1 23000
|
||||
accept-ipaccess
|
||||
!
|
||||
bssgp
|
||||
!
|
||||
@@ -13,6 +13,7 @@ T3386 = 8 s Wait for MODIFY PDP CTX ACK timer (s) (default: 8 s)
|
||||
T3395 = 8 s Wait for DEACT PDP CTX ACK timer (s) (default: 8 s)
|
||||
T3397 = 8 s Wait for DEACT AA PDP CTX ACK timer (s) (default: 8 s)
|
||||
X1001 = 5 s RANAP Release timeout. Wait for RANAP Release Complete.On expiry release Iu connection (s) (default: 5 s)
|
||||
X1002 = 5 s TRafR, Maximum time for Reset procedure in the CN (s) (default: 5 s)
|
||||
OsmoSGSN# configure terminal
|
||||
OsmoSGSN(config)# list
|
||||
...
|
||||
|
||||
@@ -59,7 +59,9 @@ sgsn_test_LDADD = \
|
||||
$(top_builddir)/src/sgsn/gprs_gmm_attach.o \
|
||||
$(top_builddir)/src/sgsn/gprs_gmm.o \
|
||||
$(top_builddir)/src/sgsn/gprs_gmm_fsm.o \
|
||||
$(top_builddir)/src/sgsn/gprs_gmm_util.o \
|
||||
$(top_builddir)/src/sgsn/gprs_mm_state_gb_fsm.o \
|
||||
$(top_builddir)/src/sgsn/gprs_routing_area.o \
|
||||
$(top_builddir)/src/sgsn/gtp_ggsn.o \
|
||||
$(top_builddir)/src/sgsn/gtp_mme.o \
|
||||
$(top_builddir)/src/sgsn/mmctx.o \
|
||||
@@ -84,7 +86,6 @@ sgsn_test_LDADD = \
|
||||
$(top_builddir)/src/gprs/gprs_llc_parse.o \
|
||||
$(top_builddir)/src/gprs/crc24.o \
|
||||
$(top_builddir)/src/gprs/sgsn_ares.o \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
@@ -100,6 +101,10 @@ if BUILD_IU
|
||||
sgsn_test_LDADD += \
|
||||
$(top_builddir)/src/sgsn/gprs_ranap.o \
|
||||
$(top_builddir)/src/sgsn/gprs_mm_state_iu_fsm.o \
|
||||
$(top_builddir)/src/sgsn/iu_client.o \
|
||||
$(top_builddir)/src/sgsn/iu_rnc.o \
|
||||
$(top_builddir)/src/sgsn/iu_rnc_fsm.o \
|
||||
$(top_builddir)/src/sgsn/sccp.o \
|
||||
$(LIBOSMORANAP_LIBS) \
|
||||
$(LIBOSMOSIGTRAN_LIBS) \
|
||||
$(LIBASN1C_LIBS) \
|
||||
|
||||
@@ -181,7 +181,7 @@ static struct msgb *create_msg(const uint8_t *data, size_t len)
|
||||
/*
|
||||
* Create a context and search for it
|
||||
*/
|
||||
static struct sgsn_mm_ctx *alloc_mm_ctx(uint32_t tlli, struct gprs_ra_id *raid)
|
||||
static struct sgsn_mm_ctx *alloc_mm_ctx(uint32_t tlli, struct osmo_routing_area_id *raid)
|
||||
{
|
||||
struct sgsn_mm_ctx *ctx, *ictx;
|
||||
struct gprs_llc_lle *lle;
|
||||
@@ -200,7 +200,7 @@ static struct sgsn_mm_ctx *alloc_mm_ctx(uint32_t tlli, struct gprs_ra_id *raid)
|
||||
}
|
||||
|
||||
static void send_0408_message(struct gprs_llc_llme *llme, uint32_t tlli,
|
||||
const struct gprs_ra_id *bssgp_raid,
|
||||
const struct osmo_routing_area_id *bssgp_raid,
|
||||
const uint8_t *data, size_t data_len)
|
||||
{
|
||||
struct msgb *msg;
|
||||
@@ -210,7 +210,7 @@ static void send_0408_message(struct gprs_llc_llme *llme, uint32_t tlli,
|
||||
|
||||
msg = create_msg(data, data_len);
|
||||
msgb_tlli(msg) = tlli;
|
||||
bssgp_create_cell_id(msgb_bcid(msg), bssgp_raid, 0);
|
||||
bssgp_create_cell_id2(msgb_bcid(msg), 8, bssgp_raid, 0);
|
||||
gsm0408_gprs_rcvmsg_gb(msg, llme, false);
|
||||
msgb_free(msg);
|
||||
}
|
||||
@@ -375,7 +375,7 @@ static void test_auth_triplets(void)
|
||||
const char *imsi1 = "1234567890";
|
||||
struct gsm_auth_tuple *at;
|
||||
struct sgsn_mm_ctx *ctx;
|
||||
struct gprs_ra_id raid = { 0, };
|
||||
struct osmo_routing_area_id raid = { 0, };
|
||||
uint32_t local_tlli = 0xffeeddcc;
|
||||
|
||||
printf("Testing authentication triplet handling\n");
|
||||
@@ -455,7 +455,7 @@ static void test_subscriber_gsup(void)
|
||||
struct gprs_subscr *s1, *s1found;
|
||||
const char *imsi1 = "1234567890";
|
||||
struct sgsn_mm_ctx *ctx;
|
||||
struct gprs_ra_id raid = { 0, };
|
||||
struct osmo_routing_area_id raid = { 0, };
|
||||
uint32_t local_tlli = 0xffeeddcc;
|
||||
struct sgsn_subscriber_pdp_data *pdpd;
|
||||
int rc;
|
||||
@@ -740,7 +740,7 @@ int my_gsup_client_send_dummy(struct osmo_gsup_client *gsupc, struct msgb *msg)
|
||||
*/
|
||||
static void test_gmm_detach(void)
|
||||
{
|
||||
struct gprs_ra_id raid = { 0, };
|
||||
struct osmo_routing_area_id raid = { 0, };
|
||||
struct sgsn_mm_ctx *ctx, *ictx;
|
||||
uint32_t local_tlli;
|
||||
|
||||
@@ -782,7 +782,7 @@ static void test_gmm_detach(void)
|
||||
*/
|
||||
static void test_gmm_detach_power_off(void)
|
||||
{
|
||||
struct gprs_ra_id raid = { 0, };
|
||||
struct osmo_routing_area_id raid = { 0, };
|
||||
struct sgsn_mm_ctx *ctx, *ictx;
|
||||
uint32_t local_tlli;
|
||||
|
||||
@@ -823,7 +823,7 @@ static void test_gmm_detach_power_off(void)
|
||||
*/
|
||||
static void test_gmm_detach_no_mmctx(void)
|
||||
{
|
||||
struct gprs_ra_id raid = { 0, };
|
||||
struct osmo_routing_area_id raid = { 0, };
|
||||
struct gprs_llc_lle *lle;
|
||||
uint32_t local_tlli;
|
||||
|
||||
@@ -860,7 +860,7 @@ static void test_gmm_detach_no_mmctx(void)
|
||||
*/
|
||||
static void test_gmm_detach_accept_unexpected(void)
|
||||
{
|
||||
struct gprs_ra_id raid = { 0, };
|
||||
struct osmo_routing_area_id raid = { 0, };
|
||||
struct gprs_llc_lle *lle;
|
||||
uint32_t local_tlli;
|
||||
|
||||
@@ -897,7 +897,7 @@ static void test_gmm_detach_accept_unexpected(void)
|
||||
*/
|
||||
static void test_gmm_status_no_mmctx(void)
|
||||
{
|
||||
struct gprs_ra_id raid = { 0, };
|
||||
struct osmo_routing_area_id raid = { 0, };
|
||||
struct gprs_llc_lle *lle;
|
||||
uint32_t local_tlli;
|
||||
|
||||
@@ -1092,7 +1092,7 @@ int my_gsup_client_send(struct osmo_gsup_client *gsupc, struct msgb *msg)
|
||||
*/
|
||||
static void test_gmm_reject(void)
|
||||
{
|
||||
struct gprs_ra_id raid = { 0, };
|
||||
struct osmo_routing_area_id raid = { 0, };
|
||||
struct sgsn_mm_ctx *ctx = NULL;
|
||||
uint32_t foreign_tlli;
|
||||
struct gprs_llc_lle *lle;
|
||||
@@ -1237,7 +1237,7 @@ static void test_gmm_reject(void)
|
||||
*/
|
||||
static void test_gmm_cancel(void)
|
||||
{
|
||||
struct gprs_ra_id raid = { 0, };
|
||||
struct osmo_routing_area_id raid = { 0, };
|
||||
struct sgsn_mm_ctx *ctx = NULL;
|
||||
struct sgsn_mm_ctx *ictx;
|
||||
uint32_t ptmsi1;
|
||||
@@ -1446,7 +1446,7 @@ static void test_ggsn_selection(void)
|
||||
struct gprs_subscr *s1;
|
||||
const char *imsi1 = "1234567890";
|
||||
struct sgsn_mm_ctx *ctx;
|
||||
struct gprs_ra_id raid = { 0, };
|
||||
struct osmo_routing_area_id raid = { 0, };
|
||||
uint32_t local_tlli = 0xffeeddcc;
|
||||
enum gsm48_gsm_cause gsm_cause;
|
||||
struct tlv_parsed tp;
|
||||
@@ -1489,7 +1489,8 @@ static void test_ggsn_selection(void)
|
||||
|
||||
pdp_data = sgsn_subscriber_pdp_data_alloc(s1->sgsn_data);
|
||||
pdp_data->context_id = 1;
|
||||
pdp_data->pdp_type = 0x0121;
|
||||
pdp_data->pdp_type_org = PDP_TYPE_ORG_IETF;
|
||||
pdp_data->pdp_type_nr = PDP_TYPE_N_IETF_IPv4;
|
||||
osmo_strlcpy(pdp_data->apn_str, "*", sizeof(pdp_data->apn_str));
|
||||
|
||||
/* Resolve GGSNs */
|
||||
@@ -1581,20 +1582,21 @@ static void test_ggsn_selection(void)
|
||||
cleanup_test();
|
||||
}
|
||||
|
||||
bool pdp_status_has_active_nsapis(const uint8_t *pdp_status, const size_t pdp_status_len);
|
||||
bool pdp_status_has_active_nsapis(uint16_t pdp_status);
|
||||
|
||||
static void test_pdp_status_has_active_nsapis(void)
|
||||
{
|
||||
const size_t pdp_status_len = 2;
|
||||
const uint8_t pdp_status1[] = { 0b00100000, 0b00000000 }; /* PDP NSAPI 5 active */
|
||||
const uint8_t pdp_status2[] = { 0b00000000, 0b00000000 }; /* no active PDP NSAPI */
|
||||
const uint8_t pdp_status3[] = { 0b00000000, 0b00000001 }; /* PDP NSAPI 8 active */
|
||||
uint16_t pdp_status1 = 0b0000000000100000; /* PDP NSAPI 5 active */
|
||||
uint16_t pdp_status2 = 0b0000000000000000; /* no active PDP NSAPI */
|
||||
uint16_t pdp_status3 = 0b0000000100000000; /* PDP NSAPI 8 active */
|
||||
uint16_t pdp_status4 = 0b0000000000000001; /* reserved NSAPI 0 active -> no active */
|
||||
|
||||
printf("Testing pdp_status_has_active_nsapis\n");
|
||||
|
||||
OSMO_ASSERT(pdp_status_has_active_nsapis(pdp_status1, pdp_status_len));
|
||||
OSMO_ASSERT(!pdp_status_has_active_nsapis(pdp_status2, pdp_status_len));
|
||||
OSMO_ASSERT(pdp_status_has_active_nsapis(pdp_status3, pdp_status_len));
|
||||
OSMO_ASSERT(pdp_status_has_active_nsapis(pdp_status1));
|
||||
OSMO_ASSERT(!pdp_status_has_active_nsapis(pdp_status2));
|
||||
OSMO_ASSERT(pdp_status_has_active_nsapis(pdp_status3));
|
||||
OSMO_ASSERT(!pdp_status_has_active_nsapis(pdp_status4));
|
||||
}
|
||||
|
||||
static struct log_info_cat gprs_categories[] = {
|
||||
|
||||
@@ -209,12 +209,12 @@ static void test_slhc(const void *ctx)
|
||||
packet_len = DISP_MAX_BYTES;
|
||||
if (packet_decompr_len > DISP_MAX_BYTES)
|
||||
packet_decompr_len = DISP_MAX_BYTES;
|
||||
printf("Original Packet: (%i bytes) %s\n", packet_len,
|
||||
printf("Original Packet: (%d bytes) %s\n", packet_len,
|
||||
osmo_hexdump_nospc(packet, packet_len));
|
||||
printf("DecompressedPacket: (%i bytes) %s\n",
|
||||
printf("DecompressedPacket: (%d bytes) %s\n",
|
||||
packet_decompr_len, osmo_hexdump_nospc(packet_decompr,
|
||||
packet_decompr_len));
|
||||
printf("CompressedPacket: (%i bytes) %s\n", packet_compr_len,
|
||||
printf("CompressedPacket: (%d bytes) %s\n", packet_compr_len,
|
||||
osmo_hexdump_nospc(packet_compr, packet_compr_len));
|
||||
slhc_o_status(comp);
|
||||
slhc_o_status(comp);
|
||||
|
||||
@@ -10,7 +10,6 @@ sndcp_xid_test_SOURCES = sndcp_xid_test.c
|
||||
|
||||
sndcp_xid_test_LDADD = \
|
||||
$(top_builddir)/src/sgsn/gprs_sndcp_xid.o \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
|
||||
@@ -55,7 +55,7 @@ static void test_xid_decode_realworld(const void *ctx)
|
||||
/* Encode comp-fields again */
|
||||
rc = gprs_sndcp_compile_xid(xid_r,sizeof(xid_r), comp_fields,
|
||||
DEFAULT_SNDCP_VERSION);
|
||||
printf("Result length=%i\n",rc);
|
||||
printf("Result length=%d\n", rc);
|
||||
printf("Encoded: %s\n", osmo_hexdump_nospc(xid, sizeof(xid)));
|
||||
printf("Rencoded: %s\n", osmo_hexdump_nospc(xid_r, rc));
|
||||
|
||||
@@ -231,7 +231,7 @@ static void test_xid_encode_decode(const void *ctx)
|
||||
DEFAULT_SNDCP_VERSION);
|
||||
OSMO_ASSERT(rc > 0);
|
||||
|
||||
printf("Encoded: %s (%i bytes)\n", osmo_hexdump_nospc(xid, rc), rc);
|
||||
printf("Encoded: %s (%d bytes)\n", osmo_hexdump_nospc(xid, rc), rc);
|
||||
|
||||
/* Parse and show contained comp fields */
|
||||
comp_fields_dec = gprs_sndcp_parse_xid(NULL, ctx, xid, rc, NULL);
|
||||
|
||||
@@ -14,6 +14,13 @@ cat $abs_srcdir/sgsn/sgsn_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/sgsn/sgsn_test], [], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([gprs_routing_area])
|
||||
AT_KEYWORDS([gprs_routing_area])
|
||||
AT_CHECK([test "$enable_gprs_routing_area_test" != no || exit 77])
|
||||
cat $abs_srcdir/gprs_routing_area/gprs_routing_area_test.ok > expout
|
||||
AT_CHECK([$abs_top_builddir/tests/gprs_routing_area/gprs_routing_area_test], [], [expout], [ignore])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([gtphub])
|
||||
AT_KEYWORDS([gtphub])
|
||||
AT_CHECK([test "$enable_gtphub_test" != no || exit 77])
|
||||
|
||||
@@ -268,7 +268,7 @@ static void v42bis(const void *ctx, int mode, uint8_t *testvec, int len)
|
||||
rc_sum += rc;
|
||||
|
||||
/* Check results */
|
||||
printf("Mode: %i\n", mode);
|
||||
printf("Mode: %d\n", mode);
|
||||
|
||||
printf("uncompressed_original= %s ASCII:",
|
||||
osmo_hexdump_nospc(uncompressed_original, len));
|
||||
@@ -314,7 +314,7 @@ static void test_v42bis_tcpip(const void *ctx, int packet_id)
|
||||
int len;
|
||||
printf
|
||||
("Testing compression/decompression with realistic TCP/IP packets:\n");
|
||||
printf("Packet No.: %i\n", packet_id);
|
||||
printf("Packet No.: %d\n", packet_id);
|
||||
len = strlen(uncompr_packets[packet_id]);
|
||||
testvec = talloc_zero_size(ctx, len);
|
||||
len = osmo_hexparse(uncompr_packets[packet_id], testvec, len);
|
||||
@@ -339,7 +339,7 @@ static void test_v42bis_tcpip_decompress(const void *ctx, int packet_id)
|
||||
|
||||
printf
|
||||
("Testing decompression with sniffed compressed TCP/IP packets:\n");
|
||||
printf("Packet No.: %i\n", packet_id);
|
||||
printf("Packet No.: %d\n", packet_id);
|
||||
len = strlen(compr_packets[packet_id]);
|
||||
|
||||
uncompressed = talloc_zero_size(ctx, len);
|
||||
|
||||
@@ -70,8 +70,7 @@ class TestVTYBase(unittest.TestCase):
|
||||
class TestVTYSGSN(TestVTYBase):
|
||||
|
||||
def vty_command(self):
|
||||
return ["./src/sgsn/osmo-sgsn", "-c",
|
||||
"doc/examples/osmo-sgsn/osmo-sgsn-accept-all.cfg"]
|
||||
return ["./src/sgsn/osmo-sgsn", "-c", "tests/osmo-sgsn-accept-all.cfg"]
|
||||
|
||||
def vty_app(self):
|
||||
return (4245, "./src/sgsn/osmo-sgsn", "OsmoSGSN", "sgsn")
|
||||
|
||||
@@ -27,7 +27,6 @@ xid_test_SOURCES = \
|
||||
|
||||
xid_test_LDADD = \
|
||||
$(top_builddir)/src/sgsn/gprs_llc_xid.o \
|
||||
$(LIBOSMOABIS_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
|
||||
@@ -73,8 +73,8 @@ static void test_xid_encode(const void *ctx)
|
||||
/* Encode data */
|
||||
rc = gprs_llc_compile_xid(xid, sizeof(xid), &xid_fields);
|
||||
OSMO_ASSERT(rc == 14);
|
||||
printf("Encoded: %s (%i bytes)\n", osmo_hexdump_nospc(xid, rc), rc);
|
||||
printf("Expected: %s (%i bytes)\n",
|
||||
printf("Encoded: %s (%d bytes)\n", osmo_hexdump_nospc(xid, rc), rc);
|
||||
printf("Expected: %s (%d bytes)\n",
|
||||
osmo_hexdump_nospc(xid_expected, sizeof(xid_expected)),
|
||||
(int)sizeof(xid_expected));
|
||||
|
||||
@@ -112,7 +112,7 @@ static void test_xid_decode(const void *ctx)
|
||||
|
||||
/* Encode xid-fields again */
|
||||
rc = gprs_llc_compile_xid(xid_r, sizeof(xid_r), xid_fields);
|
||||
printf("Result length=%i\n",rc);
|
||||
printf("Result length=%d\n", rc);
|
||||
printf("Encoded: %s\n", osmo_hexdump_nospc(xid, sizeof(xid)));
|
||||
printf("Rencoded: %s\n", osmo_hexdump_nospc(xid_r, rc));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user