mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-hlr.git
synced 2025-11-03 21:53:30 +00:00
Compare commits
59 Commits
fixeria/sq
...
fixeria/35
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d31cf5ba8 | ||
|
|
5e4069cac7 | ||
|
|
4bd7bdb958 | ||
|
|
7d29e3473a | ||
|
|
18a1b01ca8 | ||
|
|
df8d454919 | ||
|
|
9ea9bbbc7f | ||
|
|
5c14c9ccca | ||
|
|
705b61bcb7 | ||
|
|
638ba8cc04 | ||
|
|
55f5efa568 | ||
|
|
e6ce52bbde | ||
|
|
d157a56361 | ||
|
|
9c8806acf5 | ||
|
|
4b8be4d12d | ||
|
|
bc9bead62a | ||
|
|
4655e6f1fe | ||
|
|
8f3a7cce80 | ||
|
|
a820ea1f67 | ||
|
|
8aa780bf80 | ||
|
|
f08da2459b | ||
|
|
62ce834fbf | ||
|
|
bf6b4eb0b9 | ||
|
|
79efdf3474 | ||
|
|
b41394a700 | ||
|
|
0c331abdbc | ||
|
|
25e716c849 | ||
|
|
92e49ef363 | ||
|
|
95380ab037 | ||
|
|
849bfd0bef | ||
|
|
7e2d3c7f4c | ||
|
|
8f725ae655 | ||
|
|
1ed4bb4ff1 | ||
|
|
25dd785157 | ||
|
|
4f5f6f83f3 | ||
|
|
e66e525e09 | ||
|
|
4a4bdcdf97 | ||
|
|
cb364bb429 | ||
|
|
d646207553 | ||
|
|
6cee799d5e | ||
|
|
c88bdab96d | ||
|
|
607ce5ca93 | ||
|
|
ccdb970c57 | ||
|
|
a5b36a0904 | ||
|
|
13000d8d14 | ||
|
|
7ebfd065da | ||
|
|
966fcb2ca8 | ||
|
|
6fe1c2220a | ||
|
|
0da9f2f19c | ||
|
|
1eb9869d81 | ||
|
|
3adb33de93 | ||
|
|
791ea72ee4 | ||
|
|
9f7b69a618 | ||
|
|
e6c839ed2d | ||
|
|
b93c44f32e | ||
|
|
a05efe8803 | ||
|
|
764514198b | ||
|
|
5198609a5e | ||
|
|
7c2f430fc5 |
12
.gitignore
vendored
12
.gitignore
vendored
@@ -5,6 +5,7 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
.*.sw?
|
.*.sw?
|
||||||
.version
|
.version
|
||||||
|
.tarball-version
|
||||||
Makefile
|
Makefile
|
||||||
Makefile.in
|
Makefile.in
|
||||||
aclocal.m4
|
aclocal.m4
|
||||||
@@ -45,3 +46,14 @@ tests/auc/auc_test
|
|||||||
tests/gsup_server/gsup_server_test
|
tests/gsup_server/gsup_server_test
|
||||||
tests/gsup/gsup_test
|
tests/gsup/gsup_test
|
||||||
tests/db/db_test
|
tests/db/db_test
|
||||||
|
|
||||||
|
# manuals
|
||||||
|
doc/manuals/*.html
|
||||||
|
doc/manuals/*.svg
|
||||||
|
doc/manuals/*.pdf
|
||||||
|
doc/manuals/*__*.png
|
||||||
|
doc/manuals/*.check
|
||||||
|
doc/manuals/generated/
|
||||||
|
doc/manuals/osmomsc-usermanual.xml
|
||||||
|
doc/manuals/common
|
||||||
|
doc/manuals/build
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ SUBDIRS = \
|
|||||||
src \
|
src \
|
||||||
include \
|
include \
|
||||||
sql \
|
sql \
|
||||||
|
contrib \
|
||||||
tests \
|
tests \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
@@ -12,6 +13,9 @@ EXTRA_DIST = \
|
|||||||
.version \
|
.version \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||||
|
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
|
||||||
|
|
||||||
pkgconfigdir = $(libdir)/pkgconfig
|
pkgconfigdir = $(libdir)/pkgconfig
|
||||||
pkgconfig_DATA = libosmo-gsup-client.pc
|
pkgconfig_DATA = libosmo-gsup-client.pc
|
||||||
|
|
||||||
|
|||||||
62
configure.ac
62
configure.ac
@@ -92,17 +92,79 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
|
|||||||
AC_MSG_RESULT([$enable_ext_tests])
|
AC_MSG_RESULT([$enable_ext_tests])
|
||||||
AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
|
AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
|
||||||
|
|
||||||
|
# Generate manuals
|
||||||
|
AC_ARG_ENABLE(manuals,
|
||||||
|
[AS_HELP_STRING(
|
||||||
|
[--enable-manuals],
|
||||||
|
[Generate manual PDFs [default=no]],
|
||||||
|
)],
|
||||||
|
[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"])
|
||||||
|
AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"])
|
||||||
|
AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals"
|
||||||
|
fallback])
|
||||||
|
if test x"$osmo_ac_build_manuals" = x"yes"
|
||||||
|
then
|
||||||
|
# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback)
|
||||||
|
if test -n "$OSMO_GSM_MANUALS_DIR"; then
|
||||||
|
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)"
|
||||||
|
else
|
||||||
|
OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)"
|
||||||
|
if test -n "$OSMO_GSM_MANUALS_DIR"; then
|
||||||
|
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)"
|
||||||
|
else
|
||||||
|
OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals"
|
||||||
|
echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if ! test -d "$OSMO_GSM_MANUALS_DIR"; then
|
||||||
|
AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find and run check-depends
|
||||||
|
CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh"
|
||||||
|
if ! test -x "$CHECK_DEPENDS"; then
|
||||||
|
CHECK_DEPENDS="osmo-gsm-manuals-check-depends"
|
||||||
|
fi
|
||||||
|
if ! $CHECK_DEPENDS; then
|
||||||
|
AC_MSG_ERROR("missing dependencies for --enable-manuals")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Put in Makefile with absolute path
|
||||||
|
OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")"
|
||||||
|
AC_SUBST([OSMO_GSM_MANUALS_DIR])
|
||||||
|
fi
|
||||||
|
|
||||||
|
# https://www.freedesktop.org/software/systemd/man/daemon.html
|
||||||
|
AC_ARG_WITH([systemdsystemunitdir],
|
||||||
|
[AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],,
|
||||||
|
[with_systemdsystemunitdir=auto])
|
||||||
|
AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [
|
||||||
|
def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
|
||||||
|
|
||||||
|
AS_IF([test "x$def_systemdsystemunitdir" = "x"],
|
||||||
|
[AS_IF([test "x$with_systemdsystemunitdir" = "xyes"],
|
||||||
|
[AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])])
|
||||||
|
with_systemdsystemunitdir=no],
|
||||||
|
[with_systemdsystemunitdir="$def_systemdsystemunitdir"])])
|
||||||
|
AS_IF([test "x$with_systemdsystemunitdir" != "xno"],
|
||||||
|
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
|
||||||
|
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
|
||||||
|
|
||||||
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
||||||
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
|
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
|
||||||
|
|
||||||
AC_OUTPUT(
|
AC_OUTPUT(
|
||||||
Makefile
|
Makefile
|
||||||
doc/Makefile
|
doc/Makefile
|
||||||
|
doc/examples/Makefile
|
||||||
src/Makefile
|
src/Makefile
|
||||||
src/gsupclient/Makefile
|
src/gsupclient/Makefile
|
||||||
include/Makefile
|
include/Makefile
|
||||||
libosmo-gsup-client.pc
|
libosmo-gsup-client.pc
|
||||||
sql/Makefile
|
sql/Makefile
|
||||||
|
doc/manuals/Makefile
|
||||||
|
contrib/Makefile
|
||||||
|
contrib/systemd/Makefile
|
||||||
tests/Makefile
|
tests/Makefile
|
||||||
tests/auc/Makefile
|
tests/auc/Makefile
|
||||||
tests/auc/gen_ts_55_205_test_sets/Makefile
|
tests/auc/gen_ts_55_205_test_sets/Makefile
|
||||||
|
|||||||
1
contrib/Makefile.am
Normal file
1
contrib/Makefile.am
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SUBDIRS = systemd
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# jenkins build helper script for osmo-hlr. This is how we build on jenkins.osmocom.org
|
# jenkins build helper script for osmo-hlr. This is how we build on jenkins.osmocom.org
|
||||||
|
#
|
||||||
|
# environment variables:
|
||||||
|
# * WITH_MANUALS: build manual PDFs if set to "1"
|
||||||
|
# * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1")
|
||||||
|
#
|
||||||
|
|
||||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
|
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
|
||||||
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
|
echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
|
||||||
@@ -22,10 +27,18 @@ verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
|
|||||||
|
|
||||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||||
export LD_LIBRARY_PATH="$inst/lib"
|
export LD_LIBRARY_PATH="$inst/lib"
|
||||||
|
export PATH="$inst/bin:$PATH"
|
||||||
|
|
||||||
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
|
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
|
||||||
osmo-build-dep.sh libosmo-abis
|
osmo-build-dep.sh libosmo-abis
|
||||||
|
|
||||||
|
# Additional configure options and depends
|
||||||
|
CONFIG=""
|
||||||
|
if [ "$WITH_MANUALS" = "1" ]; then
|
||||||
|
osmo-build-dep.sh osmo-gsm-manuals
|
||||||
|
CONFIG="--enable-manuals"
|
||||||
|
fi
|
||||||
|
|
||||||
set +x
|
set +x
|
||||||
echo
|
echo
|
||||||
echo
|
echo
|
||||||
@@ -36,9 +49,13 @@ set -x
|
|||||||
|
|
||||||
cd "$base"
|
cd "$base"
|
||||||
autoreconf --install --force
|
autoreconf --install --force
|
||||||
./configure --enable-sanitize --enable-external-tests --enable-werror
|
./configure --enable-sanitize --enable-external-tests --enable-werror $CONFIG
|
||||||
$MAKE $PARALLEL_MAKE
|
$MAKE $PARALLEL_MAKE
|
||||||
$MAKE check || cat-testlogs.sh
|
$MAKE check || cat-testlogs.sh
|
||||||
$MAKE distcheck || cat-testlogs.sh
|
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck || cat-testlogs.sh
|
||||||
|
|
||||||
|
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
|
||||||
|
make -C "$base/doc/manuals" publish
|
||||||
|
fi
|
||||||
|
|
||||||
osmo-clean-workspace.sh
|
osmo-clean-workspace.sh
|
||||||
|
|||||||
6
contrib/systemd/Makefile.am
Normal file
6
contrib/systemd/Makefile.am
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
EXTRA_DIST = osmo-hlr.service
|
||||||
|
|
||||||
|
if HAVE_SYSTEMD
|
||||||
|
systemdsystemunit_DATA = \
|
||||||
|
osmo-hlr.service
|
||||||
|
endif
|
||||||
25
debian/control
vendored
25
debian/control
vendored
@@ -32,3 +32,28 @@ Priority: extra
|
|||||||
Depends: osmo-hlr (= ${binary:Version}), ${misc:Depends}
|
Depends: osmo-hlr (= ${binary:Version}), ${misc:Depends}
|
||||||
Description: Debug symbols for the osmo-hlr
|
Description: Debug symbols for the osmo-hlr
|
||||||
Make debugging possible
|
Make debugging possible
|
||||||
|
|
||||||
|
Package: libosmo-gsup-client0
|
||||||
|
Section: libs
|
||||||
|
Architecture: any
|
||||||
|
Multi-Arch: same
|
||||||
|
Depends: ${shlibs:Depends},
|
||||||
|
${misc:Depends}
|
||||||
|
Pre-Depends: ${misc:Pre-Depends}
|
||||||
|
Description: Osmocom GSUP (General Subscriber Update Protocol) client library
|
||||||
|
This is a shared library that can be used to implement client programs for
|
||||||
|
the GSUP protocol. The typical GSUP server is OsmoHLR, with OsmoMSC, OsmoSGSN
|
||||||
|
and External USSD Entities (EUSEs) using this library to implement clients.
|
||||||
|
|
||||||
|
Package: libosmo-gsup-client-dev
|
||||||
|
Architecture: any
|
||||||
|
Multi-Arch: same
|
||||||
|
Depends: ${misc:Depends},
|
||||||
|
libosmo-gsup-client0 (= ${binary:Version}),
|
||||||
|
libosmocore-dev
|
||||||
|
Description: Development headers of Osmocom GSUP client library
|
||||||
|
This is a shared library that can be used to implement client programs for
|
||||||
|
the GSUP protocol. The typical GSUP server is OsmoHLR, with OsmoMSC, OsmoSGSN
|
||||||
|
and External USSD Entities (EUSEs) using this library to implement clients.
|
||||||
|
.
|
||||||
|
This package contains the development headers.
|
||||||
|
|||||||
5
debian/libosmo-gsup-client-dev.install
vendored
Normal file
5
debian/libosmo-gsup-client-dev.install
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
usr/include/osmocom/gsupclient
|
||||||
|
usr/lib/*/libosmo-gsup-client*.a
|
||||||
|
usr/lib/*/libosmo-gsup-client*.so
|
||||||
|
usr/lib/*/libosmo-gsup-client*.la
|
||||||
|
usr/lib/*/pkgconfig/libosmo-gsup-client.pc
|
||||||
1
debian/libosmo-gsup-client0.install
vendored
Normal file
1
debian/libosmo-gsup-client0.install
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
usr/lib/*/libosmo-gsup-client*.so.*
|
||||||
2
debian/osmo-hlr.install
vendored
2
debian/osmo-hlr.install
vendored
@@ -1,3 +1,5 @@
|
|||||||
|
/etc/osmocom/osmo-hlr.cfg
|
||||||
|
/lib/systemd/system/osmo-hlr.service
|
||||||
/usr/bin/osmo-hlr
|
/usr/bin/osmo-hlr
|
||||||
/usr/bin/osmo-hlr-db-tool
|
/usr/bin/osmo-hlr-db-tool
|
||||||
/usr/share/doc/osmo-hlr/sql/hlr.sql
|
/usr/share/doc/osmo-hlr/sql/hlr.sql
|
||||||
|
|||||||
1
debian/osmo-hlr.service
vendored
1
debian/osmo-hlr.service
vendored
@@ -1 +0,0 @@
|
|||||||
../contrib/systemd/osmo-hlr.service
|
|
||||||
3
debian/rules
vendored
3
debian/rules
vendored
@@ -15,3 +15,6 @@ override_dh_strip:
|
|||||||
# Print test results in case of a failure
|
# Print test results in case of a failure
|
||||||
override_dh_auto_test:
|
override_dh_auto_test:
|
||||||
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
|
dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
|
||||||
|
|
||||||
|
override_dh_auto_configure:
|
||||||
|
dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system
|
||||||
|
|||||||
@@ -1,22 +1,4 @@
|
|||||||
CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
|
SUBDIRS = \
|
||||||
|
examples \
|
||||||
dist-hook:
|
manuals \
|
||||||
for f in $$($(CFG_FILES)); do \
|
$(NULL)
|
||||||
j="$(distdir)/$$f" && \
|
|
||||||
mkdir -p "$$(dirname $$j)" && \
|
|
||||||
$(INSTALL_DATA) $(srcdir)/$$f $$j; \
|
|
||||||
done
|
|
||||||
|
|
||||||
install-data-hook:
|
|
||||||
for f in $$($(CFG_FILES)); do \
|
|
||||||
j="$(DESTDIR)$(docdir)/$$f" && \
|
|
||||||
mkdir -p "$$(dirname $$j)" && \
|
|
||||||
$(INSTALL_DATA) $(srcdir)/$$f $$j; \
|
|
||||||
done
|
|
||||||
|
|
||||||
uninstall-hook:
|
|
||||||
@$(PRE_UNINSTALL)
|
|
||||||
for f in $$($(CFG_FILES)); do \
|
|
||||||
j="$(DESTDIR)$(docdir)/$$f" && \
|
|
||||||
$(RM) $$j; \
|
|
||||||
done
|
|
||||||
|
|||||||
27
doc/examples/Makefile.am
Normal file
27
doc/examples/Makefile.am
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
osmoconfdir = $(sysconfdir)/osmocom
|
||||||
|
osmoconf_DATA = osmo-hlr.cfg
|
||||||
|
|
||||||
|
EXTRA_DIST = osmo-hlr.cfg
|
||||||
|
|
||||||
|
CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,'
|
||||||
|
|
||||||
|
dist-hook:
|
||||||
|
for f in $$($(CFG_FILES)); do \
|
||||||
|
j="$(distdir)/$$f" && \
|
||||||
|
mkdir -p "$$(dirname $$j)" && \
|
||||||
|
$(INSTALL_DATA) $(srcdir)/$$f $$j; \
|
||||||
|
done
|
||||||
|
|
||||||
|
install-data-hook:
|
||||||
|
for f in $$($(CFG_FILES)); do \
|
||||||
|
j="$(DESTDIR)$(docdir)/examples/$$f" && \
|
||||||
|
mkdir -p "$$(dirname $$j)" && \
|
||||||
|
$(INSTALL_DATA) $(srcdir)/$$f $$j; \
|
||||||
|
done
|
||||||
|
|
||||||
|
uninstall-hook:
|
||||||
|
@$(PRE_UNINSTALL)
|
||||||
|
for f in $$($(CFG_FILES)); do \
|
||||||
|
j="$(DESTDIR)$(docdir)/examples/$$f" && \
|
||||||
|
$(RM) $$j; \
|
||||||
|
done
|
||||||
@@ -5,9 +5,10 @@ log stderr
|
|||||||
logging filter all 1
|
logging filter all 1
|
||||||
logging color 1
|
logging color 1
|
||||||
logging print category 1
|
logging print category 1
|
||||||
logging timestamp 1
|
logging print category-hex 0
|
||||||
|
logging print level 1
|
||||||
|
logging print file basename last
|
||||||
logging print extended-timestamp 1
|
logging print extended-timestamp 1
|
||||||
logging level all notice
|
|
||||||
logging level main notice
|
logging level main notice
|
||||||
logging level db notice
|
logging level db notice
|
||||||
logging level auc notice
|
logging level auc notice
|
||||||
@@ -22,3 +23,7 @@ hlr
|
|||||||
gsup
|
gsup
|
||||||
bind ip 127.0.0.1
|
bind ip 127.0.0.1
|
||||||
ussd route prefix *#100# internal own-msisdn
|
ussd route prefix *#100# internal own-msisdn
|
||||||
|
ussd route prefix *#101# internal own-imsi
|
||||||
|
ussd route prefix *#102# internal get-ran
|
||||||
|
ussd route prefix *#300# internal umts-off
|
||||||
|
ussd route prefix *#301# internal umts-on
|
||||||
|
|||||||
59
doc/manuals/Makefile.am
Normal file
59
doc/manuals/Makefile.am
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
EXTRA_DIST = example_subscriber_add_update_delete.vty \
|
||||||
|
example_subscriber_cs_ps_enabled.ctrl \
|
||||||
|
example_subscriber_info.ctrl \
|
||||||
|
osmohlr-usermanual.adoc \
|
||||||
|
osmohlr-usermanual-docinfo.xml \
|
||||||
|
osmohlr-vty-reference.xml \
|
||||||
|
chapters \
|
||||||
|
vty
|
||||||
|
|
||||||
|
if BUILD_MANUALS
|
||||||
|
ASCIIDOC = osmohlr-usermanual.adoc
|
||||||
|
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc $(srcdir)/*.vty $(srcdir)/*.ctrl
|
||||||
|
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
|
||||||
|
|
||||||
|
VTY_REFERENCE = osmohlr-vty-reference.xml
|
||||||
|
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
|
||||||
|
|
||||||
|
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
|
||||||
|
endif
|
||||||
|
|
||||||
|
TMP_DB = generated/hlr.db
|
||||||
|
|
||||||
|
update-examples: update-examples-ctrl update-examples-vty
|
||||||
|
|
||||||
|
.PHONY: found-update-deps
|
||||||
|
found-update-deps:
|
||||||
|
@if [ ! -f "$(top_srcdir)/sql/hlr.sql" ]; then \
|
||||||
|
echo "You need to define OSMO_HLR_PATH to point at an osmo-hlr.git"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@if [ -z "$(shell which osmo-hlr)" ]; then \
|
||||||
|
echo "osmo-hlr needs to be installed / available in the PATH"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@if [ -z "$(shell which osmo_verify_transcript_ctrl.py)" ]; then \
|
||||||
|
echo "You need to install git.osmocom.org/python/osmo-python-tests.git"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@if [ -z "$(shell which osmo_verify_transcript_vty.py)" ]; then \
|
||||||
|
echo "You need to install git.osmocom.org/python/osmo-python-tests.git"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
update-examples-ctrl: found-update-deps
|
||||||
|
mkdir -p generated
|
||||||
|
rm -f "$(TMP_DB)"
|
||||||
|
sqlite3 "$(TMP_DB)" < "$(top_srcdir)/sql/hlr.sql"
|
||||||
|
sqlite3 "$(TMP_DB)" < "$(top_srcdir)/tests/test_subscriber.sql"
|
||||||
|
osmo_verify_transcript_ctrl.py \
|
||||||
|
-r "osmo-hlr -l $(TMP_DB) -c $(top_srcdir)/doc/examples/osmo-hlr.cfg" \
|
||||||
|
-p 4259 --update *.ctrl
|
||||||
|
|
||||||
|
update-examples-vty: found-update-deps
|
||||||
|
mkdir -p generated
|
||||||
|
rm -f "$(TMP_DB)"
|
||||||
|
sqlite3 "$(TMP_DB)" < "$(top_srcdir)/sql/hlr.sql"
|
||||||
|
osmo_verify_transcript_vty.py \
|
||||||
|
-r "osmo-hlr -l $(TMP_DB) -c $(top_srcdir)/doc/examples/osmo-hlr.cfg" \
|
||||||
|
-p 4258 --update *.vty
|
||||||
106
doc/manuals/chapters/control.adoc
Normal file
106
doc/manuals/chapters/control.adoc
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
[[hlr-ctrl]]
|
||||||
|
== Control interface
|
||||||
|
|
||||||
|
The actual protocol is described in <<common-control-if>>, the variables common
|
||||||
|
to all programs using it are described in <<ctrl_common_vars>>. This section
|
||||||
|
describes the CTRL interface variables specific to OsmoHLR.
|
||||||
|
|
||||||
|
All subscriber variables are available by different selectors, which are freely
|
||||||
|
interchangeable:
|
||||||
|
|
||||||
|
.Subscriber selectors available on OsmoHLR's Control interface
|
||||||
|
[options="header",width="100%",cols="35%,65%"]
|
||||||
|
|===
|
||||||
|
|Selector|Comment
|
||||||
|
|subscriber.*by-imsi-*'123456'.*|Subscriber selector by IMSI, replace "123456" with the actual IMSI
|
||||||
|
|subscriber.*by-msisdn-*'123456'.*|Subscriber selector by MSISDN
|
||||||
|
|subscriber.*by-id-*'123456'.*|Subscriber selector by database ID
|
||||||
|
|===
|
||||||
|
|
||||||
|
Each of the above selectors feature all of these control variables:
|
||||||
|
|
||||||
|
.Subscriber variables available on OsmoHLR's Control interface
|
||||||
|
[options="header",width="100%",cols="35%,8%,8%,8%,41%"]
|
||||||
|
|===
|
||||||
|
|Name|Access|Trap|Value|Comment
|
||||||
|
|subscriber.by-\*.*info*|R|No||List (short) subscriber information
|
||||||
|
|subscriber.by-\*.*info-aud*|R|No||List subscriber authentication tokens
|
||||||
|
|subscriber.by-\*.*info-all*|R|No||List both 'info' and 'info-aud' in one
|
||||||
|
|subscriber.by-\*.*cs-enabled*|RW|No|'1' or '0'|Enable/disable circuit-switched access
|
||||||
|
|subscriber.by-\*.*ps-enabled*|RW|No|'1' or '0'|Enable/disable packet-switched access
|
||||||
|
|===
|
||||||
|
|
||||||
|
=== subscriber.by-*.info, info-aud, info-all
|
||||||
|
|
||||||
|
Query the HLR database and return current subscriber record, in multiple lines
|
||||||
|
of the format
|
||||||
|
|
||||||
|
----
|
||||||
|
name<tab>value
|
||||||
|
----
|
||||||
|
|
||||||
|
To keep the reply as short as possible, some values are omitted if they are
|
||||||
|
empty. These are the returned values and their presence
|
||||||
|
modalities; for their meaning, see <<subscriber-params>>:
|
||||||
|
|
||||||
|
.Returned values by OsmoHLR's 'info', 'info-all' and 'info-aud' commands
|
||||||
|
[options="header",width="100%",cols="15%,15%,30%,40%"]
|
||||||
|
|===
|
||||||
|
|Returned by 'info-all' and|Name|Format|Presence
|
||||||
|
|'info'|id|-9223372036854775808 .. 9223372036854775807 (usually not negative)|always
|
||||||
|
|'info'|imsi|6 to 15 decimal digits|always
|
||||||
|
|'info'|msisdn|1 to 15 decimal digits|when non-empty
|
||||||
|
|'info'|nam_cs|'1' if CS is enabled, or '0'|always
|
||||||
|
|'info'|nam_ps|'1' if PS is enabled, or '0'|always
|
||||||
|
|'info'|vlr_number|up to 15 decimal digits|when non-empty
|
||||||
|
|'info'|sgsn_number|up to 15 decimal digits|when non-empty
|
||||||
|
|'info'|sgsn_address||when non-empty
|
||||||
|
|'info'|ms_purged_cs|'1' if CS is purged, or '0'|always
|
||||||
|
|'info'|ms_purged_ps|'1' if PS is purged, or '0'|always
|
||||||
|
|'info'|periodic_lu_timer|0..4294967295|always
|
||||||
|
|'info'|periodic_rau_tau_timer|0..4294967295|always
|
||||||
|
|'info'|lmsi|8 hex digits|always
|
||||||
|
|'info-aud'|aud2g.algo|one of 'comp128v1', 'comp128v2', 'comp128v3' or 'xor'|when valid 2G auth data is set
|
||||||
|
|'info-aud'|aud2g.ki|32 hexadecimal digits|when valid 2G auth data is set
|
||||||
|
|'info-aud'|aud3g.algo|so far always 'milenage'|when valid 3G auth data is set
|
||||||
|
|'info-aud'|aud3g.k|32 hexadecimal digits|when valid 3G auth data is set
|
||||||
|
|'info-aud'|aud3g.op|32 hexadecimal digits|when valid 3G auth data is set, *not* when OPC is set
|
||||||
|
|'info-aud'|aud3g.opc|32 hexadecimal digits|when valid 3G auth data is set, *not* when OP is set
|
||||||
|
|'info-aud'|aud3g.ind_bitlen|0..28|when valid 3G auth data is set
|
||||||
|
|'info-aud'|aud3g.sqn|0 .. 18446744073709551615|when valid 3G auth data is set
|
||||||
|
|===
|
||||||
|
|
||||||
|
This is an example Control Interface transcript that illustrates the various
|
||||||
|
'info' commands:
|
||||||
|
|
||||||
|
----
|
||||||
|
include::../example_subscriber_info.ctrl[]
|
||||||
|
----
|
||||||
|
|
||||||
|
=== subscriber.by-*.ps-enabled, cs-enabled
|
||||||
|
|
||||||
|
Disable or enable packet-/circuit-switched access for the given IMSI;
|
||||||
|
|
||||||
|
* 'ps-enabled' switches access to GPRS or UMTS data services,
|
||||||
|
* 'cs-enabled' switches access to voice services.
|
||||||
|
|
||||||
|
When disabled, the next time this subscriber attempts to do a Location Updating
|
||||||
|
GSUP operation for the given domain (i.e. from the SGSN for 'ps-enabled', from
|
||||||
|
the MSC/VLR for 'cs-enabled'), it will be rejected by OsmoHLR. Currently
|
||||||
|
connected GSUP clients will be notified via GSUP when a subscriber is being
|
||||||
|
disabled, so that the subscriber can be dropped in case it is currently
|
||||||
|
attached.
|
||||||
|
|
||||||
|
The current 'ps-enabled'/'cs-enabled' status can be queried by 'GET' commands,
|
||||||
|
and also by looking at 'nam_ps' and 'nam_cs' in a 'subscriber.by-*.info'
|
||||||
|
response.
|
||||||
|
|
||||||
|
A value of "1" indicates that the given domain is enabled, which is the
|
||||||
|
default; a value of "0" disables access.
|
||||||
|
|
||||||
|
This is an example transcript that illustrates 'ps-enabled' and 'cs-enabled'
|
||||||
|
commands:
|
||||||
|
|
||||||
|
----
|
||||||
|
include::../example_subscriber_cs_ps_enabled.ctrl[]
|
||||||
|
----
|
||||||
69
doc/manuals/chapters/overview.adoc
Normal file
69
doc/manuals/chapters/overview.adoc
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
[[overview]]
|
||||||
|
== Overview
|
||||||
|
|
||||||
|
This manual should help you getting started with OsmoHLR. It will cover
|
||||||
|
aspects of configuring and running the OsmoHLR.
|
||||||
|
|
||||||
|
[[intro_overview]]
|
||||||
|
=== About OsmoHLR
|
||||||
|
|
||||||
|
OsmoHLR is Osmocom's minimal implementation of a Home Location Register (HLR)
|
||||||
|
for 2G and 3G GSM and UMTS mobile core networks. Its interfaces are:
|
||||||
|
|
||||||
|
- GSUP, serving towards OsmoMSC and OsmoSGSN;
|
||||||
|
- A local SQLite database;
|
||||||
|
- The Osmocom typical telnet VTY and CTRL interfaces.
|
||||||
|
|
||||||
|
Originally, the OpenBSC project's OsmoNITB all-in-one implementation had an
|
||||||
|
integrated HLR, managing subscribers and SMS in the same local database. Along
|
||||||
|
with the separate OsmoMSC and its new VLR component, OsmoHLR was implemented
|
||||||
|
from scratch to alleviate various shortcomings of the internal HLR:
|
||||||
|
|
||||||
|
- The separate HLR allows using centralized subscriber management for both
|
||||||
|
circuit-switched and packet-switched domains (i.e. one OsmoHLR for both
|
||||||
|
OsmoMSC and OsmoSGSN).
|
||||||
|
|
||||||
|
- VLR and HLR brought full UMTS AKA (Authentication and Key Agreement) support,
|
||||||
|
i.e. Milenage authentication in both the full 3G variant as well as the
|
||||||
|
backwards compatible 2G variant.
|
||||||
|
|
||||||
|
- In contrast to the OsmoNITB, the specific way the new OsmoMSC's VLR accesses
|
||||||
|
OsmoHLR brings fully asynchronous subscriber database access.
|
||||||
|
|
||||||
|
Find the OsmoHLR issue tracker and wiki online at
|
||||||
|
|
||||||
|
- https://osmocom.org/projects/osmo-hlr
|
||||||
|
- https://osmocom.org/projects/osmo-hlr/wiki
|
||||||
|
|
||||||
|
|
||||||
|
[[fig-gsm]]
|
||||||
|
.Typical GSM network architecture used with OsmoHLR
|
||||||
|
[graphviz]
|
||||||
|
----
|
||||||
|
digraph G {
|
||||||
|
rankdir=LR;
|
||||||
|
subgraph cluster_hlr {
|
||||||
|
label = "OsmoHLR";
|
||||||
|
GSUP [label="GSUP server"]
|
||||||
|
DB [label="SQLite DB"]
|
||||||
|
GSUP->DB
|
||||||
|
DB->CTRL [dir="back"]
|
||||||
|
DB->VTY [dir="back"]
|
||||||
|
}
|
||||||
|
|
||||||
|
Admin [label="Admin and\nMaintenance"]
|
||||||
|
SW [label="3rd party software\nintegration"]
|
||||||
|
VTY->Admin [dir="back"]
|
||||||
|
CTRL->SW [dir="back"]
|
||||||
|
|
||||||
|
MSC [label="MSC/VLR"]
|
||||||
|
MSC->GSUP [label="GSUP"]
|
||||||
|
SGSN->GSUP [label="GSUP"]
|
||||||
|
|
||||||
|
BSC->MSC
|
||||||
|
HNBGW->MSC
|
||||||
|
HNBGW->SGSN
|
||||||
|
PCU->SGSN
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
80
doc/manuals/chapters/running.adoc
Normal file
80
doc/manuals/chapters/running.adoc
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
== Running OsmoHLR
|
||||||
|
|
||||||
|
The OsmoHLR executable (`osmo-hlr`) offers the following command-line
|
||||||
|
arguments:
|
||||||
|
|
||||||
|
=== SYNOPSIS
|
||||||
|
|
||||||
|
*osmo-hlr* [-h|-V] [-d 'DBGMASK'] [-D] [-c 'CONFIGFILE'] [-s] [-T] [-e 'LOGLEVEL'] [-l 'DATABASE']
|
||||||
|
|
||||||
|
=== OPTIONS
|
||||||
|
|
||||||
|
*-h, --help*::
|
||||||
|
Print a short help message about the supported options
|
||||||
|
*-V, --version*::
|
||||||
|
Print the compile-time version number of the OsmoBTS program
|
||||||
|
*-d, --debug 'DBGMASK','DBGLEVELS'*::
|
||||||
|
Set the log subsystems and levels for logging to stderr. This
|
||||||
|
has mostly been superseded by VTY-based logging configuration,
|
||||||
|
see <<logging>> for further information.
|
||||||
|
*-D, --daemonize*::
|
||||||
|
Fork the process as a daemon into background.
|
||||||
|
*-c, --config-file 'CONFIGFILE'*::
|
||||||
|
Specify the file and path name of the configuration file to be
|
||||||
|
used. If none is specified, use `osmo-hlr.cfg` in the current
|
||||||
|
working directory.
|
||||||
|
*-s, --disable-color*::
|
||||||
|
Disable colors for logging to stderr. This has mostly been
|
||||||
|
deprecated by VTY based logging configuration, see <<logging>>
|
||||||
|
for more information.
|
||||||
|
*-T, --timestamp*::
|
||||||
|
Enable time-stamping of log messages to stderr. This has mostly
|
||||||
|
been deprecated by VTY based logging configuration, see
|
||||||
|
<<logging>> for more information.
|
||||||
|
*-e, --log-level 'LOGLEVEL'*::
|
||||||
|
Set the global log level for logging to stderr. This has mostly
|
||||||
|
been deprecated by VTY based logging configuration, see
|
||||||
|
<<logging>> for more information.
|
||||||
|
*-l, --database 'DATABASE'*::
|
||||||
|
Specify the file name of the SQLite3 database to use as HLR/AUC
|
||||||
|
storage
|
||||||
|
|
||||||
|
=== Bootstrap the Database
|
||||||
|
|
||||||
|
If no database exists yet, OsmoHLR will automatically create and bootstrap a
|
||||||
|
database file with empty tables. If no `-l` command-line option is provided,
|
||||||
|
this database file will be created in the current working directory.
|
||||||
|
|
||||||
|
Alternatively, you may use the `osmo-hlr-db-tool`, which is installed along
|
||||||
|
with `osmo-hlr`, to bootstrap an empty database, or to migrate subscriber data
|
||||||
|
from an old 'OsmoNITB' database. See `osmo-hlr-db-tool --help`.
|
||||||
|
|
||||||
|
=== Multiple instances
|
||||||
|
|
||||||
|
Running multiple instances of `osmo-hlr` on the same computer is possible if
|
||||||
|
all interfaces (VTY, CTRL) are separated using the appropriate configuration
|
||||||
|
options. The IP based interfaces are binding to local host by default. In order
|
||||||
|
to separate the processes, the user has to bind those services to specific but
|
||||||
|
different IP addresses and/or ports.
|
||||||
|
|
||||||
|
The VTY and the Control interface can be bound to IP addresses from the loopback
|
||||||
|
address range, for example:
|
||||||
|
|
||||||
|
----
|
||||||
|
line vty
|
||||||
|
bind 127.0.0.2
|
||||||
|
ctrl
|
||||||
|
bind 127.0.0.2
|
||||||
|
----
|
||||||
|
|
||||||
|
The GSUP interface can be bound to a specific IP address by the following
|
||||||
|
configuration options:
|
||||||
|
|
||||||
|
----
|
||||||
|
hlr
|
||||||
|
gsup
|
||||||
|
bind ip 10.23.42.1
|
||||||
|
----
|
||||||
|
|
||||||
|
NOTE: At the time of writing, OsmoHLR lacks a config option to change the GSUP
|
||||||
|
port, which is by default TCP port 4222.
|
||||||
69
doc/manuals/chapters/subscribers.adoc
Normal file
69
doc/manuals/chapters/subscribers.adoc
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
== Managing Subscribers
|
||||||
|
|
||||||
|
Subscribers are kept in a local SQLite database file and can be managed via VTY
|
||||||
|
and CTRL interfaces.
|
||||||
|
|
||||||
|
This section provides some examples; also refer to the OsmoHLR VTY reference
|
||||||
|
manual <<vty-ref-osmohlr>> as well as the Control interface described in
|
||||||
|
<<hlr-ctrl>>.
|
||||||
|
|
||||||
|
=== Example: Add/Update/Delete Subscriber via VTY
|
||||||
|
|
||||||
|
The following telnet VTY session adds a subscriber complete with GSM (2G) and
|
||||||
|
UMTS (3G and 2G) authentication tokens, and finally removes the subscriber
|
||||||
|
again; it assumes that osmo-hlr is running and listening for telnet VTY
|
||||||
|
connections on localhost:
|
||||||
|
|
||||||
|
----
|
||||||
|
$ telnet localhost 4258
|
||||||
|
include::../example_subscriber_add_update_delete.vty[]
|
||||||
|
----
|
||||||
|
|
||||||
|
[[subscriber-params]]
|
||||||
|
=== Subscriber Parameters
|
||||||
|
|
||||||
|
The following parameters are managed for each subscriber of the HLR, modelled
|
||||||
|
roughly after 3GPP TS 23.008, version 13.3.0; note that not all of these
|
||||||
|
parameters are necessarily in active use.
|
||||||
|
|
||||||
|
The `aud3g` table also applies to 2G networks: it provides UMTS AKA tokens for
|
||||||
|
Milenage authentication, which is available both on 3G and 2G networks. On 2G,
|
||||||
|
when both MS and network are R99 capable (like OsmoMSC and OsmoSGSN are), the
|
||||||
|
full UMTS AKA with Milenage keys from `aud_3g`, using AUTN and extended RES
|
||||||
|
tokens, is available. With pre-R99 MS or network configurations, the GSM AKA
|
||||||
|
compatible variant of Milenage, still using the Milenage keys from `aud_3g` but
|
||||||
|
transceiving only RAND and SRES, may be applicable. (See 3GPP TS 33.102, chapter
|
||||||
|
6.8.1, Authentication and key agreement of UMTS subscribers.)
|
||||||
|
|
||||||
|
.OsmoHLR's subscriber parameters
|
||||||
|
[options="header",width="100%",cols="20%,20%,60%"]
|
||||||
|
|===
|
||||||
|
|Name|Example|Description
|
||||||
|
|imsi|901700000014701|identity of the SIM/USIM, 3GPP TS 23.008 chapter 2.1.1.1
|
||||||
|
|msisdn|2342123|number to dial to reach this subscriber (multiple MSISDNs can be stored per subscriber), 3GPP TS 23.008 chapter 2.1.2
|
||||||
|
|imeisv|4234234234234275|identity of the mobile device and software version, 3GPP TS 23.008 chapter 2.2.3
|
||||||
|
|aud2g.algo|comp128v3|Authentication algorithm ID for GSM AKA, corresponds to enum osmo_auth_algo
|
||||||
|
|aud2g.ki||Subscriber's secret key (128bit)
|
||||||
|
|aud3g.algo|milenage|Authentication algorithm ID for UMTS AKA (applies to both 3G and 2G networks), corresponds to enum osmo_auth_algo
|
||||||
|
|aud3g.k|(32 hexadecimal digits)|Subscriber's secret key (128bit)
|
||||||
|
|aud3g.op|(32 hexadecimal digits)|Operator's secret key (128bit)
|
||||||
|
|aud3g.opc|(32 hexadecimal digits)|Secret key derived from OP and K (128bit), alternative to using OP which does not disclose OP to subscribers
|
||||||
|
|aud3g.sqn|123|Sequence number of last used key (64bit unsigned)
|
||||||
|
|aud3g.ind_bitlen|5|Nr of index bits at lower SQN end
|
||||||
|
|apn||
|
||||||
|
|vlr_number||3GPP TS 23.008 chapter 2.4.5
|
||||||
|
|hlr_number||3GPP TS 23.008 chapter 2.4.6
|
||||||
|
|sgsn_number||3GPP TS 23.008 chapter 2.4.8.1
|
||||||
|
|sgsn_address||3GPP TS 23.008 chapter 2.13.10
|
||||||
|
|ggsn_number||3GPP TS 23.008 chapter 2.4.8.2
|
||||||
|
|gmlc_number||3GPP TS 23.008 chapter 2.4.9.2
|
||||||
|
|smsc_number||3GPP TS 23.008 chapter 2.4.23
|
||||||
|
|periodic_lu_tmr||3GPP TS 23.008 chapter 2.4.24
|
||||||
|
|periodic_rau_tau_tmr||3GPP TS 23.008 chapter 2.13.115
|
||||||
|
|nam_cs|1|Enable/disable voice access (3GPP TS 23.008 chapter 2.1.1.2: network access mode)
|
||||||
|
|nam_ps|0|Enable/disable data access (3GPP TS 23.008 chapter 2.1.1.2: network access mode)
|
||||||
|
|lmsi||3GPP TS 23.008 chapter 2.1.8
|
||||||
|
|ms_purged_cs|0|3GPP TS 23.008 chapter 2.7.5
|
||||||
|
|ms_purged_ps|1|3GPP TS 23.008 chapter 2.7.6
|
||||||
|
|===
|
||||||
|
|
||||||
78
doc/manuals/chapters/ussd.adoc
Normal file
78
doc/manuals/chapters/ussd.adoc
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
[[ussd]]
|
||||||
|
== Unstructured Supplementary Services Data (USSD)
|
||||||
|
|
||||||
|
The _Unstructured Supplementary Services Data (USSD)_ is one service within
|
||||||
|
2G/3G networks next to other services such as circuit-switched voice, packet-switched
|
||||||
|
data and SMS (Short Message Service).
|
||||||
|
|
||||||
|
It is on an abstract level quite similar to SMS in that USSD can be used to send
|
||||||
|
textual messages. However, there are the following differences:
|
||||||
|
|
||||||
|
* USSD is between the MS (phone) and an USSD application on the network, while
|
||||||
|
SMS is primarily between two subscribers identified by their MSISDN
|
||||||
|
* USSD is faster, as it doesn't suffer from the complicated three-layer CP/RP/TP
|
||||||
|
protocol stack of SMS with it's acknowledgement of the acknowledged acknowledgement.
|
||||||
|
* USSD is session-oriented, i.e. a dialogue/session between subscriber and application
|
||||||
|
can persist for the transfer of more than one message. The dedicated radio channel
|
||||||
|
on the RAN remains established throughout that dialogue.
|
||||||
|
|
||||||
|
=== USSD in Osmocom
|
||||||
|
|
||||||
|
Until August 2018, OsmoMSC contained some minimalistic internal USSD
|
||||||
|
handling with no
|
||||||
|
ability to attach/extend it with external USSD applications.
|
||||||
|
|
||||||
|
From August 2018 onwards, OsmoMSC doesn't contain any internal USSD
|
||||||
|
handlers/applications anymore. Instead, all USSD is transported to/from
|
||||||
|
OsmoHLR via the GSUP protocol.
|
||||||
|
|
||||||
|
OsmoHLR contains some intenal USSD handlers and can route USSD messages
|
||||||
|
to any number of external USSD entities (EUSEs). The EUSE also use GSUP
|
||||||
|
to communicate USSD from/to OsmoHLR.
|
||||||
|
|
||||||
|
Each EUSE is identified by its name. The name consists of a single-word
|
||||||
|
string preceding a currently fixed ("-00-00-00-00-00-00") suffix.
|
||||||
|
There is no authentication between EUSE and OsmoHLR: Any client program
|
||||||
|
able to connect to the GSUP port of OsmoHLR can register as any EUSE
|
||||||
|
(name).
|
||||||
|
|
||||||
|
NOTE:: We plan to remove the requirement for this suffix as soon as we
|
||||||
|
are done resolving all more important issues.
|
||||||
|
|
||||||
|
=== USSD Configuration
|
||||||
|
|
||||||
|
USSD configuration in OsmoHLR happens within the `hlr` VTY node.
|
||||||
|
|
||||||
|
`euse foobar-00-00-00-00-00-00` defines an EUSE with the given name `foobar`
|
||||||
|
|
||||||
|
`ussd route prefix *123 external foobar-00-00-00-00-00-00` installs a
|
||||||
|
prefix route to the named EUSE. All USSD short codes starting with *123 will be
|
||||||
|
routed to the named EUSE.
|
||||||
|
|
||||||
|
`ussd route prefix *#100# internal own-msisdn` installs a prefix route
|
||||||
|
to the named internal USSD handler. There above command will restore
|
||||||
|
the old behavior, in which *#100# will return a text message containing
|
||||||
|
the subscribers own phone number. There is one other handler called
|
||||||
|
`own-imsi` which will return the IMSI instead of the MSISDN.
|
||||||
|
|
||||||
|
`ussd default-route external foobar-00-00-00-00-00-00` installs a
|
||||||
|
default route to the named EUSE. This means that all USSD codes for
|
||||||
|
which no more specific route exists will be routed to the named EUSE.
|
||||||
|
|
||||||
|
=== Example EUSE program
|
||||||
|
|
||||||
|
We have provided an example EUSE developed in C language using existing
|
||||||
|
Osmocom libraries for GSUP protocol handling and USSD encoding/decoding.
|
||||||
|
It will register as `foobar` EUSE to OsmoHLR on localhost. You can run
|
||||||
|
it on a different machine by specifying e.g. `osmo-euse-demo 1.2.3.4 5678`
|
||||||
|
to make it connect to OsmoHLR on IP address 1.2.3.4 and GSUP/TCP port
|
||||||
|
5678.
|
||||||
|
|
||||||
|
The idea is that you can use this as a template to develop your own USSD
|
||||||
|
applications, or any gateways to other protocols or interfaces.
|
||||||
|
|
||||||
|
You can find it in `osmo-hlr/src/osmo-euse-demo.c` or online by
|
||||||
|
following the link to http://git.osmocom.org/osmo-hlr/tree/src/osmo-euse-demo.c
|
||||||
|
|
||||||
|
This demonstration program will echo back any USSD message sent/routed
|
||||||
|
to it, quoted like _You sent "..."_.
|
||||||
34
doc/manuals/example_subscriber_add_update_delete.vty
Normal file
34
doc/manuals/example_subscriber_add_update_delete.vty
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
OsmoHLR> enable
|
||||||
|
OsmoHLR# subscriber imsi 123456789023000 create
|
||||||
|
% Created subscriber 123456789023000
|
||||||
|
ID: 1
|
||||||
|
IMSI: 123456789023000
|
||||||
|
MSISDN: none
|
||||||
|
|
||||||
|
OsmoHLR# subscriber imsi 123456789023000 update msisdn 423
|
||||||
|
% Updated subscriber IMSI='123456789023000' to MSISDN='423'
|
||||||
|
|
||||||
|
OsmoHLR# subscriber msisdn 423 update aud3g milenage k deaf0ff1ced0d0dabbedd1ced1cef00d opc cededeffacedacefacedbadfadedbeef
|
||||||
|
OsmoHLR# subscriber msisdn 423 show
|
||||||
|
ID: 1
|
||||||
|
IMSI: 123456789023000
|
||||||
|
MSISDN: 423
|
||||||
|
3G auth: MILENAGE
|
||||||
|
K=deaf0ff1ced0d0dabbedd1ced1cef00d
|
||||||
|
OPC=cededeffacedacefacedbadfadedbeef
|
||||||
|
IND-bitlen=5
|
||||||
|
|
||||||
|
OsmoHLR# subscriber msisdn 423 update aud2g comp128v3 ki beefedcafefaceacedaddeddecadefee
|
||||||
|
OsmoHLR# subscriber msisdn 423 show
|
||||||
|
ID: 1
|
||||||
|
IMSI: 123456789023000
|
||||||
|
MSISDN: 423
|
||||||
|
2G auth: COMP128v3
|
||||||
|
KI=beefedcafefaceacedaddeddecadefee
|
||||||
|
3G auth: MILENAGE
|
||||||
|
K=deaf0ff1ced0d0dabbedd1ced1cef00d
|
||||||
|
OPC=cededeffacedacefacedbadfadedbeef
|
||||||
|
IND-bitlen=5
|
||||||
|
|
||||||
|
OsmoHLR# subscriber imsi 123456789023000 delete
|
||||||
|
% Deleted subscriber for IMSI '123456789023000'
|
||||||
71
doc/manuals/example_subscriber_cs_ps_enabled.ctrl
Normal file
71
doc/manuals/example_subscriber_cs_ps_enabled.ctrl
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
GET 1 subscriber.by-msisdn-103.info
|
||||||
|
GET_REPLY 1 subscriber.by-msisdn-103.info
|
||||||
|
id 3
|
||||||
|
imsi 901990000000003
|
||||||
|
msisdn 103
|
||||||
|
nam_cs 1
|
||||||
|
nam_ps 1
|
||||||
|
ms_purged_cs 0
|
||||||
|
ms_purged_ps 0
|
||||||
|
periodic_lu_timer 0
|
||||||
|
periodic_rau_tau_timer 0
|
||||||
|
lmsi 00000000
|
||||||
|
|
||||||
|
GET 2 subscriber.by-msisdn-103.ps-enabled
|
||||||
|
GET_REPLY 2 subscriber.by-msisdn-103.ps-enabled 1
|
||||||
|
|
||||||
|
SET 3 subscriber.by-msisdn-103.ps-enabled 0
|
||||||
|
SET_REPLY 3 subscriber.by-msisdn-103.ps-enabled OK
|
||||||
|
|
||||||
|
GET 4 subscriber.by-msisdn-103.ps-enabled
|
||||||
|
GET_REPLY 4 subscriber.by-msisdn-103.ps-enabled 0
|
||||||
|
|
||||||
|
GET 5 subscriber.by-msisdn-103.info
|
||||||
|
GET_REPLY 5 subscriber.by-msisdn-103.info
|
||||||
|
id 3
|
||||||
|
imsi 901990000000003
|
||||||
|
msisdn 103
|
||||||
|
nam_cs 1
|
||||||
|
nam_ps 0
|
||||||
|
ms_purged_cs 0
|
||||||
|
ms_purged_ps 0
|
||||||
|
periodic_lu_timer 0
|
||||||
|
periodic_rau_tau_timer 0
|
||||||
|
lmsi 00000000
|
||||||
|
|
||||||
|
SET 6 subscriber.by-msisdn-103.cs-enabled 0
|
||||||
|
SET_REPLY 6 subscriber.by-msisdn-103.cs-enabled OK
|
||||||
|
|
||||||
|
GET 7 subscriber.by-msisdn-103.cs-enabled
|
||||||
|
GET_REPLY 7 subscriber.by-msisdn-103.cs-enabled 0
|
||||||
|
|
||||||
|
GET 8 subscriber.by-msisdn-103.info
|
||||||
|
GET_REPLY 8 subscriber.by-msisdn-103.info
|
||||||
|
id 3
|
||||||
|
imsi 901990000000003
|
||||||
|
msisdn 103
|
||||||
|
nam_cs 0
|
||||||
|
nam_ps 0
|
||||||
|
ms_purged_cs 0
|
||||||
|
ms_purged_ps 0
|
||||||
|
periodic_lu_timer 0
|
||||||
|
periodic_rau_tau_timer 0
|
||||||
|
lmsi 00000000
|
||||||
|
|
||||||
|
SET 9 subscriber.by-msisdn-103.cs-enabled 1
|
||||||
|
SET_REPLY 9 subscriber.by-msisdn-103.cs-enabled OK
|
||||||
|
SET 10 subscriber.by-msisdn-103.ps-enabled 1
|
||||||
|
SET_REPLY 10 subscriber.by-msisdn-103.ps-enabled OK
|
||||||
|
|
||||||
|
GET 11 subscriber.by-msisdn-103.info
|
||||||
|
GET_REPLY 11 subscriber.by-msisdn-103.info
|
||||||
|
id 3
|
||||||
|
imsi 901990000000003
|
||||||
|
msisdn 103
|
||||||
|
nam_cs 1
|
||||||
|
nam_ps 1
|
||||||
|
ms_purged_cs 0
|
||||||
|
ms_purged_ps 0
|
||||||
|
periodic_lu_timer 0
|
||||||
|
periodic_rau_tau_timer 0
|
||||||
|
lmsi 00000000
|
||||||
42
doc/manuals/example_subscriber_info.ctrl
Normal file
42
doc/manuals/example_subscriber_info.ctrl
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
GET 1 subscriber.by-imsi-901990000000003.info
|
||||||
|
GET_REPLY 1 subscriber.by-imsi-901990000000003.info
|
||||||
|
id 3
|
||||||
|
imsi 901990000000003
|
||||||
|
msisdn 103
|
||||||
|
nam_cs 1
|
||||||
|
nam_ps 1
|
||||||
|
ms_purged_cs 0
|
||||||
|
ms_purged_ps 0
|
||||||
|
periodic_lu_timer 0
|
||||||
|
periodic_rau_tau_timer 0
|
||||||
|
lmsi 00000000
|
||||||
|
|
||||||
|
GET 2 subscriber.by-msisdn-103.info-aud
|
||||||
|
GET_REPLY 2 subscriber.by-msisdn-103.info-aud
|
||||||
|
aud2g.algo COMP128v1
|
||||||
|
aud2g.ki 000102030405060708090a0b0c0d0e0f
|
||||||
|
aud3g.algo MILENAGE
|
||||||
|
aud3g.k 000102030405060708090a0b0c0d0e0f
|
||||||
|
aud3g.opc 101112131415161718191a1b1c1d1e1f
|
||||||
|
aud3g.ind_bitlen 5
|
||||||
|
aud3g.sqn 0
|
||||||
|
|
||||||
|
GET 3 subscriber.by-id-3.info-all
|
||||||
|
GET_REPLY 3 subscriber.by-id-3.info-all
|
||||||
|
id 3
|
||||||
|
imsi 901990000000003
|
||||||
|
msisdn 103
|
||||||
|
nam_cs 1
|
||||||
|
nam_ps 1
|
||||||
|
ms_purged_cs 0
|
||||||
|
ms_purged_ps 0
|
||||||
|
periodic_lu_timer 0
|
||||||
|
periodic_rau_tau_timer 0
|
||||||
|
lmsi 00000000
|
||||||
|
aud2g.algo COMP128v1
|
||||||
|
aud2g.ki 000102030405060708090a0b0c0d0e0f
|
||||||
|
aud3g.algo MILENAGE
|
||||||
|
aud3g.k 000102030405060708090a0b0c0d0e0f
|
||||||
|
aud3g.opc 101112131415161718191a1b1c1d1e1f
|
||||||
|
aud3g.ind_bitlen 5
|
||||||
|
aud3g.sqn 0
|
||||||
47
doc/manuals/osmohlr-usermanual-docinfo.xml
Normal file
47
doc/manuals/osmohlr-usermanual-docinfo.xml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<revhistory>
|
||||||
|
<revision>
|
||||||
|
<revnumber>1</revnumber>
|
||||||
|
<date>September 18th, 2017</date>
|
||||||
|
<authorinitials>NH</authorinitials>
|
||||||
|
<revremark>
|
||||||
|
Initial version; based on OsmoNITB manual version 2.
|
||||||
|
</revremark>
|
||||||
|
</revision>
|
||||||
|
</revhistory>
|
||||||
|
|
||||||
|
<authorgroup>
|
||||||
|
<author>
|
||||||
|
<firstname>Neels</firstname>
|
||||||
|
<surname>Hofmeyr</surname>
|
||||||
|
<email>nhofmeyr@sysmocom.de</email>
|
||||||
|
<authorinitials>NH</authorinitials>
|
||||||
|
<affiliation>
|
||||||
|
<shortaffil>sysmocom</shortaffil>
|
||||||
|
<orgname>sysmocom - s.f.m.c. GmbH</orgname>
|
||||||
|
<jobtitle>Senior Developer</jobtitle>
|
||||||
|
</affiliation>
|
||||||
|
</author>
|
||||||
|
</authorgroup>
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
<year>2017</year>
|
||||||
|
<holder>sysmocom - s.f.m.c. GmbH</holder>
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<legalnotice>
|
||||||
|
<para>
|
||||||
|
Permission is granted to copy, distribute and/or modify this
|
||||||
|
document under the terms of the GNU Free Documentation License,
|
||||||
|
Version 1.3 or any later version published by the Free Software
|
||||||
|
Foundation; with the Invariant Sections being just 'Foreword',
|
||||||
|
'Acknowledgements' and 'Preface', with no Front-Cover Texts,
|
||||||
|
and no Back-Cover Texts. A copy of the license is included in
|
||||||
|
the section entitled "GNU Free Documentation License".
|
||||||
|
</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>
|
||||||
|
</para>
|
||||||
|
</legalnotice>
|
||||||
36
doc/manuals/osmohlr-usermanual.adoc
Normal file
36
doc/manuals/osmohlr-usermanual.adoc
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
:gfdl-enabled:
|
||||||
|
:program-name: OsmoHLR
|
||||||
|
|
||||||
|
OsmoHLR User Manual
|
||||||
|
====================
|
||||||
|
Neels Hofmeyr <nhofmeyr@sysmocom.de>
|
||||||
|
|
||||||
|
|
||||||
|
include::./common/chapters/preface.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/chapters/overview.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/chapters/running.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/chapters/subscribers.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/chapters/ussd.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/vty.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/logging.adoc[]
|
||||||
|
|
||||||
|
include::{srcdir}/chapters/control.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/control_if.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/gsup.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/port_numbers.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/bibliography.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/glossary.adoc[]
|
||||||
|
|
||||||
|
include::./common/chapters/gfdl.adoc[]
|
||||||
|
|
||||||
38
doc/manuals/osmohlr-vty-reference.xml
Normal file
38
doc/manuals/osmohlr-vty-reference.xml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
ex:ts=2:sw=42sts=2:et
|
||||||
|
-*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
||||||
|
-->
|
||||||
|
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
|
||||||
|
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
|
||||||
|
<!ENTITY chapter-vty SYSTEM "./common/chapters/vty.xml" >
|
||||||
|
<!ENTITY sections-vty SYSTEM "generated/docbook_vty.xml" >
|
||||||
|
]>
|
||||||
|
|
||||||
|
<book>
|
||||||
|
<info>
|
||||||
|
<revhistory>
|
||||||
|
<revision>
|
||||||
|
<revnumber>v1</revnumber>
|
||||||
|
<date>18th September 2017</date>
|
||||||
|
<authorinitials>nh</authorinitials>
|
||||||
|
<revremark>Initial</revremark>
|
||||||
|
</revision>
|
||||||
|
</revhistory>
|
||||||
|
|
||||||
|
<title>OsmoHLR VTY Reference</title>
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
<year>2017</year>
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<legalnotice>
|
||||||
|
<para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved.
|
||||||
|
</para>
|
||||||
|
</legalnotice>
|
||||||
|
</info>
|
||||||
|
|
||||||
|
<!-- Main chapters-->
|
||||||
|
&chapter-vty;
|
||||||
|
</book>
|
||||||
|
|
||||||
2
doc/manuals/vty/hlr_vty_additions.xml
Normal file
2
doc/manuals/vty/hlr_vty_additions.xml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
|
||||||
|
</vtydoc>
|
||||||
1162
doc/manuals/vty/hlr_vty_reference.xml
Normal file
1162
doc/manuals/vty/hlr_vty_reference.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
#include <osmocom/core/timer.h>
|
#include <osmocom/core/timer.h>
|
||||||
#include <osmocom/gsm/oap_client.h>
|
#include <osmocom/gsm/oap_client.h>
|
||||||
|
#include <osmocom/gsm/ipa.h>
|
||||||
|
#include <osmocom/gsm/gsup.h>
|
||||||
|
|
||||||
/* a loss of GSUP between MSC and HLR is considered quite serious, let's try to recover as quickly as
|
/* a loss of GSUP between MSC and HLR is considered quite serious, let's try to recover as quickly as
|
||||||
* possible. Even one new connection attempt per second should be quite acceptable until the link is
|
* possible. Even one new connection attempt per second should be quite acceptable until the link is
|
||||||
@@ -38,7 +40,7 @@ struct osmo_gsup_client;
|
|||||||
typedef int (*osmo_gsup_client_read_cb_t)(struct osmo_gsup_client *gsupc, struct msgb *msg);
|
typedef int (*osmo_gsup_client_read_cb_t)(struct osmo_gsup_client *gsupc, struct msgb *msg);
|
||||||
|
|
||||||
struct osmo_gsup_client {
|
struct osmo_gsup_client {
|
||||||
const char *unit_name;
|
const char *unit_name; /* same as ipa_dev->unit_name, for backwards compat */
|
||||||
|
|
||||||
struct ipa_client_conn *link;
|
struct ipa_client_conn *link;
|
||||||
osmo_gsup_client_read_cb_t read_cb;
|
osmo_gsup_client_read_cb_t read_cb;
|
||||||
@@ -50,8 +52,16 @@ struct osmo_gsup_client {
|
|||||||
struct osmo_timer_list connect_timer;
|
struct osmo_timer_list connect_timer;
|
||||||
int is_connected;
|
int is_connected;
|
||||||
int got_ipa_pong;
|
int got_ipa_pong;
|
||||||
|
|
||||||
|
struct ipaccess_unit *ipa_dev; /* identification information sent to IPA server */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
|
||||||
|
struct ipaccess_unit *ipa_dev,
|
||||||
|
const char *ip_addr,
|
||||||
|
unsigned int tcp_port,
|
||||||
|
osmo_gsup_client_read_cb_t read_cb,
|
||||||
|
struct osmo_oap_client_config *oapc_config);
|
||||||
struct osmo_gsup_client *osmo_gsup_client_create(void *talloc_ctx,
|
struct osmo_gsup_client *osmo_gsup_client_create(void *talloc_ctx,
|
||||||
const char *unit_name,
|
const char *unit_name,
|
||||||
const char *ip_addr,
|
const char *ip_addr,
|
||||||
@@ -61,5 +71,7 @@ struct osmo_gsup_client *osmo_gsup_client_create(void *talloc_ctx,
|
|||||||
|
|
||||||
void osmo_gsup_client_destroy(struct osmo_gsup_client *gsupc);
|
void osmo_gsup_client_destroy(struct osmo_gsup_client *gsupc);
|
||||||
int osmo_gsup_client_send(struct osmo_gsup_client *gsupc, struct msgb *msg);
|
int osmo_gsup_client_send(struct osmo_gsup_client *gsupc, struct msgb *msg);
|
||||||
|
int osmo_gsup_client_enc_send(struct osmo_gsup_client *gsupc,
|
||||||
|
const struct osmo_gsup_message *gsup_msg);
|
||||||
struct msgb *osmo_gsup_client_msgb_alloc(void);
|
struct msgb *osmo_gsup_client_msgb_alloc(void);
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ exec_prefix=@exec_prefix@
|
|||||||
libdir=@libdir@
|
libdir=@libdir@
|
||||||
includedir=@includedir@
|
includedir=@includedir@
|
||||||
|
|
||||||
Name: Osmocom GSUP and OAP Client Library
|
Name: Osmocom GSUP Client Library
|
||||||
Description: C Utility Library
|
Description: C Utility Library
|
||||||
Version: @VERSION@
|
Version: @VERSION@
|
||||||
Libs: -L${libdir} @TALLOC_LIBS@ -losmogsm -losmocore
|
Libs: -L${libdir} -losmo-gsup-client
|
||||||
Cflags: -I${includedir}/
|
Cflags: -I${includedir}/
|
||||||
|
|
||||||
|
|||||||
31
sql/hlr.sql
31
sql/hlr.sql
@@ -1,4 +1,4 @@
|
|||||||
CREATE TABLE IF NOT EXISTS subscriber (
|
CREATE TABLE subscriber (
|
||||||
-- OsmoHLR's DB scheme is modelled roughly after TS 23.008 version 13.3.0
|
-- OsmoHLR's DB scheme is modelled roughly after TS 23.008 version 13.3.0
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
-- Chapter 2.1.1.1
|
-- Chapter 2.1.1.1
|
||||||
@@ -36,27 +36,31 @@ CREATE TABLE IF NOT EXISTS subscriber (
|
|||||||
-- Chapter 2.7.5
|
-- Chapter 2.7.5
|
||||||
ms_purged_cs BOOLEAN NOT NULL DEFAULT 0,
|
ms_purged_cs BOOLEAN NOT NULL DEFAULT 0,
|
||||||
-- Chapter 2.7.6
|
-- Chapter 2.7.6
|
||||||
ms_purged_ps BOOLEAN NOT NULL DEFAULT 0
|
ms_purged_ps BOOLEAN NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
-- Timestamp of last location update seen from subscriber
|
||||||
|
-- The value is a string which encodes a UTC timestamp in granularity of seconds.
|
||||||
|
last_lu_seen TIMESTAMP default NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS subscriber_apn (
|
CREATE TABLE subscriber_apn (
|
||||||
subscriber_id INTEGER, -- subscriber.id
|
subscriber_id INTEGER, -- subscriber.id
|
||||||
apn VARCHAR(256) NOT NULL
|
apn VARCHAR(256) NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS subscriber_multi_msisdn (
|
CREATE TABLE subscriber_multi_msisdn (
|
||||||
-- Chapter 2.1.3
|
-- Chapter 2.1.3
|
||||||
subscriber_id INTEGER, -- subscriber.id
|
subscriber_id INTEGER, -- subscriber.id
|
||||||
msisdn VARCHAR(15) NOT NULL
|
msisdn VARCHAR(15) NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS auc_2g (
|
CREATE TABLE auc_2g (
|
||||||
subscriber_id INTEGER PRIMARY KEY, -- subscriber.id
|
subscriber_id INTEGER PRIMARY KEY, -- subscriber.id
|
||||||
algo_id_2g INTEGER NOT NULL, -- enum osmo_auth_algo value
|
algo_id_2g INTEGER NOT NULL, -- enum osmo_auth_algo value
|
||||||
ki VARCHAR(32) NOT NULL -- hex string: subscriber's secret key (128bit)
|
ki VARCHAR(32) NOT NULL -- hex string: subscriber's secret key (128bit)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS auc_3g (
|
CREATE TABLE auc_3g (
|
||||||
subscriber_id INTEGER PRIMARY KEY, -- subscriber.id
|
subscriber_id INTEGER PRIMARY KEY, -- subscriber.id
|
||||||
algo_id_3g INTEGER NOT NULL, -- enum osmo_auth_algo value
|
algo_id_3g INTEGER NOT NULL, -- enum osmo_auth_algo value
|
||||||
k VARCHAR(32) NOT NULL, -- hex string: subscriber's secret key (128bit)
|
k VARCHAR(32) NOT NULL, -- hex string: subscriber's secret key (128bit)
|
||||||
@@ -66,4 +70,17 @@ CREATE TABLE IF NOT EXISTS auc_3g (
|
|||||||
ind_bitlen INTEGER NOT NULL DEFAULT 5 -- nr of index bits at lower SQN end
|
ind_bitlen INTEGER NOT NULL DEFAULT 5 -- nr of index bits at lower SQN end
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_subscr_imsi ON subscriber (imsi);
|
-- Optional: add subscriber entries to allow or disallow specific RATs (2G or 3G or ...).
|
||||||
|
-- If a subscriber has no entry, that means that all RATs are allowed (backwards compat).
|
||||||
|
CREATE TABLE subscriber_rat (
|
||||||
|
subscriber_id INTEGER, -- subscriber.id
|
||||||
|
rat TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu')) NOT NULL, -- Radio Access Technology, see enum ran_type
|
||||||
|
allowed BOOLEAN CHECK(allowed in (0, 1)) NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX idx_subscr_imsi ON subscriber (imsi);
|
||||||
|
CREATE UNIQUE INDEX idx_subscr_rat_flag ON subscriber_rat (subscriber_id, rat);
|
||||||
|
|
||||||
|
-- Set HLR database schema version number
|
||||||
|
-- Note: This constant is currently duplicated in src/db.c and must be kept in sync!
|
||||||
|
PRAGMA user_version = 2;
|
||||||
|
|||||||
8
sql/upgrade_v1_to_v2.sql
Normal file
8
sql/upgrade_v1_to_v2.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
CREATE TABLE subscriber_rat (
|
||||||
|
subscriber_id INTEGER, -- subscriber.id
|
||||||
|
rat TEXT CHECK(rat in ('GERAN-A', 'UTRAN-Iu')) NOT NULL, -- Radio Access Technology, see enum ran_type
|
||||||
|
allowed BOOLEAN NOT NULL DEFAULT 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
PRAGMA user_version = 2;
|
||||||
@@ -15,7 +15,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include \
|
|||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
populate_hlr_db.pl \
|
populate_hlr_db.pl \
|
||||||
db_bootstrap.sed \
|
db_sql2c.sed \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
BUILT_SOURCES = \
|
BUILT_SOURCES = \
|
||||||
@@ -114,11 +114,11 @@ osmo_euse_demo_LDADD = \
|
|||||||
|
|
||||||
BOOTSTRAP_SQL = $(top_srcdir)/sql/hlr.sql
|
BOOTSTRAP_SQL = $(top_srcdir)/sql/hlr.sql
|
||||||
|
|
||||||
db_bootstrap.h: $(BOOTSTRAP_SQL) $(srcdir)/db_bootstrap.sed
|
db_bootstrap.h: $(BOOTSTRAP_SQL) $(srcdir)/db_sql2c.sed
|
||||||
echo "/* DO NOT EDIT THIS FILE. It is generated from osmo-hlr.git/sql/hlr.sql */" > "$@"
|
echo "/* DO NOT EDIT THIS FILE. It is generated from files in osmo-hlr.git/sql/ */" > "$@"
|
||||||
echo "#pragma once" >> "$@"
|
echo "#pragma once" >> "$@"
|
||||||
echo "static const char *stmt_bootstrap_sql[] = {" >> "$@"
|
echo "static const char *stmt_bootstrap_sql[] = {" >> "$@"
|
||||||
cat "$(BOOTSTRAP_SQL)" \
|
cat "$(BOOTSTRAP_SQL)" \
|
||||||
| sed -f "$(srcdir)/db_bootstrap.sed" \
|
| sed -f "$(srcdir)/db_sql2c.sed" \
|
||||||
>> "$@"
|
>> "$@"
|
||||||
echo "};" >> "$@"
|
echo "};" >> "$@"
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ static void print_subscr_info(struct ctrl_cmd *cmd,
|
|||||||
struct hlr_subscriber *subscr)
|
struct hlr_subscriber *subscr)
|
||||||
{
|
{
|
||||||
ctrl_cmd_reply_printf(cmd,
|
ctrl_cmd_reply_printf(cmd,
|
||||||
"\nid\t%"PRIu64
|
"\nid\t%" PRIu64
|
||||||
FMT_S
|
FMT_S
|
||||||
FMT_S
|
FMT_S
|
||||||
FMT_BOOL
|
FMT_BOOL
|
||||||
@@ -189,7 +189,7 @@ static void print_subscr_info_aud3g(struct ctrl_cmd *cmd, struct osmo_sub_auth_d
|
|||||||
ctrl_cmd_reply_printf(cmd,
|
ctrl_cmd_reply_printf(cmd,
|
||||||
"\naud3g.%s\t%s"
|
"\naud3g.%s\t%s"
|
||||||
"\naud3g.ind_bitlen\t%u"
|
"\naud3g.ind_bitlen\t%u"
|
||||||
"\naud3g.sqn\t%"PRIu64
|
"\naud3g.sqn\t%" PRIu64
|
||||||
,
|
,
|
||||||
aud->u.umts.opc_is_op? "op" : "opc",
|
aud->u.umts.opc_is_op? "op" : "opc",
|
||||||
hexdump_buf(aud->u.umts.opc),
|
hexdump_buf(aud->u.umts.opc),
|
||||||
|
|||||||
223
src/db.c
223
src/db.c
@@ -27,6 +27,9 @@
|
|||||||
#include "db.h"
|
#include "db.h"
|
||||||
#include "db_bootstrap.h"
|
#include "db_bootstrap.h"
|
||||||
|
|
||||||
|
/* This constant is currently duplicated in sql/hlr.sql and must be kept in sync! */
|
||||||
|
#define CURRENT_SCHEMA_VERSION 2
|
||||||
|
|
||||||
#define SEL_COLUMNS \
|
#define SEL_COLUMNS \
|
||||||
"id," \
|
"id," \
|
||||||
"imsi," \
|
"imsi," \
|
||||||
@@ -40,7 +43,8 @@
|
|||||||
"nam_ps," \
|
"nam_ps," \
|
||||||
"lmsi," \
|
"lmsi," \
|
||||||
"ms_purged_cs," \
|
"ms_purged_cs," \
|
||||||
"ms_purged_ps"
|
"ms_purged_ps," \
|
||||||
|
"last_lu_seen"
|
||||||
|
|
||||||
static const char *stmt_sql[] = {
|
static const char *stmt_sql[] = {
|
||||||
[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",
|
[DB_STMT_SEL_BY_IMSI] = "SELECT " SEL_COLUMNS " FROM subscriber WHERE imsi = ?",
|
||||||
@@ -62,6 +66,7 @@ static const char *stmt_sql[] = {
|
|||||||
[DB_STMT_SUBSCR_CREATE] = "INSERT INTO subscriber (imsi) VALUES ($imsi)",
|
[DB_STMT_SUBSCR_CREATE] = "INSERT INTO subscriber (imsi) VALUES ($imsi)",
|
||||||
[DB_STMT_DEL_BY_ID] = "DELETE FROM subscriber WHERE id = $subscriber_id",
|
[DB_STMT_DEL_BY_ID] = "DELETE FROM subscriber WHERE id = $subscriber_id",
|
||||||
[DB_STMT_SET_MSISDN_BY_IMSI] = "UPDATE subscriber SET msisdn = $msisdn WHERE imsi = $imsi",
|
[DB_STMT_SET_MSISDN_BY_IMSI] = "UPDATE subscriber SET msisdn = $msisdn WHERE imsi = $imsi",
|
||||||
|
[DB_STMT_DELETE_MSISDN_BY_IMSI] = "UPDATE subscriber SET msisdn = NULL WHERE imsi = $imsi",
|
||||||
[DB_STMT_AUC_2G_INSERT] =
|
[DB_STMT_AUC_2G_INSERT] =
|
||||||
"INSERT INTO auc_2g (subscriber_id, algo_id_2g, ki)"
|
"INSERT INTO auc_2g (subscriber_id, algo_id_2g, ki)"
|
||||||
" VALUES($subscriber_id, $algo_id_2g, $ki)",
|
" VALUES($subscriber_id, $algo_id_2g, $ki)",
|
||||||
@@ -70,6 +75,13 @@ static const char *stmt_sql[] = {
|
|||||||
"INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc, ind_bitlen)"
|
"INSERT INTO auc_3g (subscriber_id, algo_id_3g, k, op, opc, ind_bitlen)"
|
||||||
" VALUES($subscriber_id, $algo_id_3g, $k, $op, $opc, $ind_bitlen)",
|
" VALUES($subscriber_id, $algo_id_3g, $k, $op, $opc, $ind_bitlen)",
|
||||||
[DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id",
|
[DB_STMT_AUC_3G_DELETE] = "DELETE FROM auc_3g WHERE subscriber_id = $subscriber_id",
|
||||||
|
[DB_STMT_SET_LAST_LU_SEEN] = "UPDATE subscriber SET last_lu_seen = datetime($val, 'unixepoch') WHERE id = $subscriber_id",
|
||||||
|
[DB_STMT_UPD_RAT_FLAG] = "INSERT OR REPLACE INTO subscriber_rat (subscriber_id, rat, allowed)"
|
||||||
|
" VALUES ($subscriber_id, $rat, $allowed)",
|
||||||
|
[DB_STMT_RAT_BY_ID] =
|
||||||
|
"SELECT rat, allowed"
|
||||||
|
" FROM subscriber_rat"
|
||||||
|
" WHERE subscriber_id = $subscriber_id",
|
||||||
};
|
};
|
||||||
|
|
||||||
static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
|
static void sql3_error_log_cb(void *arg, int err_code, const char *msg)
|
||||||
@@ -196,36 +208,170 @@ static int db_bootstrap(struct db_context *dbc)
|
|||||||
for (i = 0; i < ARRAY_SIZE(stmt_bootstrap_sql); i++) {
|
for (i = 0; i < ARRAY_SIZE(stmt_bootstrap_sql); i++) {
|
||||||
int rc;
|
int rc;
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
|
rc = sqlite3_prepare_v2(dbc->db, stmt_bootstrap_sql[i], -1, &stmt, NULL);
|
||||||
rc = sqlite3_prepare_v2(dbc->db, stmt_bootstrap_sql[i], -1,
|
|
||||||
&stmt, NULL);
|
|
||||||
if (rc != SQLITE_OK) {
|
if (rc != SQLITE_OK) {
|
||||||
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n",
|
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", stmt_bootstrap_sql[i]);
|
||||||
stmt_bootstrap_sql[i]);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* execute the statement */
|
|
||||||
rc = sqlite3_step(stmt);
|
rc = sqlite3_step(stmt);
|
||||||
db_remove_reset(stmt);
|
db_remove_reset(stmt);
|
||||||
sqlite3_finalize(stmt);
|
sqlite3_finalize(stmt);
|
||||||
if (rc != SQLITE_DONE) {
|
if (rc != SQLITE_DONE) {
|
||||||
LOGP(DDB, LOGL_ERROR, "Cannot bootstrap database: SQL error: (%d) %s,"
|
LOGP(DDB, LOGL_ERROR, "Cannot bootstrap database: SQL error: (%d) %s,"
|
||||||
" during stmt '%s'",
|
" during stmt '%s'",
|
||||||
rc, sqlite3_errmsg(dbc->db),
|
rc, sqlite3_errmsg(dbc->db), stmt_bootstrap_sql[i]);
|
||||||
stmt_bootstrap_sql[i]);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logging)
|
/* https://www.sqlite.org/fileformat2.html#storage_of_the_sql_database_schema */
|
||||||
|
static bool db_table_exists(struct db_context *dbc, const char *table_name)
|
||||||
|
{
|
||||||
|
const char *table_exists_sql = "SELECT name FROM sqlite_master WHERE type='table' AND name=?";
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = sqlite3_prepare_v2(dbc->db, table_exists_sql, -1, &stmt, NULL);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", table_exists_sql);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!db_bind_text(stmt, NULL, table_name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
db_remove_reset(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return (rc == SQLITE_ROW);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Indicate whether the database is initialized with tables for schema version 0.
|
||||||
|
* We only check for the 'subscriber' table here because Neels said so. */
|
||||||
|
static bool db_is_bootstrapped_v0(struct db_context *dbc)
|
||||||
|
{
|
||||||
|
if (!db_table_exists(dbc, "subscriber")) {
|
||||||
|
LOGP(DDB, LOGL_DEBUG, "Table 'subscriber' not found in database '%s'\n", dbc->fname);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
db_upgrade_v1(struct db_context *dbc)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
int rc;
|
||||||
|
const char *update_stmt_sql = "ALTER TABLE subscriber ADD COLUMN last_lu_seen TIMESTAMP default NULL";
|
||||||
|
const char *set_schema_version_sql = "PRAGMA user_version = 1";
|
||||||
|
|
||||||
|
rc = sqlite3_prepare_v2(dbc->db, update_stmt_sql, -1, &stmt, NULL);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", update_stmt_sql);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
db_remove_reset(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
if (rc != SQLITE_DONE) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version %d\n", 1);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = sqlite3_prepare_v2(dbc->db, set_schema_version_sql, -1, &stmt, NULL);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", set_schema_version_sql);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
if (rc != SQLITE_DONE)
|
||||||
|
LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version %d\n", 1);
|
||||||
|
|
||||||
|
db_remove_reset(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int db_upgrade_v2(struct db_context *dbc)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const char *stmts[] = {
|
||||||
|
"CREATE TABLE subscriber_rat",
|
||||||
|
"CREATE UNIQUE INDEX idx_subscr_rat_flag",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
sqlite3_stmt *stmt = NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(stmts); i++) {
|
||||||
|
int rc;
|
||||||
|
int j;
|
||||||
|
const char *stmt_sql = NULL;
|
||||||
|
|
||||||
|
if (stmts[i] != NULL) {
|
||||||
|
for (j = 0; j < ARRAY_SIZE(stmt_bootstrap_sql); j++) {
|
||||||
|
if (strstr(stmt_bootstrap_sql[j], stmts[i])) {
|
||||||
|
/* make sure we have a unique match, hence also not break; here */
|
||||||
|
OSMO_ASSERT(!stmt_sql);
|
||||||
|
stmt_sql = stmt_bootstrap_sql[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
stmt_sql = "PRAGMA user_version = 2";
|
||||||
|
OSMO_ASSERT(stmt_sql);
|
||||||
|
|
||||||
|
rc = sqlite3_prepare_v2(dbc->db, stmt_sql, -1, &stmt, NULL);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", stmt_sql);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
db_remove_reset(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
if (rc != SQLITE_DONE) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "Unable to update HLR database schema to version 2: '%s'\n",
|
||||||
|
stmt_sql);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SQLITE_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int db_get_user_version(struct db_context *dbc)
|
||||||
|
{
|
||||||
|
const char *user_version_sql = "PRAGMA user_version";
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
int version, rc;
|
||||||
|
|
||||||
|
rc = sqlite3_prepare_v2(dbc->db, user_version_sql, -1, &stmt, NULL);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "Unable to prepare SQL statement '%s'\n", user_version_sql);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
if (rc == SQLITE_ROW) {
|
||||||
|
version = sqlite3_column_int(stmt, 0);
|
||||||
|
} else {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "SQL statement '%s' failed: %d\n", user_version_sql, rc);
|
||||||
|
version = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
db_remove_reset(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logging, bool allow_upgrade)
|
||||||
{
|
{
|
||||||
struct db_context *dbc = talloc_zero(ctx, struct db_context);
|
struct db_context *dbc = talloc_zero(ctx, struct db_context);
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int rc;
|
int rc;
|
||||||
bool has_sqlite_config_sqllog = false;
|
bool has_sqlite_config_sqllog = false;
|
||||||
|
int version;
|
||||||
|
|
||||||
LOGP(DDB, LOGL_NOTICE, "using database: %s\n", fname);
|
LOGP(DDB, LOGL_NOTICE, "using database: %s\n", fname);
|
||||||
LOGP(DDB, LOGL_INFO, "Compiled against SQLite3 lib version %s\n", SQLITE_VERSION);
|
LOGP(DDB, LOGL_INFO, "Compiled against SQLite3 lib version %s\n", SQLITE_VERSION);
|
||||||
@@ -274,12 +420,69 @@ struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite_logg
|
|||||||
LOGP(DDB, LOGL_ERROR, "Unable to set Write-Ahead Logging: %s\n",
|
LOGP(DDB, LOGL_ERROR, "Unable to set Write-Ahead Logging: %s\n",
|
||||||
err_msg);
|
err_msg);
|
||||||
|
|
||||||
|
version = db_get_user_version(dbc);
|
||||||
|
if (version < 0) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "Unable to read user version number from database '%s'\n", dbc->fname);
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* An empty database will always report version zero. */
|
||||||
|
if (version == 0 && !db_is_bootstrapped_v0(dbc)) {
|
||||||
|
LOGP(DDB, LOGL_NOTICE, "Missing database tables detected; Bootstrapping database '%s'\n", dbc->fname);
|
||||||
rc = db_bootstrap(dbc);
|
rc = db_bootstrap(dbc);
|
||||||
if (rc != SQLITE_OK) {
|
if (rc != SQLITE_OK) {
|
||||||
LOGP(DDB, LOGL_ERROR, "Failed to bootstrap DB: (rc=%d) %s\n",
|
LOGP(DDB, LOGL_ERROR, "Failed to bootstrap DB: (rc=%d) %s\n",
|
||||||
rc, sqlite3_errmsg(dbc->db));
|
rc, sqlite3_errmsg(dbc->db));
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
version = CURRENT_SCHEMA_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGP(DDB, LOGL_NOTICE, "Database '%s' has HLR DB schema version %d\n", dbc->fname, version);
|
||||||
|
|
||||||
|
if (version < CURRENT_SCHEMA_VERSION && allow_upgrade) {
|
||||||
|
int orig_version = version;
|
||||||
|
|
||||||
|
switch (version) {
|
||||||
|
case 0:
|
||||||
|
rc = db_upgrade_v1(dbc);
|
||||||
|
if (rc != SQLITE_DONE) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "Failed to upgrade HLR DB schema to version 1: (rc=%d) %s\n",
|
||||||
|
rc, sqlite3_errmsg(dbc->db));
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
version = 1;
|
||||||
|
/* fall through */
|
||||||
|
case 1:
|
||||||
|
rc = db_upgrade_v2(dbc);
|
||||||
|
if (rc != SQLITE_DONE) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "Failed to upgrade HLR DB schema to version 2: (rc=%d) %s\n",
|
||||||
|
rc, sqlite3_errmsg(dbc->db));
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
version = 2;
|
||||||
|
/* fall through */
|
||||||
|
/* case N: ... */
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
LOGP(DDB, LOGL_NOTICE, "Database '%s' has been upgraded from HLR DB schema version %d to %d\n",
|
||||||
|
dbc->fname, orig_version, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version != CURRENT_SCHEMA_VERSION) {
|
||||||
|
if (version < CURRENT_SCHEMA_VERSION) {
|
||||||
|
LOGP(DDB, LOGL_NOTICE, "HLR DB schema version %d is outdated\n", version);
|
||||||
|
if (!allow_upgrade) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "Not upgrading HLR database from schema version %d to %d; "
|
||||||
|
"use the --db-upgrade option to allow HLR database upgrades\n",
|
||||||
|
version, CURRENT_SCHEMA_VERSION);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
LOGP(DDB, LOGL_ERROR, "HLR DB schema version %d is unknown\n", version);
|
||||||
|
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
|
||||||
/* prepare all SQL statements */
|
/* prepare all SQL statements */
|
||||||
for (i = 0; i < ARRAY_SIZE(dbc->stmt); i++) {
|
for (i = 0; i < ARRAY_SIZE(dbc->stmt); i++) {
|
||||||
|
|||||||
27
src/db.h
27
src/db.h
@@ -3,6 +3,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
#include <osmocom/gsm/gsm_utils.h>
|
||||||
|
|
||||||
struct hlr;
|
struct hlr;
|
||||||
|
|
||||||
enum stmt_idx {
|
enum stmt_idx {
|
||||||
@@ -20,10 +22,14 @@ enum stmt_idx {
|
|||||||
DB_STMT_SUBSCR_CREATE,
|
DB_STMT_SUBSCR_CREATE,
|
||||||
DB_STMT_DEL_BY_ID,
|
DB_STMT_DEL_BY_ID,
|
||||||
DB_STMT_SET_MSISDN_BY_IMSI,
|
DB_STMT_SET_MSISDN_BY_IMSI,
|
||||||
|
DB_STMT_DELETE_MSISDN_BY_IMSI,
|
||||||
DB_STMT_AUC_2G_INSERT,
|
DB_STMT_AUC_2G_INSERT,
|
||||||
DB_STMT_AUC_2G_DELETE,
|
DB_STMT_AUC_2G_DELETE,
|
||||||
DB_STMT_AUC_3G_INSERT,
|
DB_STMT_AUC_3G_INSERT,
|
||||||
DB_STMT_AUC_3G_DELETE,
|
DB_STMT_AUC_3G_DELETE,
|
||||||
|
DB_STMT_SET_LAST_LU_SEEN,
|
||||||
|
DB_STMT_UPD_RAT_FLAG,
|
||||||
|
DB_STMT_RAT_BY_ID,
|
||||||
_NUM_DB_STMT
|
_NUM_DB_STMT
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,7 +44,7 @@ bool db_bind_text(sqlite3_stmt *stmt, const char *param_name, const char *text);
|
|||||||
bool db_bind_int(sqlite3_stmt *stmt, const char *param_name, int nr);
|
bool db_bind_int(sqlite3_stmt *stmt, const char *param_name, int nr);
|
||||||
bool db_bind_int64(sqlite3_stmt *stmt, const char *param_name, int64_t nr);
|
bool db_bind_int64(sqlite3_stmt *stmt, const char *param_name, int64_t nr);
|
||||||
void db_close(struct db_context *dbc);
|
void db_close(struct db_context *dbc);
|
||||||
struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite3_logging);
|
struct db_context *db_open(void *ctx, const char *fname, bool enable_sqlite3_logging, bool allow_upgrades);
|
||||||
|
|
||||||
#include <osmocom/crypt/auth.h>
|
#include <osmocom/crypt/auth.h>
|
||||||
|
|
||||||
@@ -69,8 +75,8 @@ struct hlr_subscriber {
|
|||||||
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
|
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
|
||||||
char msisdn[GT_MAX_DIGITS+1];
|
char msisdn[GT_MAX_DIGITS+1];
|
||||||
/* imeisv? */
|
/* imeisv? */
|
||||||
char vlr_number[GT_MAX_DIGITS+1];
|
char vlr_number[32];
|
||||||
char sgsn_number[GT_MAX_DIGITS+1];
|
char sgsn_number[32];
|
||||||
char sgsn_address[GT_MAX_DIGITS+1];
|
char sgsn_address[GT_MAX_DIGITS+1];
|
||||||
/* ggsn number + address */
|
/* ggsn number + address */
|
||||||
/* gmlc number */
|
/* gmlc number */
|
||||||
@@ -82,8 +88,19 @@ struct hlr_subscriber {
|
|||||||
uint32_t lmsi;
|
uint32_t lmsi;
|
||||||
bool ms_purged_cs;
|
bool ms_purged_cs;
|
||||||
bool ms_purged_ps;
|
bool ms_purged_ps;
|
||||||
|
time_t last_lu_seen;
|
||||||
|
bool rat_types[OSMO_RAT_COUNT];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct hlr_subscriber hlr_subscriber_empty = {
|
||||||
|
.rat_types = { true, true, true },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A format string for use with strptime(3). This format string is
|
||||||
|
* used to parse the last_lu_seen column stored in the HLR database.
|
||||||
|
* See https://sqlite.org/lang_datefunc.html, function datetime(). */
|
||||||
|
#define DB_LAST_LU_SEEN_FMT "%Y-%m-%d %H:%M:%S"
|
||||||
|
|
||||||
/* Like struct osmo_sub_auth_data, but the keys are in hexdump representation.
|
/* Like struct osmo_sub_auth_data, but the keys are in hexdump representation.
|
||||||
* This is useful because SQLite requires them in hexdump format, and callers
|
* This is useful because SQLite requires them in hexdump format, and callers
|
||||||
* like the VTY and CTRL interface also have them available as hexdump to begin
|
* like the VTY and CTRL interface also have them available as hexdump to begin
|
||||||
@@ -130,6 +147,10 @@ int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
|
|||||||
|
|
||||||
int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
|
int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps);
|
||||||
|
|
||||||
|
int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed);
|
||||||
|
int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr);
|
||||||
|
int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed);
|
||||||
|
|
||||||
/*! Call sqlite3_column_text() and copy result to a char[].
|
/*! Call sqlite3_column_text() and copy result to a char[].
|
||||||
* \param[out] buf A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
|
* \param[out] buf A char[] used as sizeof() arg(!) and osmo_strlcpy() target.
|
||||||
* \param[in] stmt An sqlite3_stmt*.
|
* \param[in] stmt An sqlite3_stmt*.
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ int db_update_sqn(struct db_context *dbc, int64_t subscr_id, uint64_t new_sqn)
|
|||||||
/* execute the statement */
|
/* execute the statement */
|
||||||
rc = sqlite3_step(stmt);
|
rc = sqlite3_step(stmt);
|
||||||
if (rc != SQLITE_DONE) {
|
if (rc != SQLITE_DONE) {
|
||||||
LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%"PRId64
|
LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%" PRId64
|
||||||
": SQL error: (%d) %s\n",
|
": SQL error: (%d) %s\n",
|
||||||
subscr_id, rc, sqlite3_errmsg(dbc->db));
|
subscr_id, rc, sqlite3_errmsg(dbc->db));
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
@@ -59,11 +59,11 @@ int db_update_sqn(struct db_context *dbc, int64_t subscr_id, uint64_t new_sqn)
|
|||||||
/* verify execution result */
|
/* verify execution result */
|
||||||
rc = sqlite3_changes(dbc->db);
|
rc = sqlite3_changes(dbc->db);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%"PRId64
|
LOGP(DAUC, LOGL_ERROR, "Cannot update SQN for subscriber ID=%" PRId64
|
||||||
": no auc_3g entry for such subscriber\n", subscr_id);
|
": no auc_3g entry for such subscriber\n", subscr_id);
|
||||||
ret = -ENOENT;
|
ret = -ENOENT;
|
||||||
} else if (rc != 1) {
|
} else if (rc != 1) {
|
||||||
LOGP(DAUC, LOGL_ERROR, "Update SQN for subscriber ID=%"PRId64
|
LOGP(DAUC, LOGL_ERROR, "Update SQN for subscriber ID=%" PRId64
|
||||||
": SQL modified %d rows (expected 1)\n", subscr_id, rc);
|
": SQL modified %d rows (expected 1)\n", subscr_id, rc);
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
}
|
}
|
||||||
|
|||||||
210
src/db_hlr.c
210
src/db_hlr.c
@@ -17,9 +17,15 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _POSIX_C_SOURCE 200809L /* for strptime(3) */
|
||||||
|
/* These are needed as well due to the above _POSIX_C_SOURCE definition: */
|
||||||
|
#define _DEFAULT_SOURCE /* for struct timezone */
|
||||||
|
#define _XOPEN_SOURCE /* for clockid_t */
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include <osmocom/core/utils.h>
|
#include <osmocom/core/utils.h>
|
||||||
#include <osmocom/crypt/auth.h>
|
#include <osmocom/crypt/auth.h>
|
||||||
@@ -92,7 +98,7 @@ int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
|
|||||||
rc = sqlite3_step(stmt);
|
rc = sqlite3_step(stmt);
|
||||||
if (rc != SQLITE_DONE) {
|
if (rc != SQLITE_DONE) {
|
||||||
LOGP(DAUC, LOGL_ERROR,
|
LOGP(DAUC, LOGL_ERROR,
|
||||||
"Cannot delete subscriber ID=%"PRId64": SQL error: (%d) %s\n",
|
"Cannot delete subscriber ID=%" PRId64 ": SQL error: (%d) %s\n",
|
||||||
subscr_id, rc, sqlite3_errmsg(dbc->db));
|
subscr_id, rc, sqlite3_errmsg(dbc->db));
|
||||||
db_remove_reset(stmt);
|
db_remove_reset(stmt);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
@@ -101,11 +107,11 @@ int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
|
|||||||
/* verify execution result */
|
/* verify execution result */
|
||||||
rc = sqlite3_changes(dbc->db);
|
rc = sqlite3_changes(dbc->db);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
LOGP(DAUC, LOGL_ERROR, "Cannot delete: no such subscriber: ID=%"PRId64"\n",
|
LOGP(DAUC, LOGL_ERROR, "Cannot delete: no such subscriber: ID=%" PRId64 "\n",
|
||||||
subscr_id);
|
subscr_id);
|
||||||
ret = -ENOENT;
|
ret = -ENOENT;
|
||||||
} else if (rc != 1) {
|
} else if (rc != 1) {
|
||||||
LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%"PRId64
|
LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%" PRId64
|
||||||
": SQL modified %d rows (expected 1)\n", subscr_id, rc);
|
": SQL modified %d rows (expected 1)\n", subscr_id, rc);
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
}
|
}
|
||||||
@@ -135,7 +141,7 @@ int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
|
|||||||
|
|
||||||
/*! Set a subscriber's MSISDN in the HLR database.
|
/*! Set a subscriber's MSISDN in the HLR database.
|
||||||
* \param[in,out] dbc database context.
|
* \param[in,out] dbc database context.
|
||||||
* \param[in] imsi ASCII string of IMSI digits.
|
* \param[in] imsi ASCII string of IMSI digits, or NULL to remove the MSISDN.
|
||||||
* \param[in] msisdn ASCII string of MSISDN digits.
|
* \param[in] msisdn ASCII string of MSISDN digits.
|
||||||
* \returns 0 on success, -EINVAL in case of invalid MSISDN string, -EIO on
|
* \returns 0 on success, -EINVAL in case of invalid MSISDN string, -EIO on
|
||||||
* database failure, -ENOENT if no such subscriber exists.
|
* database failure, -ENOENT if no such subscriber exists.
|
||||||
@@ -146,19 +152,22 @@ int db_subscr_update_msisdn_by_imsi(struct db_context *dbc, const char *imsi,
|
|||||||
int rc;
|
int rc;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!osmo_msisdn_str_valid(msisdn)) {
|
if (msisdn && !osmo_msisdn_str_valid(msisdn)) {
|
||||||
LOGHLR(imsi, LOGL_ERROR,
|
LOGHLR(imsi, LOGL_ERROR,
|
||||||
"Cannot update subscriber: invalid MSISDN: '%s'\n",
|
"Cannot update subscriber: invalid MSISDN: '%s'\n",
|
||||||
msisdn);
|
msisdn);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SET_MSISDN_BY_IMSI];
|
sqlite3_stmt *stmt = dbc->stmt[
|
||||||
|
msisdn ? DB_STMT_SET_MSISDN_BY_IMSI : DB_STMT_DELETE_MSISDN_BY_IMSI];
|
||||||
|
|
||||||
if (!db_bind_text(stmt, "$imsi", imsi))
|
if (!db_bind_text(stmt, "$imsi", imsi))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
if (msisdn) {
|
||||||
if (!db_bind_text(stmt, "$msisdn", msisdn))
|
if (!db_bind_text(stmt, "$msisdn", msisdn))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
/* execute the statement */
|
/* execute the statement */
|
||||||
rc = sqlite3_step(stmt);
|
rc = sqlite3_step(stmt);
|
||||||
@@ -312,7 +321,7 @@ int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
|
|||||||
* empty, and no entry is not an error then.*/
|
* empty, and no entry is not an error then.*/
|
||||||
ret = -ENOENT;
|
ret = -ENOENT;
|
||||||
else if (rc != 1) {
|
else if (rc != 1) {
|
||||||
LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%"PRId64
|
LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%" PRId64
|
||||||
" from %s: SQL modified %d rows (expected 1)\n",
|
" from %s: SQL modified %d rows (expected 1)\n",
|
||||||
subscr_id, label, rc);
|
subscr_id, label, rc);
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
@@ -383,6 +392,8 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
|
|||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
const char *last_lu_seen_str;
|
||||||
|
struct tm tm;
|
||||||
|
|
||||||
/* execute the statement */
|
/* execute the statement */
|
||||||
rc = sqlite3_step(stmt);
|
rc = sqlite3_step(stmt);
|
||||||
@@ -398,7 +409,7 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
|
|||||||
if (!subscr)
|
if (!subscr)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
*subscr = (struct hlr_subscriber){};
|
*subscr = hlr_subscriber_empty;
|
||||||
|
|
||||||
/* obtain the various columns */
|
/* obtain the various columns */
|
||||||
subscr->id = sqlite3_column_int64(stmt, 0);
|
subscr->id = sqlite3_column_int64(stmt, 0);
|
||||||
@@ -415,10 +426,27 @@ static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscri
|
|||||||
subscr->lmsi = sqlite3_column_int(stmt, 10);
|
subscr->lmsi = sqlite3_column_int(stmt, 10);
|
||||||
subscr->ms_purged_cs = sqlite3_column_int(stmt, 11);
|
subscr->ms_purged_cs = sqlite3_column_int(stmt, 11);
|
||||||
subscr->ms_purged_ps = sqlite3_column_int(stmt, 12);
|
subscr->ms_purged_ps = sqlite3_column_int(stmt, 12);
|
||||||
|
last_lu_seen_str = (const char *)sqlite3_column_text(stmt, 13);
|
||||||
|
if (last_lu_seen_str && last_lu_seen_str[0] != '\0') {
|
||||||
|
if (strptime(last_lu_seen_str, DB_LAST_LU_SEEN_FMT, &tm) == NULL) {
|
||||||
|
LOGP(DAUC, LOGL_ERROR, "Cannot parse last LU timestamp '%s' of subscriber with IMSI='%s': %s\n",
|
||||||
|
last_lu_seen_str, subscr->imsi, strerror(errno));
|
||||||
|
} else {
|
||||||
|
subscr->last_lu_seen = mktime(&tm);
|
||||||
|
if (subscr->last_lu_seen == -1) {
|
||||||
|
LOGP(DAUC, LOGL_ERROR, "Cannot convert LU timestamp '%s' to time_t: %s\n",
|
||||||
|
last_lu_seen_str, strerror(errno));
|
||||||
|
subscr->last_lu_seen = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
db_remove_reset(stmt);
|
db_remove_reset(stmt);
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
db_subscr_get_rat_types(dbc, subscr);
|
||||||
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case 0:
|
case 0:
|
||||||
*err = NULL;
|
*err = NULL;
|
||||||
@@ -500,7 +528,7 @@ int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
|
|||||||
|
|
||||||
rc = db_sel(dbc, stmt, subscr, &err);
|
rc = db_sel(dbc, stmt, subscr, &err);
|
||||||
if (rc)
|
if (rc)
|
||||||
LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: ID=%"PRId64": %s\n",
|
LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: ID=%" PRId64 ": %s\n",
|
||||||
id, err);
|
id, err);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -574,6 +602,7 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
|
|||||||
{
|
{
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
int rc, ret = 0;
|
int rc, ret = 0;
|
||||||
|
struct timespec localtime;
|
||||||
|
|
||||||
stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
|
stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
|
||||||
: DB_STMT_UPD_VLR_BY_ID];
|
: DB_STMT_UPD_VLR_BY_ID];
|
||||||
@@ -587,7 +616,7 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
|
|||||||
/* execute the statement */
|
/* execute the statement */
|
||||||
rc = sqlite3_step(stmt);
|
rc = sqlite3_step(stmt);
|
||||||
if (rc != SQLITE_DONE) {
|
if (rc != SQLITE_DONE) {
|
||||||
LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%"PRId64": SQL Error: %s\n",
|
LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%" PRId64 ": SQL Error: %s\n",
|
||||||
is_ps? "SGSN" : "VLR", subscr_id, sqlite3_errmsg(dbc->db));
|
is_ps? "SGSN" : "VLR", subscr_id, sqlite3_errmsg(dbc->db));
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto out;
|
goto out;
|
||||||
@@ -596,17 +625,58 @@ int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
|
|||||||
/* verify execution result */
|
/* verify execution result */
|
||||||
rc = sqlite3_changes(dbc->db);
|
rc = sqlite3_changes(dbc->db);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
LOGP(DAUC, LOGL_ERROR, "Cannot update %s number for subscriber ID=%"PRId64
|
LOGP(DAUC, LOGL_ERROR, "Cannot update %s number for subscriber ID=%" PRId64
|
||||||
": no such subscriber\n",
|
": no such subscriber\n",
|
||||||
is_ps? "SGSN" : "VLR", subscr_id);
|
is_ps? "SGSN" : "VLR", subscr_id);
|
||||||
ret = -ENOENT;
|
ret = -ENOENT;
|
||||||
|
goto out;
|
||||||
} else if (rc != 1) {
|
} else if (rc != 1) {
|
||||||
LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%"PRId64
|
LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%" PRId64
|
||||||
": SQL modified %d rows (expected 1)\n",
|
": SQL modified %d rows (expected 1)\n",
|
||||||
is_ps? "SGSN" : "VLR", subscr_id, rc);
|
is_ps? "SGSN" : "VLR", subscr_id, rc);
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db_remove_reset(stmt);
|
||||||
|
|
||||||
|
if (osmo_clock_gettime(CLOCK_REALTIME, &localtime) != 0) {
|
||||||
|
LOGP(DAUC, LOGL_ERROR, "Cannot get the current time: (%d) %s\n", errno, strerror(errno));
|
||||||
|
ret = -errno;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt = dbc->stmt[DB_STMT_SET_LAST_LU_SEEN];
|
||||||
|
|
||||||
|
if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
|
||||||
|
return -EIO;
|
||||||
|
/* The timestamp will be converted to UTC by SQLite. */
|
||||||
|
if (!db_bind_int64(stmt, "$val", (int64_t)localtime.tv_sec)) {
|
||||||
|
ret = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
if (rc != SQLITE_DONE) {
|
||||||
|
LOGP(DAUC, LOGL_ERROR,
|
||||||
|
"Cannot update LU timestamp for subscriber ID=%" PRId64 ": SQL error: (%d) %s\n",
|
||||||
|
subscr_id, rc, sqlite3_errmsg(dbc->db));
|
||||||
|
ret = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify execution result */
|
||||||
|
rc = sqlite3_changes(dbc->db);
|
||||||
|
if (!rc) {
|
||||||
|
LOGP(DAUC, LOGL_ERROR, "Cannot update LU timestamp for subscriber ID=%" PRId64
|
||||||
|
": no such subscriber\n", subscr_id);
|
||||||
|
ret = -ENOENT;
|
||||||
|
goto out;
|
||||||
|
} else if (rc != 1) {
|
||||||
|
LOGP(DAUC, LOGL_ERROR, "Update LU timestamp for subscriber ID=%" PRId64
|
||||||
|
": SQL modified %d rows (expected 1)\n", subscr_id, rc);
|
||||||
|
ret = -EIO;
|
||||||
|
}
|
||||||
out:
|
out:
|
||||||
db_remove_reset(stmt);
|
db_remove_reset(stmt);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -715,3 +785,119 @@ int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val,
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
int ret = 0;
|
||||||
|
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_RAT_FLAG];
|
||||||
|
|
||||||
|
if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
|
||||||
|
if (!db_bind_text(stmt, "$rat", osmo_rat_type_name(rat)))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (!db_bind_int(stmt, "$allowed", allowed ? 1 : 0))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
/* execute the statement */
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
if (rc != SQLITE_DONE) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "%s %s: SQL error: %s\n",
|
||||||
|
allowed ? "enable" : "disable", osmo_rat_type_name(rat),
|
||||||
|
sqlite3_errmsg(dbc->db));
|
||||||
|
ret = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify execution result */
|
||||||
|
rc = sqlite3_changes(dbc->db);
|
||||||
|
if (!rc) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "Cannot %s %s: no such subscriber: ID=%" PRIu64 "\n",
|
||||||
|
allowed ? "enable" : "disable", osmo_rat_type_name(rat),
|
||||||
|
subscr_id);
|
||||||
|
ret = -ENOENT;
|
||||||
|
goto out;
|
||||||
|
} else if (rc != 1) {
|
||||||
|
LOGP(DDB, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
|
||||||
|
allowed ? "enable" : "disable", osmo_rat_type_name(rat),
|
||||||
|
rc);
|
||||||
|
ret = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
db_remove_reset(stmt);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
int ret = 0;
|
||||||
|
int i;
|
||||||
|
sqlite3_stmt *stmt = dbc->stmt[DB_STMT_RAT_BY_ID];
|
||||||
|
|
||||||
|
if (!db_bind_int64(stmt, "$subscriber_id", subscr->id))
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
for (i = 0; i < OSMO_RAT_COUNT; i++)
|
||||||
|
subscr->rat_types[i] = true;
|
||||||
|
|
||||||
|
/* execute the statement */
|
||||||
|
while (1) {
|
||||||
|
enum osmo_rat_type rat;
|
||||||
|
bool allowed;
|
||||||
|
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
|
||||||
|
if (rc == SQLITE_DONE)
|
||||||
|
break;
|
||||||
|
if (rc != SQLITE_ROW)
|
||||||
|
return -rc;
|
||||||
|
|
||||||
|
rc = get_string_value(osmo_rat_type_names, (const char*)sqlite3_column_text(stmt, 0));
|
||||||
|
if (rc == -EINVAL) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (rc <= 0 || rc >= OSMO_RAT_COUNT) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
rat = rc;
|
||||||
|
|
||||||
|
allowed = sqlite3_column_int(stmt, 1);
|
||||||
|
|
||||||
|
subscr->rat_types[rat] = allowed;
|
||||||
|
LOGP(DAUC, LOGL_DEBUG, "db: imsi='%s' %s %s\n",
|
||||||
|
subscr->imsi, osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
db_remove_reset(stmt);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
|
||||||
|
|
||||||
|
db_subscr_get_rat_types(hlr->dbc, subscr);
|
||||||
|
|
||||||
|
if (subscr->rat_types[rat] == allowed) {
|
||||||
|
LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n",
|
||||||
|
allowed ? "enable" : "disable", osmo_rat_type_name(rat));
|
||||||
|
return -ENOEXEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = db_subscr_set_rat_type_flag(hlr->dbc, subscr->id, rat, allowed);
|
||||||
|
if (rc)
|
||||||
|
return rc > 0? -rc : rc;
|
||||||
|
|
||||||
|
/* FIXME: If we're disabling, send message to VLR to detach subscriber */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Input to this is sql/hlr.sql.
|
# Input to this are sql/*.sql files.
|
||||||
#
|
#
|
||||||
# We want each SQL statement line wrapped in "...\n", and each end (";") to
|
# We want each SQL statement line wrapped in "...\n", and each end (";") to
|
||||||
# become a comma:
|
# become a comma:
|
||||||
@@ -7,16 +7,16 @@
|
|||||||
* Author: Neels Hofmeyr
|
* Author: Neels Hofmeyr
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU Affero General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -170,16 +170,12 @@ static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg)
|
|||||||
struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg);
|
struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg);
|
||||||
struct osmo_gsup_client *gsupc = (struct osmo_gsup_client *)link->data;
|
struct osmo_gsup_client *gsupc = (struct osmo_gsup_client *)link->data;
|
||||||
int rc;
|
int rc;
|
||||||
struct ipaccess_unit ipa_dev = {
|
|
||||||
/* see gsup_client_create() on const vs non-const */
|
|
||||||
.unit_name = (char*)gsupc->unit_name,
|
|
||||||
};
|
|
||||||
|
|
||||||
OSMO_ASSERT(ipa_dev.unit_name);
|
OSMO_ASSERT(gsupc->unit_name);
|
||||||
|
|
||||||
msg->l2h = &hh->data[0];
|
msg->l2h = &hh->data[0];
|
||||||
|
|
||||||
rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg);
|
rc = ipaccess_bts_handle_ccm(link, gsupc->ipa_dev, msg);
|
||||||
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
LOGP(DLGSUP, LOGL_NOTICE,
|
LOGP(DLGSUP, LOGL_NOTICE,
|
||||||
@@ -262,8 +258,21 @@ static void start_test_procedure(struct osmo_gsup_client *gsupc)
|
|||||||
gsup_client_send_ping(gsupc);
|
gsup_client_send_ping(gsupc);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct osmo_gsup_client *osmo_gsup_client_create(void *talloc_ctx,
|
/*!
|
||||||
const char *unit_name,
|
* Create a gsup client connecting to the specified IP address and TCP port.
|
||||||
|
* Use the provided ipaccess unit as the client-side identifier; ipa_dev should
|
||||||
|
* be allocated in talloc_ctx talloc_ctx as well.
|
||||||
|
* \param[in] talloc_ctx talloc context.
|
||||||
|
* \param[in] ipa_dev IP access unit which contains client identification information; must be allocated
|
||||||
|
* in talloc_ctx as well to ensure it lives throughout the lifetime of the connection.
|
||||||
|
* \param[in] ip_addr GSUP server IP address.
|
||||||
|
* \param[in] tcp_port GSUP server TCP port.
|
||||||
|
* \param[in] read_cb callback for reading from the GSUP connection.
|
||||||
|
* \param[in] oapc_config OPA client configuration.
|
||||||
|
* \returns a GSUP client connection or NULL on failure.
|
||||||
|
*/
|
||||||
|
struct osmo_gsup_client *osmo_gsup_client_create2(void *talloc_ctx,
|
||||||
|
struct ipaccess_unit *ipa_dev,
|
||||||
const char *ip_addr,
|
const char *ip_addr,
|
||||||
unsigned int tcp_port,
|
unsigned int tcp_port,
|
||||||
osmo_gsup_client_read_cb_t read_cb,
|
osmo_gsup_client_read_cb_t read_cb,
|
||||||
@@ -274,12 +283,8 @@ struct osmo_gsup_client *osmo_gsup_client_create(void *talloc_ctx,
|
|||||||
|
|
||||||
gsupc = talloc_zero(talloc_ctx, struct osmo_gsup_client);
|
gsupc = talloc_zero(talloc_ctx, struct osmo_gsup_client);
|
||||||
OSMO_ASSERT(gsupc);
|
OSMO_ASSERT(gsupc);
|
||||||
|
gsupc->unit_name = (const char *)ipa_dev->unit_name; /* API backwards compat */
|
||||||
/* struct ipaccess_unit has a non-const unit_name, so let's copy to be
|
gsupc->ipa_dev = ipa_dev;
|
||||||
* able to have a non-const unit_name here as well. To not taint the
|
|
||||||
* public gsup_client API, let's store it in a const char* anyway. */
|
|
||||||
gsupc->unit_name = talloc_strdup(gsupc, unit_name);
|
|
||||||
OSMO_ASSERT(gsupc->unit_name);
|
|
||||||
|
|
||||||
/* a NULL oapc_config will mark oap_state disabled. */
|
/* a NULL oapc_config will mark oap_state disabled. */
|
||||||
rc = osmo_oap_client_init(oapc_config, &gsupc->oap_state);
|
rc = osmo_oap_client_init(oapc_config, &gsupc->oap_state);
|
||||||
@@ -313,6 +318,22 @@ failed:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like osmo_gsup_client_create2() except it expects a unit name instead
|
||||||
|
* of a full-blown ipacess_unit as the client-side identifier.
|
||||||
|
*/
|
||||||
|
struct osmo_gsup_client *osmo_gsup_client_create(void *talloc_ctx,
|
||||||
|
const char *unit_name,
|
||||||
|
const char *ip_addr,
|
||||||
|
unsigned int tcp_port,
|
||||||
|
osmo_gsup_client_read_cb_t read_cb,
|
||||||
|
struct osmo_oap_client_config *oapc_config)
|
||||||
|
{
|
||||||
|
struct ipaccess_unit *ipa_dev = talloc_zero(talloc_ctx, struct ipaccess_unit);
|
||||||
|
ipa_dev->unit_name = talloc_strdup(ipa_dev, unit_name);
|
||||||
|
return osmo_gsup_client_create2(talloc_ctx, ipa_dev, ip_addr, tcp_port, read_cb, oapc_config);
|
||||||
|
}
|
||||||
|
|
||||||
void osmo_gsup_client_destroy(struct osmo_gsup_client *gsupc)
|
void osmo_gsup_client_destroy(struct osmo_gsup_client *gsupc)
|
||||||
{
|
{
|
||||||
osmo_timer_del(&gsupc->connect_timer);
|
osmo_timer_del(&gsupc->connect_timer);
|
||||||
@@ -339,6 +360,42 @@ int osmo_gsup_client_send(struct osmo_gsup_client *gsupc, struct msgb *msg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Encode and send a GSUP message.
|
||||||
|
* \param[in] gsupc GSUP client.
|
||||||
|
* \param[in] gsup_msg GSUP message to be sent.
|
||||||
|
* \returns 0 in case of success, negative on error.
|
||||||
|
*/
|
||||||
|
int osmo_gsup_client_enc_send(struct osmo_gsup_client *gsupc,
|
||||||
|
const struct osmo_gsup_message *gsup_msg)
|
||||||
|
{
|
||||||
|
struct msgb *gsup_msgb;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
gsup_msgb = osmo_gsup_client_msgb_alloc();
|
||||||
|
if (!gsup_msgb) {
|
||||||
|
LOGP(DLGSUP, LOGL_ERROR, "Couldn't allocate GSUP message\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = osmo_gsup_encode(gsup_msgb, gsup_msg);
|
||||||
|
if (rc) {
|
||||||
|
LOGP(DLGSUP, LOGL_ERROR, "Couldn't encode GSUP message\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = osmo_gsup_client_send(gsupc, gsup_msgb);
|
||||||
|
if (rc) {
|
||||||
|
LOGP(DLGSUP, LOGL_ERROR, "Couldn't send GSUP message\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
talloc_free(gsup_msgb);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
struct msgb *osmo_gsup_client_msgb_alloc(void)
|
struct msgb *osmo_gsup_client_msgb_alloc(void)
|
||||||
{
|
{
|
||||||
return msgb_alloc_headroom(4000, 64, __func__);
|
return msgb_alloc_headroom(4000, 64, __func__);
|
||||||
|
|||||||
133
src/hlr.c
133
src/hlr.c
@@ -54,16 +54,15 @@ static int quit = 0;
|
|||||||
void
|
void
|
||||||
osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
|
osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
|
||||||
{
|
{
|
||||||
/* FIXME: the below code can only be re-enabled after we make sure that an ISD
|
|
||||||
* is only sent tot the currently serving VLR and/or SGSN (if there are any).
|
|
||||||
* We cannot blindly flood the entire PLMN with this, as it would create subscriber
|
|
||||||
* state in every VLR/SGSN out there, even those that have never seen the subscriber.
|
|
||||||
* See https://osmocom.org/issues/3154 for details. */
|
|
||||||
#if 0
|
|
||||||
struct osmo_gsup_conn *co;
|
struct osmo_gsup_conn *co;
|
||||||
|
|
||||||
if (g_hlr->gs == NULL)
|
if (g_hlr->gs == NULL) {
|
||||||
|
LOGP(DLGSUP, LOGL_DEBUG,
|
||||||
|
"IMSI %s: NOT Notifying peers of subscriber data change,"
|
||||||
|
" there is no GSUP server\n",
|
||||||
|
subscr->imsi);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
llist_for_each_entry(co, &g_hlr->gs->clients, list) {
|
llist_for_each_entry(co, &g_hlr->gs->clients, list) {
|
||||||
struct osmo_gsup_message gsup = { };
|
struct osmo_gsup_message gsup = { };
|
||||||
@@ -72,20 +71,50 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
|
|||||||
struct msgb *msg_out;
|
struct msgb *msg_out;
|
||||||
uint8_t *peer;
|
uint8_t *peer;
|
||||||
int peer_len;
|
int peer_len;
|
||||||
|
size_t peer_strlen;
|
||||||
|
const char *peer_compare;
|
||||||
enum osmo_gsup_cn_domain cn_domain;
|
enum osmo_gsup_cn_domain cn_domain;
|
||||||
|
|
||||||
if (co->supports_ps)
|
if (co->supports_ps) {
|
||||||
cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
|
cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
|
||||||
else if (co->supports_cs)
|
peer_compare = subscr->sgsn_number;
|
||||||
|
} else if (co->supports_cs) {
|
||||||
cn_domain = OSMO_GSUP_CN_DOMAIN_CS;
|
cn_domain = OSMO_GSUP_CN_DOMAIN_CS;
|
||||||
else {
|
peer_compare = subscr->vlr_number;
|
||||||
/* We have not yet received a location update from this subscriber .*/
|
} else {
|
||||||
|
/* We have not yet received a location update from this GSUP client.*/
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peer_len = osmo_gsup_conn_ccm_get(co, &peer, IPAC_IDTAG_SERNR);
|
||||||
|
if (peer_len < 0) {
|
||||||
|
LOGP(DLGSUP, LOGL_ERROR,
|
||||||
|
"IMSI='%s': cannot get peer name for connection %s:%u\n", subscr->imsi,
|
||||||
|
co && co->conn && co->conn->server? co->conn->server->addr : "unset",
|
||||||
|
co && co->conn && co->conn->server? co->conn->server->port : 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
peer_strlen = strnlen((const char*)peer, peer_len);
|
||||||
|
if (strlen(peer_compare) != peer_strlen || strncmp(peer_compare, (const char *)peer, peer_len)) {
|
||||||
|
/* Mismatch. The subscriber is not subscribed with this GSUP client. */
|
||||||
|
/* I hope peer is always nul terminated... */
|
||||||
|
if (peer_strlen < peer_len)
|
||||||
|
LOGP(DLGSUP, LOGL_DEBUG,
|
||||||
|
"IMSI %s: subscriber change: skipping %s peer %s\n",
|
||||||
|
subscr->imsi, cn_domain == OSMO_GSUP_CN_DOMAIN_PS ? "PS" : "CS",
|
||||||
|
osmo_quote_str((char*)peer, -1));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGP(DLGSUP, LOGL_DEBUG,
|
||||||
|
"IMSI %s: subscriber change: notifying %s peer %s\n",
|
||||||
|
subscr->imsi, cn_domain == OSMO_GSUP_CN_DOMAIN_PS ? "PS" : "CS",
|
||||||
|
osmo_quote_str(peer_compare, -1));
|
||||||
|
|
||||||
if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi, subscr->msisdn, msisdn_enc,
|
if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi, subscr->msisdn, msisdn_enc,
|
||||||
sizeof(msisdn_enc), apn, sizeof(apn), cn_domain) != 0) {
|
sizeof(msisdn_enc), apn, sizeof(apn), cn_domain) != 0) {
|
||||||
LOGP(DMAIN, LOGL_ERROR,
|
LOGP(DLGSUP, LOGL_ERROR,
|
||||||
"IMSI='%s': Cannot notify GSUP client; could not create gsup message "
|
"IMSI='%s': Cannot notify GSUP client; could not create gsup message "
|
||||||
"for %s:%u\n", subscr->imsi,
|
"for %s:%u\n", subscr->imsi,
|
||||||
co && co->conn && co->conn->server? co->conn->server->addr : "unset",
|
co && co->conn && co->conn->server? co->conn->server->addr : "unset",
|
||||||
@@ -96,7 +125,7 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
|
|||||||
/* Send ISD to MSC/SGSN */
|
/* Send ISD to MSC/SGSN */
|
||||||
msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP ISD UPDATE");
|
msg_out = msgb_alloc_headroom(1024+16, 16, "GSUP ISD UPDATE");
|
||||||
if (msg_out == NULL) {
|
if (msg_out == NULL) {
|
||||||
LOGP(DMAIN, LOGL_ERROR,
|
LOGP(DLGSUP, LOGL_ERROR,
|
||||||
"IMSI='%s': Cannot notify GSUP client; could not allocate msg buffer "
|
"IMSI='%s': Cannot notify GSUP client; could not allocate msg buffer "
|
||||||
"for %s:%u\n", subscr->imsi,
|
"for %s:%u\n", subscr->imsi,
|
||||||
co && co->conn && co->conn->server? co->conn->server->addr : "unset",
|
co && co->conn && co->conn->server? co->conn->server->addr : "unset",
|
||||||
@@ -105,15 +134,6 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
|
|||||||
}
|
}
|
||||||
osmo_gsup_encode(msg_out, &gsup);
|
osmo_gsup_encode(msg_out, &gsup);
|
||||||
|
|
||||||
peer_len = osmo_gsup_conn_ccm_get(co, &peer, IPAC_IDTAG_SERNR);
|
|
||||||
if (peer_len < 0) {
|
|
||||||
LOGP(DMAIN, LOGL_ERROR,
|
|
||||||
"IMSI='%s': cannot get peer name for connection %s:%u\n", subscr->imsi,
|
|
||||||
co && co->conn && co->conn->server? co->conn->server->addr : "unset",
|
|
||||||
co && co->conn && co->conn->server? co->conn->server->port : 0);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (osmo_gsup_addr_send(g_hlr->gs, peer, peer_len, msg_out) < 0) {
|
if (osmo_gsup_addr_send(g_hlr->gs, peer, peer_len, msg_out) < 0) {
|
||||||
LOGP(DMAIN, LOGL_ERROR,
|
LOGP(DMAIN, LOGL_ERROR,
|
||||||
"IMSI='%s': Cannot notify GSUP client; send operation failed "
|
"IMSI='%s': Cannot notify GSUP client; send operation failed "
|
||||||
@@ -123,7 +143,6 @@ osmo_hlr_subscriber_update_notify(struct hlr_subscriber *subscr)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
@@ -242,26 +261,33 @@ void lu_op_rx_gsup(struct lu_operation *luop,
|
|||||||
static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
|
static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
|
||||||
const struct osmo_gsup_message *gsup)
|
const struct osmo_gsup_message *gsup)
|
||||||
{
|
{
|
||||||
|
struct hlr_subscriber *subscr;
|
||||||
struct lu_operation *luop = lu_op_alloc_conn(conn);
|
struct lu_operation *luop = lu_op_alloc_conn(conn);
|
||||||
|
int i;
|
||||||
|
bool allowed;
|
||||||
|
|
||||||
if (!luop) {
|
if (!luop) {
|
||||||
LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
|
LOGP(DMAIN, LOGL_ERROR, "LU REQ from conn without addr?\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subscr = &luop->subscr;
|
||||||
|
|
||||||
lu_op_statechg(luop, LU_S_LU_RECEIVED);
|
lu_op_statechg(luop, LU_S_LU_RECEIVED);
|
||||||
|
|
||||||
if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_CS)
|
switch (gsup->cn_domain) {
|
||||||
|
case OSMO_GSUP_CN_DOMAIN_CS:
|
||||||
conn->supports_cs = true;
|
conn->supports_cs = true;
|
||||||
if (gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_PS) {
|
break;
|
||||||
conn->supports_ps = true;
|
default:
|
||||||
luop->is_ps = true;
|
|
||||||
} else {
|
|
||||||
/* The client didn't send a CN_DOMAIN IE; assume packet-switched in
|
/* The client didn't send a CN_DOMAIN IE; assume packet-switched in
|
||||||
* accordance with the GSUP spec in osmo-hlr's user manual (section
|
* accordance with the GSUP spec in osmo-hlr's user manual (section
|
||||||
* 11.6.15 "CN Domain" says "if no CN Domain IE is present within
|
* 11.6.15 "CN Domain" says "if no CN Domain IE is present within
|
||||||
* a request, the PS Domain is assumed." */
|
* a request, the PS Domain is assumed." */
|
||||||
|
case OSMO_GSUP_CN_DOMAIN_PS:
|
||||||
conn->supports_ps = true;
|
conn->supports_ps = true;
|
||||||
luop->is_ps = true;
|
luop->is_ps = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
llist_add(&luop->list, &g_lu_ops);
|
llist_add(&luop->list, &g_lu_ops);
|
||||||
|
|
||||||
@@ -285,6 +311,34 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if any available RAT type is allowed. See 3GPP TS 29.010 3.2 'Routeing area updating' and 3.8 'Location
|
||||||
|
* update' for the "No Suitable cells in location area" error code. */
|
||||||
|
allowed = false;
|
||||||
|
LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' on %s sent RAT types: %zu\n", subscr->imsi,
|
||||||
|
gsup->cn_domain == OSMO_GSUP_CN_DOMAIN_CS ? "CS" : "PS", gsup->rat_types_len);
|
||||||
|
for (i = 0; i < gsup->rat_types_len; i++) {
|
||||||
|
enum osmo_rat_type rat = gsup->rat_types[i];
|
||||||
|
if (rat <= 0 || rat >= OSMO_RAT_COUNT) {
|
||||||
|
lu_op_tx_error(luop, GMM_CAUSE_COND_IE_ERR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (luop->subscr.rat_types[rat]) {
|
||||||
|
allowed = true;
|
||||||
|
LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' allowed on %s\n",
|
||||||
|
subscr->imsi, osmo_rat_type_name(rat));
|
||||||
|
} else {
|
||||||
|
LOGP(DAUC, LOGL_DEBUG, "LU: IMSI='%s' not allowed on %s\n",
|
||||||
|
subscr->imsi, osmo_rat_type_name(rat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!allowed && gsup->rat_types_len > 0) {
|
||||||
|
LOGP(DAUC, LOGL_DEBUG, "ISMI='%s' not allowed on %s%s\n",
|
||||||
|
subscr->imsi, osmo_rat_type_name(gsup->rat_types[0]),
|
||||||
|
gsup->rat_types_len > 1 ? " (nor on the other available RAT types)" : "");
|
||||||
|
lu_op_tx_error(luop, GMM_CAUSE_NO_SUIT_CELL_IN_LA);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO: Set subscriber tracing = deactive in VLR/SGSN */
|
/* TODO: Set subscriber tracing = deactive in VLR/SGSN */
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
@@ -297,6 +351,15 @@ static int rx_upd_loc_req(struct osmo_gsup_conn *conn,
|
|||||||
lu_op_tx_cancel_old(luop);
|
lu_op_tx_cancel_old(luop);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Store the VLR / SGSN number with the subscriber, so we know where it was last seen. */
|
||||||
|
LOGP(DAUC, LOGL_DEBUG, "IMSI='%s': storing %s = %s\n",
|
||||||
|
subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number",
|
||||||
|
osmo_quote_str((const char*)luop->peer, -1));
|
||||||
|
if (db_subscr_lu(g_hlr->dbc, subscr->id, (const char *)luop->peer, luop->is_ps))
|
||||||
|
LOGP(DAUC, LOGL_ERROR, "IMSI='%s': Cannot update %s in the database\n",
|
||||||
|
subscr->imsi, luop->is_ps ? "SGSN number" : "VLR number");
|
||||||
|
|
||||||
{
|
{
|
||||||
/* TODO: Subscriber allowed to roam in PLMN? */
|
/* TODO: Subscriber allowed to roam in PLMN? */
|
||||||
/* TODO: Update RoutingInfo */
|
/* TODO: Update RoutingInfo */
|
||||||
@@ -451,6 +514,7 @@ static void print_help()
|
|||||||
printf(" -s --disable-color Do not print ANSI colors in the log\n");
|
printf(" -s --disable-color Do not print ANSI colors in the log\n");
|
||||||
printf(" -T --timestamp Prefix every log line with a timestamp.\n");
|
printf(" -T --timestamp Prefix every log line with a timestamp.\n");
|
||||||
printf(" -e --log-level number Set a global loglevel.\n");
|
printf(" -e --log-level number Set a global loglevel.\n");
|
||||||
|
printf(" -U --db-upgrade Allow HLR database schema upgrades.\n");
|
||||||
printf(" -V --version Print the version of OsmoHLR.\n");
|
printf(" -V --version Print the version of OsmoHLR.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,10 +522,12 @@ static struct {
|
|||||||
const char *config_file;
|
const char *config_file;
|
||||||
const char *db_file;
|
const char *db_file;
|
||||||
bool daemonize;
|
bool daemonize;
|
||||||
|
bool db_upgrade;
|
||||||
} cmdline_opts = {
|
} cmdline_opts = {
|
||||||
.config_file = "osmo-hlr.cfg",
|
.config_file = "osmo-hlr.cfg",
|
||||||
.db_file = "hlr.db",
|
.db_file = "hlr.db",
|
||||||
.daemonize = false,
|
.daemonize = false,
|
||||||
|
.db_upgrade = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void handle_options(int argc, char **argv)
|
static void handle_options(int argc, char **argv)
|
||||||
@@ -477,11 +543,12 @@ static void handle_options(int argc, char **argv)
|
|||||||
{"disable-color", 0, 0, 's'},
|
{"disable-color", 0, 0, 's'},
|
||||||
{"log-level", 1, 0, 'e'},
|
{"log-level", 1, 0, 'e'},
|
||||||
{"timestamp", 0, 0, 'T'},
|
{"timestamp", 0, 0, 'T'},
|
||||||
|
{"db-upgrade", 0, 0, 'U' },
|
||||||
{"version", 0, 0, 'V' },
|
{"version", 0, 0, 'V' },
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "hc:l:d:Dse:TV",
|
c = getopt_long(argc, argv, "hc:l:d:Dse:TUV",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
@@ -512,6 +579,9 @@ static void handle_options(int argc, char **argv)
|
|||||||
case 'T':
|
case 'T':
|
||||||
log_set_print_timestamp(osmo_stderr_target, 1);
|
log_set_print_timestamp(osmo_stderr_target, 1);
|
||||||
break;
|
break;
|
||||||
|
case 'U':
|
||||||
|
cmdline_opts.db_upgrade = true;
|
||||||
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
print_version(1);
|
print_version(1);
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -572,6 +642,9 @@ int main(int argc, char **argv)
|
|||||||
INIT_LLIST_HEAD(&g_hlr->ss_sessions);
|
INIT_LLIST_HEAD(&g_hlr->ss_sessions);
|
||||||
INIT_LLIST_HEAD(&g_hlr->ussd_routes);
|
INIT_LLIST_HEAD(&g_hlr->ussd_routes);
|
||||||
|
|
||||||
|
/* Init default (call independent) SS session guard timeout value */
|
||||||
|
g_hlr->ncss_guard_timeout = NCSS_GUARD_TIMEOUT_DEFAULT;
|
||||||
|
|
||||||
rc = osmo_init_logging2(hlr_ctx, &hlr_log_info);
|
rc = osmo_init_logging2(hlr_ctx, &hlr_log_info);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
fprintf(stderr, "Error initializing logging\n");
|
fprintf(stderr, "Error initializing logging\n");
|
||||||
@@ -605,7 +678,7 @@ int main(int argc, char **argv)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_hlr->dbc = db_open(hlr_ctx, cmdline_opts.db_file, true);
|
g_hlr->dbc = db_open(hlr_ctx, cmdline_opts.db_file, true, cmdline_opts.db_upgrade);
|
||||||
if (!g_hlr->dbc) {
|
if (!g_hlr->dbc) {
|
||||||
LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
|
LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ struct hlr {
|
|||||||
struct hlr_euse *euse_default;
|
struct hlr_euse *euse_default;
|
||||||
struct llist_head iuse_list;
|
struct llist_head iuse_list;
|
||||||
|
|
||||||
|
/* NCSS (call independent) session guard timeout value */
|
||||||
|
int ncss_guard_timeout;
|
||||||
|
|
||||||
struct llist_head ussd_routes;
|
struct llist_head ussd_routes;
|
||||||
|
|
||||||
struct llist_head ss_sessions;
|
struct llist_head ss_sessions;
|
||||||
|
|||||||
@@ -44,8 +44,10 @@ static struct {
|
|||||||
const char *db_file;
|
const char *db_file;
|
||||||
bool bootstrap;
|
bool bootstrap;
|
||||||
const char *import_nitb_db;
|
const char *import_nitb_db;
|
||||||
|
bool db_upgrade;
|
||||||
} cmdline_opts = {
|
} cmdline_opts = {
|
||||||
.db_file = "hlr.db",
|
.db_file = "hlr.db",
|
||||||
|
.db_upgrade = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void print_help()
|
static void print_help()
|
||||||
@@ -59,6 +61,7 @@ static void print_help()
|
|||||||
printf(" -s --disable-color Do not print ANSI colors in the log\n");
|
printf(" -s --disable-color Do not print ANSI colors in the log\n");
|
||||||
printf(" -T --timestamp Prefix every log line with a timestamp.\n");
|
printf(" -T --timestamp Prefix every log line with a timestamp.\n");
|
||||||
printf(" -e --log-level number Set a global loglevel.\n");
|
printf(" -e --log-level number Set a global loglevel.\n");
|
||||||
|
printf(" -U --db-upgrade Allow HLR database schema upgrades.\n");
|
||||||
printf(" -V --version Print the version of OsmoHLR-db-tool.\n");
|
printf(" -V --version Print the version of OsmoHLR-db-tool.\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Commands:\n");
|
printf("Commands:\n");
|
||||||
@@ -96,11 +99,12 @@ static void handle_options(int argc, char **argv)
|
|||||||
{"disable-color", 0, 0, 's'},
|
{"disable-color", 0, 0, 's'},
|
||||||
{"timestamp", 0, 0, 'T'},
|
{"timestamp", 0, 0, 'T'},
|
||||||
{"log-level", 1, 0, 'e'},
|
{"log-level", 1, 0, 'e'},
|
||||||
|
{"db-upgrade", 0, 0, 'U' },
|
||||||
{"version", 0, 0, 'V' },
|
{"version", 0, 0, 'V' },
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "hl:d:sTe:V",
|
c = getopt_long(argc, argv, "hl:d:sTe:UV",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
@@ -124,6 +128,9 @@ static void handle_options(int argc, char **argv)
|
|||||||
case 'e':
|
case 'e':
|
||||||
log_set_log_level(osmo_stderr_target, atoi(optarg));
|
log_set_log_level(osmo_stderr_target, atoi(optarg));
|
||||||
break;
|
break;
|
||||||
|
case 'U':
|
||||||
|
cmdline_opts.db_upgrade = true;
|
||||||
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
print_version(1);
|
print_version(1);
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
@@ -293,7 +300,7 @@ void import_nitb_subscr(sqlite3 *nitb_db, sqlite3_stmt *stmt)
|
|||||||
|
|
||||||
imsi = sqlite3_column_int64(stmt, 0);
|
imsi = sqlite3_column_int64(stmt, 0);
|
||||||
|
|
||||||
snprintf(imsi_str, sizeof(imsi_str), "%"PRId64, imsi);
|
snprintf(imsi_str, sizeof(imsi_str), "%" PRId64, imsi);
|
||||||
|
|
||||||
rc = db_subscr_create(dbc, imsi_str);
|
rc = db_subscr_create(dbc, imsi_str);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
@@ -409,7 +416,7 @@ int main(int argc, char **argv)
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_hlr_db_tool_ctx->dbc = db_open(g_hlr_db_tool_ctx, cmdline_opts.db_file, true);
|
g_hlr_db_tool_ctx->dbc = db_open(g_hlr_db_tool_ctx, cmdline_opts.db_file, true, cmdline_opts.db_upgrade);
|
||||||
if (!g_hlr_db_tool_ctx->dbc) {
|
if (!g_hlr_db_tool_ctx->dbc) {
|
||||||
LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
|
LOGP(DMAIN, LOGL_FATAL, "Error opening database\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
|
|||||||
140
src/hlr_ussd.c
140
src/hlr_ussd.c
@@ -126,8 +126,10 @@ static struct hlr_ussd_route *ussd_route_lookup_7bit(struct hlr *hlr, const char
|
|||||||
struct hlr_ussd_route *rt;
|
struct hlr_ussd_route *rt;
|
||||||
llist_for_each_entry(rt, &hlr->ussd_routes, list) {
|
llist_for_each_entry(rt, &hlr->ussd_routes, list) {
|
||||||
if (!strncmp(ussd_code, rt->prefix, strlen(rt->prefix))) {
|
if (!strncmp(ussd_code, rt->prefix, strlen(rt->prefix))) {
|
||||||
LOGP(DSS, LOGL_DEBUG, "Found EUSE %s (prefix %s) for USSD Code '%s'\n",
|
LOGP(DSS, LOGL_DEBUG, "Found %s '%s' (prefix '%s') for USSD "
|
||||||
rt->u.euse->name, rt->prefix, ussd_code);
|
"Code '%s'\n", rt->is_external ? "EUSE" : "IUSE",
|
||||||
|
rt->is_external ? rt->u.euse->name : rt->u.iuse->name,
|
||||||
|
rt->prefix, ussd_code);
|
||||||
return rt;
|
return rt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,11 +208,11 @@ struct ss_session *ss_session_alloc(struct hlr *hlr, const char *imsi, uint32_t
|
|||||||
|
|
||||||
OSMO_STRLCPY_ARRAY(ss->imsi, imsi);
|
OSMO_STRLCPY_ARRAY(ss->imsi, imsi);
|
||||||
ss->session_id = session_id;
|
ss->session_id = session_id;
|
||||||
|
|
||||||
|
/* Schedule self-destruction timer */
|
||||||
osmo_timer_setup(&ss->timeout, ss_session_timeout, ss);
|
osmo_timer_setup(&ss->timeout, ss_session_timeout, ss);
|
||||||
/* NOTE: The timeout is currently global and not refreshed with subsequent messages
|
if (g_hlr->ncss_guard_timeout > 0)
|
||||||
* within the SS/USSD session. So 30s after the initial SS message, the session will
|
osmo_timer_schedule(&ss->timeout, g_hlr->ncss_guard_timeout, 0);
|
||||||
* timeout! */
|
|
||||||
osmo_timer_schedule(&ss->timeout, 30, 0);
|
|
||||||
|
|
||||||
llist_add_tail(&ss->list, &hlr->ss_sessions);
|
llist_add_tail(&ss->list, &hlr->ss_sessions);
|
||||||
return ss;
|
return ss;
|
||||||
@@ -295,7 +297,7 @@ static int handle_ussd_own_msisdn(struct osmo_gsup_conn *conn, struct ss_session
|
|||||||
if (strlen(subscr.msisdn) == 0)
|
if (strlen(subscr.msisdn) == 0)
|
||||||
snprintf(buf, sizeof(buf), "You have no MSISDN!");
|
snprintf(buf, sizeof(buf), "You have no MSISDN!");
|
||||||
else
|
else
|
||||||
snprintf(buf, sizeof(buf), "Your extension is %s\r", subscr.msisdn);
|
snprintf(buf, sizeof(buf), "Your extension is %s", subscr.msisdn);
|
||||||
ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
|
ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
|
||||||
break;
|
break;
|
||||||
case -ENOENT:
|
case -ENOENT:
|
||||||
@@ -313,11 +315,98 @@ static int handle_ussd_own_imsi(struct osmo_gsup_conn *conn, struct ss_session *
|
|||||||
const struct osmo_gsup_message *gsup, const struct ss_request *req)
|
const struct osmo_gsup_message *gsup, const struct ss_request *req)
|
||||||
{
|
{
|
||||||
char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
|
char buf[GSM0480_USSD_7BIT_STRING_LEN+1];
|
||||||
snprintf(buf, sizeof(buf), "Your IMSI is %s!\n", ss->imsi);
|
snprintf(buf, sizeof(buf), "Your IMSI is %s", ss->imsi);
|
||||||
ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
|
ss_tx_ussd_7bit(ss, true, req->invoke_id, buf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int handle_ussd_get_ran(struct osmo_gsup_conn *conn, struct ss_session *ss,
|
||||||
|
const struct osmo_gsup_message *gsup,
|
||||||
|
const struct ss_request *req)
|
||||||
|
{
|
||||||
|
struct hlr_subscriber subscr;
|
||||||
|
const char *response;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
#define RAN_TYPE_DESC "Available RAN types: "
|
||||||
|
|
||||||
|
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
|
||||||
|
switch (rc) {
|
||||||
|
case 0:
|
||||||
|
if (subscr.rat_types[OSMO_RAT_GERAN_A] && subscr.rat_types[OSMO_RAT_UTRAN_IU])
|
||||||
|
response = RAN_TYPE_DESC "GERAN-A (2G) & UTRAN-Iu (3G)";
|
||||||
|
else if (subscr.rat_types[OSMO_RAT_GERAN_A])
|
||||||
|
response = RAN_TYPE_DESC "GERAN-A (2G)";
|
||||||
|
else if (subscr.rat_types[OSMO_RAT_UTRAN_IU])
|
||||||
|
response = RAN_TYPE_DESC "UTRAN-Iu (3G)";
|
||||||
|
else
|
||||||
|
response = "No RAN types available";
|
||||||
|
|
||||||
|
rc = ss_tx_ussd_7bit(ss, true, req->invoke_id, response);
|
||||||
|
break;
|
||||||
|
case -ENOENT:
|
||||||
|
rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
|
||||||
|
break;
|
||||||
|
case -EIO:
|
||||||
|
default:
|
||||||
|
rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_ussd_umts_on(struct osmo_gsup_conn *conn, struct ss_session *ss,
|
||||||
|
const struct osmo_gsup_message *gsup,
|
||||||
|
const struct ss_request *req)
|
||||||
|
{
|
||||||
|
struct hlr_subscriber subscr;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
|
||||||
|
switch (rc) {
|
||||||
|
case 0:
|
||||||
|
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_UTRAN_IU, true);
|
||||||
|
rc = ss_tx_ussd_7bit(ss, true, req->invoke_id,
|
||||||
|
"Enabled UTRAN-Iu (3G)");
|
||||||
|
break;
|
||||||
|
case -ENOENT:
|
||||||
|
rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
|
||||||
|
break;
|
||||||
|
case -EIO:
|
||||||
|
default:
|
||||||
|
rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_ussd_umts_off(struct osmo_gsup_conn *conn, struct ss_session *ss,
|
||||||
|
const struct osmo_gsup_message *gsup,
|
||||||
|
const struct ss_request *req)
|
||||||
|
{
|
||||||
|
struct hlr_subscriber subscr;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = db_subscr_get_by_imsi(g_hlr->dbc, ss->imsi, &subscr);
|
||||||
|
switch (rc) {
|
||||||
|
case 0:
|
||||||
|
hlr_subscr_rat_flag(g_hlr, &subscr, OSMO_RAT_UTRAN_IU, false);
|
||||||
|
rc = ss_tx_ussd_7bit(ss, true, req->invoke_id,
|
||||||
|
"Disabled UTRAN-Iu (3G)");
|
||||||
|
break;
|
||||||
|
case -ENOENT:
|
||||||
|
rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_UNKNOWN_SUBSCRIBER);
|
||||||
|
break;
|
||||||
|
case -EIO:
|
||||||
|
default:
|
||||||
|
rc = ss_tx_error(ss, true, GSM0480_ERR_CODE_SYSTEM_FAILURE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct hlr_iuse hlr_iuses[] = {
|
static const struct hlr_iuse hlr_iuses[] = {
|
||||||
{
|
{
|
||||||
@@ -328,6 +417,18 @@ static const struct hlr_iuse hlr_iuses[] = {
|
|||||||
.name = "own-imsi",
|
.name = "own-imsi",
|
||||||
.handle_ussd = handle_ussd_own_imsi,
|
.handle_ussd = handle_ussd_own_imsi,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "get-ran",
|
||||||
|
.handle_ussd = handle_ussd_get_ran,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "umts-on",
|
||||||
|
.handle_ussd = handle_ussd_umts_on,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.name = "umts-off",
|
||||||
|
.handle_ussd = handle_ussd_umts_off,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct hlr_iuse *iuse_find(const char *name)
|
const struct hlr_iuse *iuse_find(const char *name)
|
||||||
@@ -397,8 +498,15 @@ static int handle_ss(struct ss_session *ss, const struct osmo_gsup_message *gsup
|
|||||||
|
|
||||||
LOGPSS(ss, LOGL_INFO, "SS CompType=%s, OpCode=%s\n",
|
LOGPSS(ss, LOGL_INFO, "SS CompType=%s, OpCode=%s\n",
|
||||||
gsm0480_comp_type_name(comp_type), gsm0480_op_code_name(req->opcode));
|
gsm0480_comp_type_name(comp_type), gsm0480_op_code_name(req->opcode));
|
||||||
/* FIXME */
|
|
||||||
return 0;
|
/**
|
||||||
|
* FIXME: As we don't store any SS related information
|
||||||
|
* (e.g. call forwarding preferences) in the database,
|
||||||
|
* we don't handle "structured" SS requests at all.
|
||||||
|
*/
|
||||||
|
LOGPSS(ss, LOGL_NOTICE, "Structured SS requests are not supported, rejecting...\n");
|
||||||
|
ss_tx_error(ss, req->invoke_id, GSM0480_ERR_CODE_FACILITY_NOT_SUPPORTED);
|
||||||
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle a USSD GSUP message for a given SS Session received from VLR or EUSE */
|
/* Handle a USSD GSUP message for a given SS Session received from VLR or EUSE */
|
||||||
@@ -446,6 +554,8 @@ static int handle_ussd(struct osmo_gsup_conn *conn, struct ss_session *ss,
|
|||||||
} else {
|
} else {
|
||||||
/* Handle internally */
|
/* Handle internally */
|
||||||
ss->u.iuse->handle_ussd(conn, ss, gsup, req);
|
ss->u.iuse->handle_ussd(conn, ss, gsup, req);
|
||||||
|
/* Release session immediately */
|
||||||
|
ss_session_free(ss);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,6 +615,11 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
|
|||||||
ss->is_external = false;
|
ss->is_external = false;
|
||||||
ss->u.iuse = rt->u.iuse;
|
ss->u.iuse = rt->u.iuse;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (hlr->euse_default) {
|
||||||
|
ss->is_external = true;
|
||||||
|
ss->u.euse = hlr->euse_default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* dispatch unstructured SS to routing */
|
/* dispatch unstructured SS to routing */
|
||||||
@@ -521,6 +636,11 @@ int rx_proc_ss_req(struct osmo_gsup_conn *conn, const struct osmo_gsup_message *
|
|||||||
gsup->imsi, gsup->session_id);
|
gsup->imsi, gsup->session_id);
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reschedule self-destruction timer */
|
||||||
|
if (g_hlr->ncss_guard_timeout > 0)
|
||||||
|
osmo_timer_schedule(&ss->timeout, g_hlr->ncss_guard_timeout, 0);
|
||||||
|
|
||||||
if (ss_op_is_ussd(req.opcode)) {
|
if (ss_op_is_ussd(req.opcode)) {
|
||||||
/* dispatch unstructured SS to routing */
|
/* dispatch unstructured SS to routing */
|
||||||
handle_ussd(conn, ss, gsup, &req);
|
handle_ussd(conn, ss, gsup, &req);
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
|
|
||||||
#include <osmocom/core/linuxlist.h>
|
#include <osmocom/core/linuxlist.h>
|
||||||
#include <osmocom/gsm/gsup.h>
|
#include <osmocom/gsm/gsup.h>
|
||||||
|
|
||||||
#include "gsup_server.h"
|
#include "gsup_server.h"
|
||||||
|
|
||||||
struct osmo_gsup_conn;
|
#define NCSS_GUARD_TIMEOUT_DEFAULT 30
|
||||||
|
|
||||||
struct hlr_ussd_route {
|
struct hlr_ussd_route {
|
||||||
/* g_hlr.routes */
|
/* g_hlr.routes */
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include "hlr.h"
|
#include "hlr.h"
|
||||||
#include "hlr_vty.h"
|
#include "hlr_vty.h"
|
||||||
#include "hlr_vty_subscr.h"
|
#include "hlr_vty_subscr.h"
|
||||||
|
#include "hlr_ussd.h"
|
||||||
#include "gsup_server.h"
|
#include "gsup_server.h"
|
||||||
|
|
||||||
struct cmd_node hlr_node = {
|
struct cmd_node hlr_node = {
|
||||||
@@ -132,10 +133,13 @@ DEFUN(cfg_hlr_gsup_bind_ip,
|
|||||||
#define UROUTE_STR "Routing Configuration\n"
|
#define UROUTE_STR "Routing Configuration\n"
|
||||||
#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
|
#define PREFIX_STR "Prefix-Matching Route\n" "USSD Prefix\n"
|
||||||
|
|
||||||
#define INT_CHOICE "(own-msisdn|own-imsi)"
|
#define INT_CHOICE "(own-msisdn|own-imsi|get-ran|umts-on|umts-off)"
|
||||||
#define INT_STR "Internal USSD Handler\n" \
|
#define INT_STR "Internal USSD Handler\n" \
|
||||||
"Respond with subscribers' own MSISDN\n" \
|
"Respond with subscribers' own MSISDN\n" \
|
||||||
"Respond with subscribers' own IMSI\n"
|
"Respond with subscribers' own IMSI\n" \
|
||||||
|
"Respond with available RAN types\n" \
|
||||||
|
"Enable UMTS service\n" \
|
||||||
|
"Disable UMTS service\n"
|
||||||
|
|
||||||
#define EXT_STR "External USSD Handler\n" \
|
#define EXT_STR "External USSD Handler\n" \
|
||||||
"Name of External USSD Handler (IPA CCM ID)\n"
|
"Name of External USSD Handler (IPA CCM ID)\n"
|
||||||
@@ -193,7 +197,13 @@ DEFUN(cfg_ussd_defaultroute, cfg_ussd_defaultroute_cmd,
|
|||||||
USSD_STR "Configure default-route for all USSD to unknown destinations\n"
|
USSD_STR "Configure default-route for all USSD to unknown destinations\n"
|
||||||
EXT_STR)
|
EXT_STR)
|
||||||
{
|
{
|
||||||
struct hlr_euse *euse = euse_find(g_hlr, argv[0]);
|
struct hlr_euse *euse;
|
||||||
|
|
||||||
|
euse = euse_find(g_hlr, argv[0]);
|
||||||
|
if (!euse) {
|
||||||
|
vty_out(vty, "%% Cannot find EUSE %s%s", argv[0], VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
|
||||||
if (g_hlr->euse_default != euse) {
|
if (g_hlr->euse_default != euse) {
|
||||||
vty_out(vty, "Switching default route from %s to %s%s",
|
vty_out(vty, "Switching default route from %s to %s%s",
|
||||||
@@ -282,9 +292,22 @@ static int config_write_euse(struct vty *vty)
|
|||||||
if (g_hlr->euse_default)
|
if (g_hlr->euse_default)
|
||||||
vty_out(vty, " ussd default-route external %s%s", g_hlr->euse_default->name, VTY_NEWLINE);
|
vty_out(vty, " ussd default-route external %s%s", g_hlr->euse_default->name, VTY_NEWLINE);
|
||||||
|
|
||||||
|
if (g_hlr->ncss_guard_timeout != NCSS_GUARD_TIMEOUT_DEFAULT)
|
||||||
|
vty_out(vty, " ncss-guard-timeout %i%s",
|
||||||
|
g_hlr->ncss_guard_timeout, VTY_NEWLINE);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFUN(cfg_ncss_guard_timeout, cfg_ncss_guard_timeout_cmd,
|
||||||
|
"ncss-guard-timeout <0-255>",
|
||||||
|
"Set guard timer for NCSS (call independent SS) session activity\n"
|
||||||
|
"Guard timer value (sec.), or 0 to disable")
|
||||||
|
{
|
||||||
|
g_hlr->ncss_guard_timeout = atoi(argv[0]);
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************************************
|
/***********************************************************************
|
||||||
* Common Code
|
* Common Code
|
||||||
***********************************************************************/
|
***********************************************************************/
|
||||||
@@ -347,6 +370,7 @@ void hlr_vty_init(const struct log_info *cat)
|
|||||||
install_element(HLR_NODE, &cfg_ussd_no_route_pfx_cmd);
|
install_element(HLR_NODE, &cfg_ussd_no_route_pfx_cmd);
|
||||||
install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd);
|
install_element(HLR_NODE, &cfg_ussd_defaultroute_cmd);
|
||||||
install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd);
|
install_element(HLR_NODE, &cfg_ussd_no_defaultroute_cmd);
|
||||||
|
install_element(HLR_NODE, &cfg_ncss_guard_timeout_cmd);
|
||||||
|
|
||||||
hlr_vty_subscriber_init();
|
hlr_vty_subscriber_init();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,14 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include <osmocom/gsm/gsm23003.h>
|
#include <osmocom/gsm/gsm23003.h>
|
||||||
#include <osmocom/vty/vty.h>
|
#include <osmocom/vty/vty.h>
|
||||||
#include <osmocom/vty/command.h>
|
#include <osmocom/vty/command.h>
|
||||||
#include <osmocom/core/utils.h>
|
#include <osmocom/core/utils.h>
|
||||||
|
#include <osmocom/gsm/gsm_utils.h>
|
||||||
|
|
||||||
#include "hlr.h"
|
#include "hlr.h"
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
@@ -33,9 +36,21 @@ struct vty;
|
|||||||
|
|
||||||
#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
|
#define hexdump_buf(buf) osmo_hexdump_nospc((void*)buf, sizeof(buf))
|
||||||
|
|
||||||
|
static char *get_datestr(const time_t *t)
|
||||||
|
{
|
||||||
|
static char buf[32];
|
||||||
|
struct tm tm;
|
||||||
|
|
||||||
|
tm = *gmtime(t);
|
||||||
|
|
||||||
|
strftime(buf, sizeof(buf), "%FT%T+00:00", &tm);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
|
static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
int i;
|
||||||
struct osmo_sub_auth_data aud2g;
|
struct osmo_sub_auth_data aud2g;
|
||||||
struct osmo_sub_auth_data aud3g;
|
struct osmo_sub_auth_data aud3g;
|
||||||
|
|
||||||
@@ -63,6 +78,14 @@ static void subscr_dump_full_vty(struct vty *vty, struct hlr_subscriber *subscr)
|
|||||||
vty_out(vty, " PS disabled%s", VTY_NEWLINE);
|
vty_out(vty, " PS disabled%s", VTY_NEWLINE);
|
||||||
if (subscr->ms_purged_ps)
|
if (subscr->ms_purged_ps)
|
||||||
vty_out(vty, " PS purged%s", VTY_NEWLINE);
|
vty_out(vty, " PS purged%s", VTY_NEWLINE);
|
||||||
|
if (subscr->last_lu_seen)
|
||||||
|
vty_out(vty, " last LU seen: %s%s", get_datestr(&subscr->last_lu_seen), VTY_NEWLINE);
|
||||||
|
for (i = OSMO_RAT_UNKNOWN + 1; i < ARRAY_SIZE(subscr->rat_types); i++) {
|
||||||
|
vty_out(vty, " %s: %s%s", osmo_rat_type_name(i), subscr->rat_types[i] ? "allowed" : "forbidden",
|
||||||
|
VTY_NEWLINE);
|
||||||
|
}
|
||||||
|
if (subscr->ms_purged_cs)
|
||||||
|
vty_out(vty, " CS purged%s", VTY_NEWLINE);
|
||||||
|
|
||||||
if (!*subscr->imsi)
|
if (!*subscr->imsi)
|
||||||
return;
|
return;
|
||||||
@@ -130,18 +153,19 @@ static int get_subscr_by_argv(struct vty *vty, const char *type, const char *id,
|
|||||||
#define SUBSCR_CMD "subscriber "
|
#define SUBSCR_CMD "subscriber "
|
||||||
#define SUBSCR_CMD_HELP "Subscriber management commands\n"
|
#define SUBSCR_CMD_HELP "Subscriber management commands\n"
|
||||||
|
|
||||||
#define SUBSCR_ID "(imsi|msisdn|id) IDENT "
|
#define SUBSCR_ID "(imsi|msisdn|id) IDENT"
|
||||||
#define SUBSCR_ID_HELP \
|
#define SUBSCR_ID_HELP \
|
||||||
"Identify subscriber by IMSI\n" \
|
"Identify subscriber by IMSI\n" \
|
||||||
"Identify subscriber by MSISDN (phone number)\n" \
|
"Identify subscriber by MSISDN (phone number)\n" \
|
||||||
"Identify subscriber by database ID\n" \
|
"Identify subscriber by database ID\n" \
|
||||||
"IMSI/MSISDN/ID of the subscriber\n"
|
"IMSI/MSISDN/ID of the subscriber\n"
|
||||||
|
|
||||||
#define SUBSCR SUBSCR_CMD SUBSCR_ID
|
#define SUBSCR SUBSCR_CMD SUBSCR_ID " "
|
||||||
#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
|
#define SUBSCR_HELP SUBSCR_CMD_HELP SUBSCR_ID_HELP
|
||||||
|
|
||||||
#define SUBSCR_UPDATE SUBSCR "update "
|
#define SUBSCR_UPDATE SUBSCR "update "
|
||||||
#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
|
#define SUBSCR_UPDATE_HELP SUBSCR_HELP "Set or update subscriber data\n"
|
||||||
|
#define SUBSCR_MSISDN_HELP "Set MSISDN (phone number) of the subscriber\n"
|
||||||
|
|
||||||
DEFUN(subscriber_show,
|
DEFUN(subscriber_show,
|
||||||
subscriber_show_cmd,
|
subscriber_show_cmd,
|
||||||
@@ -159,6 +183,10 @@ DEFUN(subscriber_show,
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALIAS(subscriber_show, show_subscriber_cmd,
|
||||||
|
"show " SUBSCR_CMD SUBSCR_ID,
|
||||||
|
SHOW_STR SUBSCR_CMD_HELP SUBSCR_ID_HELP);
|
||||||
|
|
||||||
DEFUN(subscriber_create,
|
DEFUN(subscriber_create,
|
||||||
subscriber_create_cmd,
|
subscriber_create_cmd,
|
||||||
SUBSCR_CMD "imsi IDENT create",
|
SUBSCR_CMD "imsi IDENT create",
|
||||||
@@ -224,9 +252,9 @@ DEFUN(subscriber_delete,
|
|||||||
|
|
||||||
DEFUN(subscriber_msisdn,
|
DEFUN(subscriber_msisdn,
|
||||||
subscriber_msisdn_cmd,
|
subscriber_msisdn_cmd,
|
||||||
SUBSCR_UPDATE "msisdn MSISDN",
|
SUBSCR_UPDATE "msisdn (none|MSISDN)",
|
||||||
SUBSCR_UPDATE_HELP
|
SUBSCR_UPDATE_HELP SUBSCR_MSISDN_HELP
|
||||||
"Set MSISDN (phone number) of the subscriber\n"
|
"Remove MSISDN (phone number)\n"
|
||||||
"New MSISDN (phone number)\n")
|
"New MSISDN (phone number)\n")
|
||||||
{
|
{
|
||||||
struct hlr_subscriber subscr;
|
struct hlr_subscriber subscr;
|
||||||
@@ -234,6 +262,9 @@ DEFUN(subscriber_msisdn,
|
|||||||
const char *id = argv[1];
|
const char *id = argv[1];
|
||||||
const char *msisdn = argv[2];
|
const char *msisdn = argv[2];
|
||||||
|
|
||||||
|
if (strcmp(msisdn, "none") == 0)
|
||||||
|
msisdn = NULL;
|
||||||
|
else {
|
||||||
if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
|
if (strlen(msisdn) > sizeof(subscr.msisdn) - 1) {
|
||||||
vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
|
vty_out(vty, "%% MSISDN is too long, max. %zu characters are allowed%s",
|
||||||
sizeof(subscr.msisdn)-1, VTY_NEWLINE);
|
sizeof(subscr.msisdn)-1, VTY_NEWLINE);
|
||||||
@@ -244,6 +275,7 @@ DEFUN(subscriber_msisdn,
|
|||||||
vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
|
vty_out(vty, "%% MSISDN invalid: '%s'%s", msisdn, VTY_NEWLINE);
|
||||||
return CMD_WARNING;
|
return CMD_WARNING;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (get_subscr_by_argv(vty, id_type, id, &subscr))
|
if (get_subscr_by_argv(vty, id_type, id, &subscr))
|
||||||
return CMD_WARNING;
|
return CMD_WARNING;
|
||||||
@@ -254,11 +286,18 @@ DEFUN(subscriber_msisdn,
|
|||||||
return CMD_WARNING;
|
return CMD_WARNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msisdn) {
|
||||||
vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
|
vty_out(vty, "%% Updated subscriber IMSI='%s' to MSISDN='%s'%s",
|
||||||
subscr.imsi, msisdn, VTY_NEWLINE);
|
subscr.imsi, msisdn, VTY_NEWLINE);
|
||||||
|
|
||||||
if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
|
if (db_subscr_get_by_msisdn(g_hlr->dbc, msisdn, &subscr) == 0)
|
||||||
osmo_hlr_subscriber_update_notify(&subscr);
|
osmo_hlr_subscriber_update_notify(&subscr);
|
||||||
|
} else {
|
||||||
|
vty_out(vty, "%% Updated subscriber IMSI='%s': removed MSISDN%s",
|
||||||
|
subscr.imsi, VTY_NEWLINE);
|
||||||
|
|
||||||
|
osmo_hlr_subscriber_update_notify(&subscr);
|
||||||
|
}
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -475,9 +514,49 @@ DEFUN(subscriber_aud3g,
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFUN(subscriber_rat,
|
||||||
|
subscriber_rat_cmd,
|
||||||
|
SUBSCR_UPDATE "rat (geran-a|utran-iu) (allowed|forbidden)",
|
||||||
|
SUBSCR_UPDATE_HELP
|
||||||
|
"Allow or forbid specific Radio Access Types\n"
|
||||||
|
"Set access to GERAN-A\n"
|
||||||
|
"Set access to UTRAN-Iu\n"
|
||||||
|
"Allow access\n"
|
||||||
|
"Forbid access\n")
|
||||||
|
{
|
||||||
|
struct hlr_subscriber subscr;
|
||||||
|
const char *id_type = argv[0];
|
||||||
|
const char *id = argv[1];
|
||||||
|
const char *rat_str = argv[2];
|
||||||
|
const char *allowed_forbidden = argv[3];
|
||||||
|
enum osmo_rat_type rat;
|
||||||
|
bool allowed;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (strcmp(rat_str, "geran-a") == 0)
|
||||||
|
rat = OSMO_RAT_GERAN_A;
|
||||||
|
else if (strcmp(rat_str, "utran-iu") == 0)
|
||||||
|
rat = OSMO_RAT_UTRAN_IU;
|
||||||
|
|
||||||
|
allowed = (strcmp(allowed_forbidden, "allowed") == 0);
|
||||||
|
|
||||||
|
if (get_subscr_by_argv(vty, id_type, id, &subscr))
|
||||||
|
return CMD_WARNING;
|
||||||
|
|
||||||
|
rc = hlr_subscr_rat_flag(g_hlr, &subscr, rat, allowed);
|
||||||
|
|
||||||
|
if (rc && rc != -ENOEXEC) {
|
||||||
|
vty_out(vty, "%% Error: cannot set %s to %s%s",
|
||||||
|
osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden", VTY_NEWLINE);
|
||||||
|
return CMD_WARNING;
|
||||||
|
}
|
||||||
|
return CMD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
void hlr_vty_subscriber_init(void)
|
void hlr_vty_subscriber_init(void)
|
||||||
{
|
{
|
||||||
install_element_ve(&subscriber_show_cmd);
|
install_element_ve(&subscriber_show_cmd);
|
||||||
|
install_element_ve(&show_subscriber_cmd);
|
||||||
install_element(ENABLE_NODE, &subscriber_create_cmd);
|
install_element(ENABLE_NODE, &subscriber_create_cmd);
|
||||||
install_element(ENABLE_NODE, &subscriber_delete_cmd);
|
install_element(ENABLE_NODE, &subscriber_delete_cmd);
|
||||||
install_element(ENABLE_NODE, &subscriber_msisdn_cmd);
|
install_element(ENABLE_NODE, &subscriber_msisdn_cmd);
|
||||||
@@ -485,4 +564,5 @@ void hlr_vty_subscriber_init(void)
|
|||||||
install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
|
install_element(ENABLE_NODE, &subscriber_aud2g_cmd);
|
||||||
install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
|
install_element(ENABLE_NODE, &subscriber_no_aud3g_cmd);
|
||||||
install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
|
install_element(ENABLE_NODE, &subscriber_aud3g_cmd);
|
||||||
|
install_element(ENABLE_NODE, &subscriber_rat_cmd);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ struct lu_operation {
|
|||||||
enum lu_state state;
|
enum lu_state state;
|
||||||
/*! CS (false) or PS (true) Location Update? */
|
/*! CS (false) or PS (true) Location Update? */
|
||||||
bool is_ps;
|
bool is_ps;
|
||||||
|
/*! RAT type indicator: coming in on GERAN-A? UTRAN-Iu? */
|
||||||
|
enum osmo_rat_type via_rat;
|
||||||
|
|
||||||
/*! currently running timer */
|
/*! currently running timer */
|
||||||
struct osmo_timer_list timer;
|
struct osmo_timer_list timer;
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ python-tests:
|
|||||||
# don't run vty and ctrl tests concurrently so that the ports don't conflict
|
# don't run vty and ctrl tests concurrently so that the ports don't conflict
|
||||||
$(MAKE) vty-test
|
$(MAKE) vty-test
|
||||||
$(MAKE) ctrl-test
|
$(MAKE) ctrl-test
|
||||||
|
else
|
||||||
|
python-tests:
|
||||||
|
echo "Not running python-based external tests (determined at configure-time)"
|
||||||
|
endif
|
||||||
|
|
||||||
VTY_TEST_DB = hlr_vty_test.db
|
VTY_TEST_DB = hlr_vty_test.db
|
||||||
|
|
||||||
@@ -75,11 +79,6 @@ ctrl-test:
|
|||||||
-rm -f $(CTRL_TEST_DB)
|
-rm -f $(CTRL_TEST_DB)
|
||||||
-rm $(CTRL_TEST_DB)-*
|
-rm $(CTRL_TEST_DB)-*
|
||||||
|
|
||||||
else
|
|
||||||
python-tests:
|
|
||||||
echo "Not running python-based tests (determined at configure-time)"
|
|
||||||
endif
|
|
||||||
|
|
||||||
check-local: atconfig $(TESTSUITE)
|
check-local: atconfig $(TESTSUITE)
|
||||||
$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
|
$(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS)
|
||||||
$(MAKE) $(AM_MAKEFLAGS) python-tests
|
$(MAKE) $(AM_MAKEFLAGS) python-tests
|
||||||
|
|||||||
@@ -850,7 +850,7 @@ int main(int argc, char **argv)
|
|||||||
log_set_log_level(osmo_stderr_target, LOGL_ERROR);
|
log_set_log_level(osmo_stderr_target, LOGL_ERROR);
|
||||||
/* Disable SQLite logging so that we're not vulnerable on SQLite error messages changing across
|
/* Disable SQLite logging so that we're not vulnerable on SQLite error messages changing across
|
||||||
* library versions. */
|
* library versions. */
|
||||||
dbc = db_open(ctx, "db_test.db", false);
|
dbc = db_open(ctx, "db_test.db", false, false);
|
||||||
log_set_log_level(osmo_stderr_target, 0);
|
log_set_log_level(osmo_stderr_target, 0);
|
||||||
OSMO_ASSERT(dbc);
|
OSMO_ASSERT(dbc);
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ OsmoHLR> list
|
|||||||
show talloc-context (application|all) (full|brief|DEPTH) filter REGEXP
|
show talloc-context (application|all) (full|brief|DEPTH) filter REGEXP
|
||||||
show gsup-connections
|
show gsup-connections
|
||||||
subscriber (imsi|msisdn|id) IDENT show
|
subscriber (imsi|msisdn|id) IDENT show
|
||||||
|
show subscriber (imsi|msisdn|id) IDENT
|
||||||
|
|
||||||
OsmoHLR> enable
|
OsmoHLR> enable
|
||||||
OsmoHLR# list
|
OsmoHLR# list
|
||||||
@@ -77,6 +78,7 @@ OsmoHLR(config-hlr)# list
|
|||||||
no ussd route prefix PREFIX
|
no ussd route prefix PREFIX
|
||||||
ussd default-route external EUSE
|
ussd default-route external EUSE
|
||||||
no ussd default-route
|
no ussd default-route
|
||||||
|
ncss-guard-timeout <0-255>
|
||||||
|
|
||||||
OsmoHLR(config-hlr)# gsup
|
OsmoHLR(config-hlr)# gsup
|
||||||
OsmoHLR(config-hlr-gsup)# list
|
OsmoHLR(config-hlr-gsup)# list
|
||||||
@@ -107,25 +109,15 @@ Current configuration:
|
|||||||
!
|
!
|
||||||
!
|
!
|
||||||
log stderr
|
log stderr
|
||||||
logging filter all 1
|
...
|
||||||
logging color 1
|
|
||||||
logging print category 1
|
|
||||||
logging print extended-timestamp 1
|
|
||||||
logging print file 1
|
|
||||||
logging level all notice
|
|
||||||
logging level main notice
|
logging level main notice
|
||||||
logging level db notice
|
logging level db notice
|
||||||
logging level auc notice
|
logging level auc notice
|
||||||
logging level ss info
|
logging level ss info
|
||||||
...
|
...
|
||||||
!
|
|
||||||
line vty
|
|
||||||
no login
|
|
||||||
!
|
|
||||||
ctrl
|
|
||||||
bind 127.0.0.1
|
|
||||||
hlr
|
hlr
|
||||||
gsup
|
gsup
|
||||||
bind ip 127.0.0.1
|
bind ip 127.0.0.1
|
||||||
ussd route prefix *#100# internal own-msisdn
|
ussd route prefix *#100# internal own-msisdn
|
||||||
|
ussd route prefix *#101# internal own-imsi
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ OsmoHLR> enable
|
|||||||
OsmoHLR# list
|
OsmoHLR# list
|
||||||
...
|
...
|
||||||
subscriber (imsi|msisdn|id) IDENT show
|
subscriber (imsi|msisdn|id) IDENT show
|
||||||
|
show subscriber (imsi|msisdn|id) IDENT
|
||||||
subscriber imsi IDENT create
|
subscriber imsi IDENT create
|
||||||
subscriber (imsi|msisdn|id) IDENT delete
|
subscriber (imsi|msisdn|id) IDENT delete
|
||||||
subscriber (imsi|msisdn|id) IDENT update msisdn MSISDN
|
subscriber (imsi|msisdn|id) IDENT update msisdn (none|MSISDN)
|
||||||
subscriber (imsi|msisdn|id) IDENT update aud2g none
|
subscriber (imsi|msisdn|id) IDENT update aud2g none
|
||||||
subscriber (imsi|msisdn|id) IDENT update aud2g (comp128v1|comp128v2|comp128v3|xor) ki KI
|
subscriber (imsi|msisdn|id) IDENT update aud2g (comp128v1|comp128v2|comp128v3|xor) ki KI
|
||||||
subscriber (imsi|msisdn|id) IDENT update aud3g none
|
subscriber (imsi|msisdn|id) IDENT update aud3g none
|
||||||
@@ -33,6 +34,13 @@ OsmoHLR# subscriber id 1 show
|
|||||||
OsmoHLR# subscriber msisdn 12345 show
|
OsmoHLR# subscriber msisdn 12345 show
|
||||||
% No subscriber for msisdn = '12345'
|
% No subscriber for msisdn = '12345'
|
||||||
|
|
||||||
|
OsmoHLR# show subscriber imsi 123456789023000
|
||||||
|
% No subscriber for imsi = '123456789023000'
|
||||||
|
OsmoHLR# show subscriber id 1
|
||||||
|
% No subscriber for id = '1'
|
||||||
|
OsmoHLR# show subscriber msisdn 12345
|
||||||
|
% No subscriber for msisdn = '12345'
|
||||||
|
|
||||||
OsmoHLR# subscriber imsi 1234567890230001 create
|
OsmoHLR# subscriber imsi 1234567890230001 create
|
||||||
% Not a valid IMSI: 1234567890230001
|
% Not a valid IMSI: 1234567890230001
|
||||||
OsmoHLR# subscriber imsi 12345678902300x create
|
OsmoHLR# subscriber imsi 12345678902300x create
|
||||||
@@ -78,6 +86,21 @@ OsmoHLR# subscriber msisdn 12345 update msisdn 423
|
|||||||
OsmoHLR# subscriber msisdn 12345 show
|
OsmoHLR# subscriber msisdn 12345 show
|
||||||
% No subscriber for msisdn = '12345'
|
% No subscriber for msisdn = '12345'
|
||||||
|
|
||||||
|
OsmoHLR# subscriber msisdn 423 update msisdn none
|
||||||
|
% Updated subscriber IMSI='123456789023000': removed MSISDN
|
||||||
|
OsmoHLR# subscriber msisdn 423 show
|
||||||
|
% No subscriber for msisdn = '423'
|
||||||
|
OsmoHLR# subscriber imsi 123456789023000 show
|
||||||
|
ID: 1
|
||||||
|
IMSI: 123456789023000
|
||||||
|
MSISDN: none
|
||||||
|
OsmoHLR# subscriber imsi 123456789023000 update msisdn 423
|
||||||
|
% Updated subscriber IMSI='123456789023000' to MSISDN='423'
|
||||||
|
OsmoHLR# subscriber msisdn 423 show
|
||||||
|
ID: 1
|
||||||
|
IMSI: 123456789023000
|
||||||
|
MSISDN: 423
|
||||||
|
|
||||||
OsmoHLR# subscriber imsi 123456789023000 show
|
OsmoHLR# subscriber imsi 123456789023000 show
|
||||||
ID: 1
|
ID: 1
|
||||||
IMSI: 123456789023000
|
IMSI: 123456789023000
|
||||||
@@ -96,6 +119,10 @@ OsmoHLR# subscriber imsi 123456789023000 update ?
|
|||||||
aud2g Set 2G authentication data
|
aud2g Set 2G authentication data
|
||||||
aud3g Set UMTS authentication data (3G, and 2G with UMTS AKA)
|
aud3g Set UMTS authentication data (3G, and 2G with UMTS AKA)
|
||||||
|
|
||||||
|
OsmoHLR# subscriber imsi 123456789023000 update msisdn ?
|
||||||
|
none Remove MSISDN (phone number)
|
||||||
|
MSISDN New MSISDN (phone number)
|
||||||
|
|
||||||
OsmoHLR# subscriber imsi 123456789023000 update aud2g ?
|
OsmoHLR# subscriber imsi 123456789023000 update aud2g ?
|
||||||
none Delete 2G authentication data
|
none Delete 2G authentication data
|
||||||
comp128v1 Use COMP128v1 algorithm
|
comp128v1 Use COMP128v1 algorithm
|
||||||
|
|||||||
Reference in New Issue
Block a user