split off osmo-msc: remove files, apply build, rename

Change-Id: Icf025e5ea8d180613b3114282951c9afa67af9a7
This commit is contained in:
Neels Hofmeyr 2017-07-06 18:39:28 +02:00 committed by Harald Welte
parent 4585317f1b
commit bac227653a
251 changed files with 17 additions and 100345 deletions

View File

@ -44,30 +44,11 @@ PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl)
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 0.9.5)
PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 0.2.0)
PKG_CHECK_MODULES(LIBOSMOGB, libosmogb >= 0.6.4)
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.0.1)
PKG_CHECK_MODULES(LIBOSMOSIGTRAN, libosmo-sigtran) # TODO version?
PKG_CHECK_MODULES(LIBCRYPTO, libcrypto >= 0.9.5)
PKG_CHECK_MODULES(LIBOSMOLEGACYMGCP, libosmo-legacy-mgcp >= 0.0.1)
# Enabke/disable the NAT?
AC_ARG_ENABLE([nat], [AS_HELP_STRING([--enable-nat], [Build the BSC NAT. Requires SCCP])],
[osmo_ac_build_nat="$enableval"],[osmo_ac_build_nat="no"])
if test "$osmo_ac_build_nat" = "yes" ; then
PKG_CHECK_MODULES(LIBOSMOSCCP, libosmo-sccp >= 0.0.2)
fi
AM_CONDITIONAL(BUILD_NAT, test "x$osmo_ac_build_nat" = "xyes")
AC_SUBST(osmo_ac_build_nat)
# Enable/disable the BSC?
AC_ARG_ENABLE([osmo-bsc], [AS_HELP_STRING([--enable-osmo-bsc], [Build the Osmo BSC])],
[osmo_ac_build_bsc="$enableval"],[osmo_ac_build_bsc="no"])
if test "$osmo_ac_build_bsc" = "yes" ; then
PKG_CHECK_MODULES(LIBOSMOSCCP, libosmo-sccp >= 0.0.6)
fi
AM_CONDITIONAL(BUILD_BSC, test "x$osmo_ac_build_bsc" = "xyes")
AC_SUBST(osmo_ac_build_bsc)
# Enable/disable smpp support in the msc?
AC_ARG_ENABLE([smpp], [AS_HELP_STRING([--enable-smpp], [Build the SMPP interface])],
[osmo_ac_build_smpp="$enableval"],[osmo_ac_build_smpp="no"])
@ -105,23 +86,6 @@ fi
AM_CONDITIONAL(BUILD_IU, test "x$osmo_ac_iu" = "xyes")
AC_SUBST(osmo_ac_iu)
found_libgtp=yes
PKG_CHECK_MODULES(LIBGTP, libgtp >= 0.92, , found_libgtp=no)
AM_CONDITIONAL(HAVE_LIBGTP, test "$found_libgtp" = yes)
AC_SUBST(found_libgtp)
found_libcares=yes
PKG_CHECK_MODULES([LIBCARES], [libcares], [], [found_libcares=no])
AM_CONDITIONAL(HAVE_LIBCARES, test "$found_libcares" = yes)
AC_SUBST(found_libcares)
found_libgtp_and_libcares=no
if test "$found_libgtp" = "yes" -a "$found_libcares" = "yes"; then
found_libgtp_and_libcares=yes
fi
AC_SUBST(found_libgtp_and_libcares)
dnl checks for header files
AC_HEADER_STDC
AC_CHECK_HEADERS(dbi/dbd.h,,AC_MSG_ERROR(DBI library is not installed))
@ -227,40 +191,15 @@ AC_OUTPUT(
include/openbsc/Makefile
include/Makefile
src/Makefile
src/libtrau/Makefile
src/libbsc/Makefile
src/libmsc/Makefile
src/libvlr/Makefile
src/libcommon/Makefile
src/libfilter/Makefile
src/libcommon-cs/Makefile
src/osmo-msc/Makefile
src/osmo-bsc/Makefile
src/osmo-bsc_nat/Makefile
src/ipaccess/Makefile
src/utils/Makefile
src/gprs/Makefile
tests/Makefile
tests/atlocal
tests/gsm0408/Makefile
tests/channel/Makefile
tests/bsc/Makefile
tests/bsc-nat/Makefile
tests/bsc-nat-trie/Makefile
tests/gprs/Makefile
tests/gbproxy/Makefile
tests/abis/Makefile
tests/smpp/Makefile
tests/trau/Makefile
tests/sgsn/Makefile
tests/subscr/Makefile
tests/oap/Makefile
tests/gtphub/Makefile
tests/xid/Makefile
tests/sndcp_xid/Makefile
tests/slhc/Makefile
tests/v42bis/Makefile
tests/nanobts_omlattr/Makefile
tests/sms_queue/Makefile
tests/msc_vlr/Makefile
doc/Makefile

View File

@ -1,2 +0,0 @@
CONFIG_FILE="/etc/osmocom/osmo-gtphub.cfg"

View File

@ -1 +0,0 @@
openbsc/doc/examples/osmo-gtphub

View File

@ -1,150 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: osmo-gtphub
# Required-Start: $network $local_fs
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Osmocom GTP hub
# Description: Osmocom GTP hub
### END INIT INFO
# Author: Neels Hofmeyr <nhofmeyr@sysmocom.de>
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
NAME=osmo-gtphub # Introduce the short server's name here
DESC="Osmocom GTP hub" # Introduce a short description here
DAEMON=/usr/bin/osmo-gtphub # Introduce the server's location here
SCRIPTNAME=/etc/init.d/osmo-gtphub
# Exit if the package is not installed
[ -x $DAEMON ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/osmo-gtphub ] && . /etc/default/osmo-gtphub
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
DAEMON_ARGS="$DAEMON_ARGS -D -c $CONFIG_FILE"
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --exec $DAEMON -- \
$DAEMON_ARGS \
|| return 2
# Add code here, if necessary, that waits for the process to be ready
# to handle requests from services started subsequently which depend
# on this one. As a last resort, sleep for some time.
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
return "$RETVAL"
}
#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
start-stop-daemon --stop --signal 1 --quiet $PIDFILE --name $NAME
return 0
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
#reload|force-reload)
#
# If do_reload() is not implemented then leave this commented out
# and leave 'force-reload' as an alias for 'restart'.
#
#log_daemon_msg "Reloading $DESC" "$NAME"
#do_reload
#log_end_msg $?
#;;
restart|force-reload)
#
# If the "reload" option is implemented then remove the
# 'force-reload' alias
#
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
:

View File

@ -1 +0,0 @@
/usr/bin/osmo-gtphub

View File

@ -1,2 +0,0 @@
/usr/bin/bs11_config
/usr/bin/isdnsync

View File

@ -1,153 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: osmocom-bsc-nat
# Required-Start: $network $local_fs
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Osmocom GSM network-in-a-box
# Description: A minimal implementation of the GSM Base Station Controller,
# Mobile Switching Center, Home Location regster and all other
# components to run a self-contained GSM network.
### END INIT INFO
# Author: Harald Welte <laforge@gnumonks.org>
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
NAME=osmo-bsc_nat # Introduce the short server's name here
DESC="Osmocom GSM BSC Multiplexer (NAT)" # Introduce a short description here
DAEMON=/usr/bin/osmo-bsc_nat # Introduce the server's location here
SCRIPTNAME=/etc/init.d/osmocom-bsc-nat
CONFIG_FILE=/etc/osmocom/osmocom-bsc-nat.cfg
# Exit if the package is not installed
[ -x $DAEMON ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/osmocom-bsc-nat ] && . /etc/default/osmocom-bsc-nat
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
DAEMON_ARGS="-D -c $CONFIG_FILE"
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --exec $DAEMON -- \
$DAEMON_ARGS \
|| return 2
# Add code here, if necessary, that waits for the process to be ready
# to handle requests from services started subsequently which depend
# on this one. As a last resort, sleep for some time.
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
return "$RETVAL"
}
#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
start-stop-daemon --stop --signal 1 --quiet $PIDFILE --name $NAME
return 0
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
#reload|force-reload)
#
# If do_reload() is not implemented then leave this commented out
# and leave 'force-reload' as an alias for 'restart'.
#
#log_daemon_msg "Reloading $DESC" "$NAME"
#do_reload
#log_end_msg $?
#;;
restart|force-reload)
#
# If the "reload" option is implemented then remove the
# 'force-reload' alias
#
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
:

View File

@ -1 +0,0 @@
/usr/bin/osmo-bsc_nat

View File

@ -1 +0,0 @@
openbsc/doc/examples/osmo-bsc_mgcp

View File

@ -1,2 +0,0 @@
/usr/bin/osmo-bsc_mgcp
/usr/bin/osmo-bsc

View File

@ -1,151 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: osmo-gbproxy
# Required-Start: $network $local_fs
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Osmocom GBproxy
# Description: A tool to proxy the GPRS Gb interface.
### END INIT INFO
# Author: Harald Welte <laforge@gnumonks.org>
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
NAME=osmo-gbproxy # Introduce the short server's name here
DESC="Osmocom GBProxy" # Introduce a short description here
DAEMON=/usr/bin/osmo-gbproxy # Introduce the server's location here
SCRIPTNAME=/etc/init.d/osmocom-gbproxy
CONFIG_FILE=/etc/osmocom/osmocom-gbproxy.cfg
# Exit if the package is not installed
[ -x $DAEMON ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/osmocom-gbproxy ] && . /etc/default/osmocom-gbproxy
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
DAEMON_ARGS="-D -c $CONFIG_FILE"
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --exec $DAEMON -- \
$DAEMON_ARGS \
|| return 2
# Add code here, if necessary, that waits for the process to be ready
# to handle requests from services started subsequently which depend
# on this one. As a last resort, sleep for some time.
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
return "$RETVAL"
}
#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
start-stop-daemon --stop --signal 1 --quiet $PIDFILE --name $NAME
return 0
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
#reload|force-reload)
#
# If do_reload() is not implemented then leave this commented out
# and leave 'force-reload' as an alias for 'restart'.
#
#log_daemon_msg "Reloading $DESC" "$NAME"
#do_reload
#log_end_msg $?
#;;
restart|force-reload)
#
# If the "reload" option is implemented then remove the
# 'force-reload' alias
#
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
:

View File

@ -1 +0,0 @@
/usr/bin/osmo-gbproxy

View File

@ -1,3 +0,0 @@
/usr/bin/ipaccess-config
/usr/bin/abisip-find
/usr/bin/ipaccess-proxy

View File

@ -1,2 +0,0 @@
CONFIG_FILE="/etc/osmocom/osmo-sgsn.cfg"

View File

@ -1 +0,0 @@
openbsc/doc/examples/osmo-sgsn

View File

@ -1,150 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: osmo-sgsn
# Required-Start: $network $local_fs
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Osmocom Serving GPRS Support Node
# Description: Osmocom Serving GPRS Support Node
### END INIT INFO
# Author: Harald Welte <laforge@gnumonks.org>
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
NAME=osmo-sgsn # Introduce the short server's name here
DESC="Osmocom Serving GPRS Support Node" # Introduce a short description here
DAEMON=/usr/bin/osmo-sgsn # Introduce the server's location here
SCRIPTNAME=/etc/init.d/osmocom-sgsn
# Exit if the package is not installed
[ -x $DAEMON ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/osmocom-sgsn ] && . /etc/default/osmocom-sgsn
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
DAEMON_ARGS="$DAEMON_ARGS -D -c $CONFIG_FILE"
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --exec $DAEMON -- \
$DAEMON_ARGS \
|| return 2
# Add code here, if necessary, that waits for the process to be ready
# to handle requests from services started subsequently which depend
# on this one. As a last resort, sleep for some time.
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
return "$RETVAL"
}
#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
start-stop-daemon --stop --signal 1 --quiet $PIDFILE --name $NAME
return 0
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
#reload|force-reload)
#
# If do_reload() is not implemented then leave this commented out
# and leave 'force-reload' as an alias for 'restart'.
#
#log_daemon_msg "Reloading $DESC" "$NAME"
#do_reload
#log_end_msg $?
#;;
restart|force-reload)
#
# If the "reload" option is implemented then remove the
# 'force-reload' alias
#
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
:

View File

@ -1 +0,0 @@
/usr/bin/osmo-sgsn

View File

@ -1,31 +0,0 @@
The Siemens BS-11 supports the following additional GSM 12.21 OML operations:
CREATE OBJECT
abis_om_fom_hdr.obj_class can be
A3:
A5: ALCO, BBSIG, CCLK, GPSU, LI, PA
A8: EnvaBTSE
A9: BPORT
the abis_om_obj_inst.trx_nr field indicates the index of object, whereas the
abis_om_fom_hdr.bts_nr indicates the type of the object.
enum abis_bs11_objtype {
BS11_OBJ_ALCO = 0x01,
BS11_OBJ_BBSIG = 0x02, /* obj_class: 0,1 */
BS11_OBJ_TRX1 = 0x03, /* only DEACTIVATE TRX1 */
BS11_OBJ_CCLK = 0x04,
BS11_OBJ_GPSU = 0x06,
BS11_OBJ_LI = 0x07,
BS11_OBJ_PA = 0x09, /* obj_class: 0, 1*/
};
In case of CREATE ENVABTSE, the abis_om_obj_inst.trx_nr indicates the EnvaBTSEx
number.
In case of A9 (CREAETE BPORT), the abis_om_obj_inst.bts_nr indicates which BPORT
shall be used.

View File

@ -1,95 +0,0 @@
GSM 04.08 7.1.7 / 9.1.7 RR CHANNEL RELESE
RSL 08.58 3.4 / ? RLL Link Release Request
RSL 08.58 4.6 / 8.4.5 DEACTivate SACCH
* Deactivate SACCH according to Channel Release Proc 04.08
* to be sent after RR CHANNEL RELEASE is sent to MS
RSL 08.58 4.7 / 8.4.14 RF CHANnel RELease
* tells the BTS to release a radio channel
* "when an activated radio channel is no longer needed"
* BTS responds with RF CHANnel RELease ACKnowledge
GSM 04.08 3.4.13: RR connection release procedure
* network sends RR CHANNEL RELEASE to MS on the DCCH
* start T3109
* deactivate SACCH
* MS disconnects main signalling link (by sending DISC)
* all other data links are disconnected by local end link release
* network receives DISC (BTS sends RLL REL IND to BSC)
* stop T3109
* start T3111
* when T3111 times out, the network can reuse the channls
* if T3109 times out, the network deactivates the channels
and can reuse them
* this probably means simply RF CHANnel RELease
== Implementation in OpenBSC ==
There are two possible reasons a gsm_subscriber_connection
will be released. One is a network failure, the other is
the completion of an operation/transaction.
=== Failure ===
The BSC API will call the gsm_04_08.c:gsm0408_clear_request callback
and the MSC part will release all transactions, operations and such
and the channels will be released as error case.
=== Success ===
Every time an 'operation' or 'transaction' is finished msc_release_connection
will be called and it will determine if the gsm_subscriber_connection can
be released.
In case it can be released bsc_api.c:gsm0808_clear will be called
which will release all lchan's associated with the connection. For the
primary channel a SACH Deactivate will be send with the release
reason NORMAL RELEASE.
bsc_api.c:gsm0808_clear
* Release a channel used for handover
* Release the primary lchan with normal release, SACH deactivate
chan_alloc.c:lchan_release(chan, sacch_deactivate, reason)
* Start the release procedure. It is working in steps with callbacks
coming from the abis_rsl.c code.
* Release all SAPI's > 0 as local end (The BTS should send a
REL_CONF a message)
* Send SACH Deactivate on SAPI=0 if required.
* Start T3109 (stop it when the main signalling link is disconnected)
or when the channel released. On timeout start the error handling.
* abis_rsl.c schedules the RSL_MT_RF_CHAN_REL once all SAPI's are
released and after T3111 has timed out or there is an error.
RX of RELease INDication:
* Calls internal rsl_handle_release which might release the RF.
RX of RELease CONFirmation:
* Calls internal rsl_handle_release which might release the RF.
* RX of RF_CHAN_REL_ACK
* call lchan_free()
=== Integration with SMS ===
* RX of CP_ERROR or unimplemented MT
* trigger trans_free() which will msc_release_connection()
* CP TC1* expired while waiting for CP-ACK
* trigger trans_free() which will msc_release_connection()
* RX of RP_ERROR
* trigger trans_free() which will msc_release_connection()
* TX of CP-ACK in MT DELIVER
* trigger trans_free() which will msc_release_connection()
* RX of CP-ACK in MO SUBMIT
* trigger trans_free() which will msc_release_connection()

View File

@ -1,101 +0,0 @@
!
! OsmoBSC (0.9.14+gitr1+3d331c0062bb0c9694dbd4d1eab7adc58138c3ae) configuration saved from vty
!!
password foo
!
!
line vty
no login
!
e1_input
e1_line 0 driver ipa
network
network country code 1
mobile network code 1
short name OsmoBSC
long name OsmoBSC
auth policy closed
location updating reject cause 13
encryption a5 0
neci 1
paging any use tch 0
rrlp mode none
mm info 1
handover 0
handover window rxlev averaging 10
handover window rxqual averaging 1
handover window rxlev neighbor averaging 10
handover power budget interval 6
handover power budget hysteresis 3
handover maximum distance 9999
bts 0
type nanobts
band DCS1800
cell_identity 0
location_area_code 1
training_sequence_code 7
base_station_id_code 63
ms max power 15
cell reselection hysteresis 4
rxlev access min 0
channel allocator ascending
rach tx integer 9
rach max transmission 7
dtx uplink force
dtx downlink
ip.access unit_id 0 0
oml ip.access stream_id 255 line 0
neighbor-list mode manual-si5
neighbor-list add arfcn 100
neighbor-list add arfcn 200
si5 neighbor-list add arfcn 10
si5 neighbor-list add arfcn 20
gprs mode none
trx 0
rf_locked 0
arfcn 871
nominal power 23
max_power_red 20
rsl e1 tei 0
timeslot 0
phys_chan_config CCCH+SDCCH4
hopping enabled 0
timeslot 1
phys_chan_config TCH/F
hopping enabled 0
timeslot 2
phys_chan_config TCH/F
hopping enabled 0
timeslot 3
phys_chan_config TCH/F
hopping enabled 0
timeslot 4
phys_chan_config TCH/F
hopping enabled 0
timeslot 5
phys_chan_config TCH/F
hopping enabled 0
timeslot 6
phys_chan_config TCH/F
hopping enabled 0
timeslot 7
phys_chan_config TCH/F
hopping enabled 0
cs7 instance 1
point-code 3.0.0
sccp-address bsc_local
point-code 3.0.0
sccp-address msc_remote
point-code 1.0.0
msc
bsc-addr bsc_local
msc-addr msc_remote
ip.access rtp-base 4000
timeout-ping 20
timeout-pong 5
dest 192.168.100.11 6666 0
access-list-name msc-list
no access-list-name
bsc
no access-list-name
access-list-name bsc-list

View File

@ -1,19 +0,0 @@
!
! MGCP configuration hand edited
! !
password foo
!
line vty
no login
!
mgcp
!local ip 10.23.24.2
!bts ip 10.24.24.1
!bind ip 10.23.24.1
bind port 2427
rtp base 4000
rtp force-ptime 20
sdp audio payload number 98
sdp audio payload name AMR/8000
number endpoints 31
no rtcp-omit

View File

@ -1 +0,0 @@
678012512671923:6:6:

View File

@ -1,66 +0,0 @@
!
! OsmoBSCNAT (0.12.0.266-2daa9) configuration saved from vty
!!
!
log stderr
logging filter all 1
logging color 1
logging timestamp 0
logging level all debug
logging level rll notice
logging level cc notice
logging level mm notice
logging level rr notice
logging level rsl notice
logging level nm info
logging level mncc notice
logging level pag notice
logging level meas notice
logging level sccp notice
logging level msc notice
logging level mgcp notice
logging level ho notice
logging level db notice
logging level ref notice
logging level gprs debug
logging level ns info
logging level bssgp debug
logging level llc debug
logging level sndcp debug
logging level nat notice
logging level ctrl notice
logging level smpp debug
logging level lglobal notice
logging level llapd notice
logging level linp notice
logging level lmux notice
logging level lmi notice
logging level lmib notice
logging level lsms notice
!
line vty
no login
!
mgcp
bind ip 0.0.0.0
bind port 2427
rtp bts-base 4000
rtp net-base 16000
rtp ip-dscp 0
no rtcp-omit
sdp audio-payload number 126
sdp audio-payload name AMR/8000
loop 0
number endpoints 1
call-agent ip 127.0.0.1
rtp transcoder-base 0
transcoder-remote-base 4000
nat
msc ip 127.0.0.1
msc port 5000
timeout auth 2
timeout ping 20
timeout pong 5
ip-dscp 0
bscs-config-file bscs.cfg
access-list bla imsi-allow ^11$

View File

@ -1,44 +0,0 @@
!
! OsmoGbProxy (UNKNOWN) configuration saved from vty
!!
!
log stderr
logging filter all 1
logging color 1
logging timestamp 0
logging level all debug
logging level gprs debug
logging level ns info
logging level bssgp debug
logging level lglobal notice
logging level llapd notice
logging level linp notice
logging level lmux notice
logging level lmi notice
logging level lmib notice
logging level lsms notice
!
line vty
no login
!
ns
nse 666 nsvci 666
nse 666 remote-role sgsn
! nse 666 encapsulation framerelay-gre
! nse 666 remote-ip 172.16.1.70
! nse 666 fr-dlci 666
timer tns-block 3
timer tns-block-retries 3
timer tns-reset 3
timer tns-reset-retries 3
timer tns-test 30
timer tns-alive 3
timer tns-alive-retries 10
encapsulation udp local-port 23000
! encapsulation framerelay-gre enabled 1
gbproxy
sgsn nsei 666
core-mobile-country-code 666
core-mobile-network-code 6
core-access-point-name none match-imsi ^666066|^66607
tlli-list max-length 200

View File

@ -1,25 +0,0 @@
!
! Osmocom Gb Proxy (0.9.0.404-6463) configuration saved from vty
!!
!
line vty
no login
!
gbproxy
sgsn nsei 101
ns
nse 101 nsvci 101
nse 101 remote-role sgsn
nse 101 encapsulation udp
nse 101 remote-ip 192.168.100.239
nse 101 remote-port 7777
timer tns-block 3
timer tns-block-retries 3
timer tns-reset 3
timer tns-reset-retries 3
timer tns-test 30
timer tns-alive 3
timer tns-alive-retries 10
encapsulation framerelay-gre enabled 0
encapsulation framerelay-gre local-ip 0.0.0.0
encapsulation udp local-port 23000

View File

@ -1,90 +0,0 @@
Here is a simple setup to test GTPHub operations. The IP addresses picked will
work well only on a system that creates local addresses (127.0.0.123) on the
fly (like linux) -- you may pick of course different IP addresses.
Overview of the example setup:
sgsnemu gtphub ggsn
127.0.0.1 <--> 127.0.0.3 127.0.0.4 <--> 127.0.0.2
Prerequisites: openggsn.
Have a local directory where you store config files and from which you launch
the GSNs and the hub (they will store restart counter files in that dir).
In it, have these config files:
ggsn.conf:
# GGSN local address
listen 127.0.0.2
# End User Addresses are picked from this range
net 10.23.42.0/24
pcodns1 8.8.8.8
logfile /tmp/foo
gtphub.conf:
gtphub
bind-to-sgsns 127.0.0.3
bind-to-ggsns 127.0.0.4
ggsn-proxy 127.0.0.2
end
(
You may omit the ggsn-proxy if GRX ares is working, or if you add the GRX
address and GGSN IP address to /etc/hosts something like:
127.0.0.2 internet.mnc070.mcc901.gprs
)
Once the config files are in place, start the programs, in separate terminals.
GGSN and SGSN need to be started with root priviliges to be able to create tun
interfaces. GTPHub may run as unprivileged user.
The LD_LIBRARY_PATH below may be needed if OpenGGSN installed to /usr/local.
1. GGSN:
sudo -s
cd <your-test-dir>
LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/ggsn -f -c ./ggsn.conf
2. GTPHub:
cd <your-test-dir>
path/to/openbsc/openbsc/src/gprs/osmo-gtphub -c gtphub.conf #-e 1 #for DEBUG level
3. SGSN tests:
sudo -s
cd <your-test-dir>
/usr/local/bin/sgsnemu --createif -l 127.0.0.1 -r 127.0.0.3 --imsi 420001214365100 --contexts=3
Add more SGSNs using different IMSIs and local ports (if the same IMSI is used,
the GGSN will reuse TEIs and tunnels will be discarded automatically):
/usr/local/bin/sgsnemu --createif -l 127.0.0.11 -r 127.0.0.3 --imsi 420001214365300 --contexts=3
This shows the basic setup of GTPHub. Testing internet traffic via sgsnemu
still needs some effort to announce a mobile subscriber or the like (I have
used a real BTS, osmo-sgsn and a testing SIM in a web phone, instead).
The core capability of GTPHub is to manage more than two GSNs, e.g. an SGSN
contacting various GGSNs over the single GTPHub link. You would configure the
SGSN to use one fixed GGSN (sending to gtphub) and gtphub will resolve the
GGSNs once it has received the messages. So the SGSN may be behind NAT (add
"sgsn-use-sender" to gtphub.conf) and communicate to various GGSNs over a
single link to gtphub.
I hope this helps to get you going.
Any suggestions/patches are welcome!
~Neels

View File

@ -1,25 +0,0 @@
!
! Osmocom gtphub configuration
!
! This file is used for VTY tests, referenced by openbsc/osmoappdesc.py
! For the test, try to use most config commands.
!
line vty
no login
gtphub
! Local addresses to listen on and send from, both on one interface.
! The side towards SGSN uses nonstandard ports.
bind-to-sgsns ctrl 127.0.0.1 12123 user 127.0.0.1 12153
! The GGSN side with standard ports.
bind-to-ggsns 127.0.0.1
! Proxy: unconditionally direct all traffic to...
sgsn-proxy 127.0.0.4
! Proxy with nonstandard ports or separate IPs:
ggsn-proxy ctrl 127.0.0.3 2123 user 127.0.0.5 2152
! Add a name server for GGSN resolution
grx-dns-add 192.168.0.1

View File

@ -1,25 +0,0 @@
!
! Osmocom gtphub configuration
!
line vty
no login
gtphub
! Local addresses to listen on and send from, each on standard ports
! 2123 and 2152. Setting these addresses is mandatory.
bind-to-sgsns 127.0.0.1
bind-to-ggsns 127.0.0.2
! Local nonstandard ports or separate IPs:
!bind-to-sgsns ctrl 127.0.0.1 2342 user 127.0.0.1 4223
! Proxy: unconditionally direct all traffic to...
!ggsn-proxy 127.0.0.3
!sgsn-proxy 127.0.0.4
! Proxy with nonstandard ports or separate IPs:
!ggsn-proxy ctrl 127.0.0.3 2123 user 127.0.0.5 2152
! Add a name server for GGSN resolution
!grx-dns-add 192.168.0.1

View File

@ -1,29 +0,0 @@
!
! Osmocom SGSN configuration
!
!
line vty
no login
!
sgsn
gtp local-ip 127.0.0.1
ggsn 0 remote-ip 127.0.0.2
ggsn 0 gtp-version 1
auth-policy remote
gsup remote-ip 127.0.0.1
gsup remote-port 4222
!
ns
timer tns-block 3
timer tns-block-retries 3
timer tns-reset 3
timer tns-reset-retries 3
timer tns-test 30
timer tns-alive 3
timer tns-alive-retries 10
encapsulation udp local-ip 127.0.0.1
encapsulation udp local-port 23000
encapsulation framerelay-gre enabled 0
!
bssgp
!

View File

@ -1,22 +0,0 @@
oml interface design notes
problems:
* there is no way how to tag a command sent to the BTS, with the response
having the same tag to identify the originator of the command
* therefore, we can have e.g. both the BSC and the OML interface send a
SET ATTRIBUTE message, where the responses would end up at the wrong
query.
* The BTS has 10s to ACK/NACK a command. We do not run any timers.
the only possible solutions i can imagine:
* have some kind of exclusive locking, where the OML interface gets blocked
from the BSC and is exclusively assigned to the OML console until all commands
of the OML console have terminated. This can either be done explicitly
dynamically or on demand
* use the OML interface synchronously, i.e. always wait for the response from
the BTS before
* unilateral / unsolicited messages need to be broadcasted to both the BSC and
the OML console

View File

@ -1,8 +1,3 @@
SUBDIRS = \
openbsc \
$(NULL)
noinst_HEADERS = \
mISDNif.h \
compat_af_isdn.h \
$(NULL)

View File

@ -1,39 +0,0 @@
#ifdef MISDN_OLD_AF_COMPATIBILITY
#undef AF_ISDN
#undef PF_ISDN
extern int AF_ISDN;
#define PF_ISDN AF_ISDN
int AF_ISDN;
#endif
extern void init_af_isdn(void);
#ifdef AF_COMPATIBILITY_FUNC
#ifdef MISDN_OLD_AF_COMPATIBILITY
void init_af_isdn(void)
{
int s;
/* test for new value */
AF_ISDN = 34;
s = socket(AF_ISDN, SOCK_RAW, ISDN_P_BASE);
if (s >= 0) {
close(s);
return;
}
AF_ISDN = 27;
s = socket(AF_ISDN, SOCK_RAW, ISDN_P_BASE);
if (s >= 0) {
close(s);
return;
}
}
#else
void init_af_isdn(void)
{
}
#endif
#endif

View File

@ -1,387 +0,0 @@
/*
*
* Author Karsten Keil <kkeil@novell.com>
*
* Copyright 2008 by Karsten Keil <kkeil@novell.com>
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* version 2.1 as published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU LESSER GENERAL PUBLIC LICENSE for more details.
*
*/
#ifndef mISDNIF_H
#define mISDNIF_H
#include <stdarg.h>
#ifdef linux
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/socket.h>
#else
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/socket.h>
#endif
/*
* ABI Version 32 bit
*
* <8 bit> Major version
* - changed if any interface become backwards incompatible
*
* <8 bit> Minor version
* - changed if any interface is extended but backwards compatible
*
* <16 bit> Release number
* - should be incremented on every checkin
*/
#define MISDN_MAJOR_VERSION 1
#define MISDN_MINOR_VERSION 1
#define MISDN_RELEASE 20
/* primitives for information exchange
* generell format
* <16 bit 0 >
* <8 bit command>
* BIT 8 = 1 LAYER private
* BIT 7 = 1 answer
* BIT 6 = 1 DATA
* <8 bit target layer mask>
*
* Layer = 00 is reserved for general commands
Layer = 01 L2 -> HW
Layer = 02 HW -> L2
Layer = 04 L3 -> L2
Layer = 08 L2 -> L3
* Layer = FF is reserved for broadcast commands
*/
#define MISDN_CMDMASK 0xff00
#define MISDN_LAYERMASK 0x00ff
/* generell commands */
#define OPEN_CHANNEL 0x0100
#define CLOSE_CHANNEL 0x0200
#define CONTROL_CHANNEL 0x0300
#define CHECK_DATA 0x0400
/* layer 2 -> layer 1 */
#define PH_ACTIVATE_REQ 0x0101
#define PH_DEACTIVATE_REQ 0x0201
#define PH_DATA_REQ 0x2001
#define MPH_ACTIVATE_REQ 0x0501
#define MPH_DEACTIVATE_REQ 0x0601
#define MPH_INFORMATION_REQ 0x0701
#define PH_CONTROL_REQ 0x0801
/* layer 1 -> layer 2 */
#define PH_ACTIVATE_IND 0x0102
#define PH_ACTIVATE_CNF 0x4102
#define PH_DEACTIVATE_IND 0x0202
#define PH_DEACTIVATE_CNF 0x4202
#define PH_DATA_IND 0x2002
#define PH_DATA_E_IND 0x3002
#define MPH_ACTIVATE_IND 0x0502
#define MPH_DEACTIVATE_IND 0x0602
#define MPH_INFORMATION_IND 0x0702
#define PH_DATA_CNF 0x6002
#define PH_CONTROL_IND 0x0802
#define PH_CONTROL_CNF 0x4802
/* layer 3 -> layer 2 */
#define DL_ESTABLISH_REQ 0x1004
#define DL_RELEASE_REQ 0x1104
#define DL_DATA_REQ 0x3004
#define DL_UNITDATA_REQ 0x3104
#define DL_INFORMATION_REQ 0x0004
/* layer 2 -> layer 3 */
#define DL_ESTABLISH_IND 0x1008
#define DL_ESTABLISH_CNF 0x5008
#define DL_RELEASE_IND 0x1108
#define DL_RELEASE_CNF 0x5108
#define DL_DATA_IND 0x3008
#define DL_UNITDATA_IND 0x3108
#define DL_INFORMATION_IND 0x0008
/* intern layer 2 managment */
#define MDL_ASSIGN_REQ 0x1804
#define MDL_ASSIGN_IND 0x1904
#define MDL_REMOVE_REQ 0x1A04
#define MDL_REMOVE_IND 0x1B04
#define MDL_STATUS_UP_IND 0x1C04
#define MDL_STATUS_DOWN_IND 0x1D04
#define MDL_STATUS_UI_IND 0x1E04
#define MDL_ERROR_IND 0x1F04
#define MDL_ERROR_RSP 0x5F04
/* DL_INFORMATION_IND types */
#define DL_INFO_L2_CONNECT 0x0001
#define DL_INFO_L2_REMOVED 0x0002
/* PH_CONTROL types */
/* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */
#define DTMF_TONE_VAL 0x2000
#define DTMF_TONE_MASK 0x007F
#define DTMF_TONE_START 0x2100
#define DTMF_TONE_STOP 0x2200
#define DTMF_HFC_COEF 0x4000
#define DSP_CONF_JOIN 0x2403
#define DSP_CONF_SPLIT 0x2404
#define DSP_RECEIVE_OFF 0x2405
#define DSP_RECEIVE_ON 0x2406
#define DSP_ECHO_ON 0x2407
#define DSP_ECHO_OFF 0x2408
#define DSP_MIX_ON 0x2409
#define DSP_MIX_OFF 0x240a
#define DSP_DELAY 0x240b
#define DSP_JITTER 0x240c
#define DSP_TXDATA_ON 0x240d
#define DSP_TXDATA_OFF 0x240e
#define DSP_TX_DEJITTER 0x240f
#define DSP_TX_DEJ_OFF 0x2410
#define DSP_TONE_PATT_ON 0x2411
#define DSP_TONE_PATT_OFF 0x2412
#define DSP_VOL_CHANGE_TX 0x2413
#define DSP_VOL_CHANGE_RX 0x2414
#define DSP_BF_ENABLE_KEY 0x2415
#define DSP_BF_DISABLE 0x2416
#define DSP_BF_ACCEPT 0x2416
#define DSP_BF_REJECT 0x2417
#define DSP_PIPELINE_CFG 0x2418
#define HFC_VOL_CHANGE_TX 0x2601
#define HFC_VOL_CHANGE_RX 0x2602
#define HFC_SPL_LOOP_ON 0x2603
#define HFC_SPL_LOOP_OFF 0x2604
/* DSP_TONE_PATT_ON parameter */
#define TONE_OFF 0x0000
#define TONE_GERMAN_DIALTONE 0x0001
#define TONE_GERMAN_OLDDIALTONE 0x0002
#define TONE_AMERICAN_DIALTONE 0x0003
#define TONE_GERMAN_DIALPBX 0x0004
#define TONE_GERMAN_OLDDIALPBX 0x0005
#define TONE_AMERICAN_DIALPBX 0x0006
#define TONE_GERMAN_RINGING 0x0007
#define TONE_GERMAN_OLDRINGING 0x0008
#define TONE_AMERICAN_RINGPBX 0x000b
#define TONE_GERMAN_RINGPBX 0x000c
#define TONE_GERMAN_OLDRINGPBX 0x000d
#define TONE_AMERICAN_RINGING 0x000e
#define TONE_GERMAN_BUSY 0x000f
#define TONE_GERMAN_OLDBUSY 0x0010
#define TONE_AMERICAN_BUSY 0x0011
#define TONE_GERMAN_HANGUP 0x0012
#define TONE_GERMAN_OLDHANGUP 0x0013
#define TONE_AMERICAN_HANGUP 0x0014
#define TONE_SPECIAL_INFO 0x0015
#define TONE_GERMAN_GASSENBESETZT 0x0016
#define TONE_GERMAN_AUFSCHALTTON 0x0016
/* MPH_INFORMATION_IND */
#define L1_SIGNAL_LOS_OFF 0x0010
#define L1_SIGNAL_LOS_ON 0x0011
#define L1_SIGNAL_AIS_OFF 0x0012
#define L1_SIGNAL_AIS_ON 0x0013
#define L1_SIGNAL_RDI_OFF 0x0014
#define L1_SIGNAL_RDI_ON 0x0015
#define L1_SIGNAL_SLIP_RX 0x0020
#define L1_SIGNAL_SLIP_TX 0x0021
/*
* protocol ids
* D channel 1-31
* B channel 33 - 63
*/
#define ISDN_P_NONE 0
#define ISDN_P_BASE 0
#define ISDN_P_TE_S0 0x01
#define ISDN_P_NT_S0 0x02
#define ISDN_P_TE_E1 0x03
#define ISDN_P_NT_E1 0x04
#define ISDN_P_TE_UP0 0x05
#define ISDN_P_NT_UP0 0x06
#define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \
(p == ISDN_P_TE_UP0) || (p == ISDN_P_LAPD_TE))
#define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \
(p == ISDN_P_NT_UP0) || (p == ISDN_P_LAPD_NT))
#define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0))
#define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1))
#define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0))
#define ISDN_P_LAPD_TE 0x10
#define ISDN_P_LAPD_NT 0x11
#define ISDN_P_B_MASK 0x1f
#define ISDN_P_B_START 0x20
#define ISDN_P_B_RAW 0x21
#define ISDN_P_B_HDLC 0x22
#define ISDN_P_B_X75SLP 0x23
#define ISDN_P_B_L2DTMF 0x24
#define ISDN_P_B_L2DSP 0x25
#define ISDN_P_B_L2DSPHDLC 0x26
#define OPTION_L2_PMX 1
#define OPTION_L2_PTP 2
#define OPTION_L2_FIXEDTEI 3
#define OPTION_L2_CLEANUP 4
/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */
#define MISDN_MAX_IDLEN 20
struct mISDNhead {
unsigned int prim;
unsigned int id;
} __attribute__((packed));
#define MISDN_HEADER_LEN sizeof(struct mISDNhead)
#define MAX_DATA_SIZE 2048
#define MAX_DATA_MEM (MAX_DATA_SIZE + MISDN_HEADER_LEN)
#define MAX_DFRAME_LEN 260
#define MISDN_ID_ADDR_MASK 0xFFFF
#define MISDN_ID_TEI_MASK 0xFF00
#define MISDN_ID_SAPI_MASK 0x00FF
#define MISDN_ID_TEI_ANY 0x7F00
#define MISDN_ID_ANY 0xFFFF
#define MISDN_ID_NONE 0xFFFE
#define GROUP_TEI 127
#define TEI_SAPI 63
#define CTRL_SAPI 0
#define MISDN_MAX_CHANNEL 127
#define MISDN_CHMAP_SIZE ((MISDN_MAX_CHANNEL + 1) >> 3)
#define SOL_MISDN 0
struct sockaddr_mISDN {
sa_family_t family;
unsigned char dev;
unsigned char channel;
unsigned char sapi;
unsigned char tei;
};
struct mISDNversion {
unsigned char major;
unsigned char minor;
unsigned short release;
};
#define MAX_DEVICE_ID 63
struct mISDN_devinfo {
u_int id;
u_int Dprotocols;
u_int Bprotocols;
u_int protocol;
u_char channelmap[MISDN_CHMAP_SIZE];
u_int nrbchan;
char name[MISDN_MAX_IDLEN];
};
struct mISDN_devrename {
u_int id;
char name[MISDN_MAX_IDLEN];
};
struct ph_info_ch {
int32_t protocol;
int64_t Flags;
};
struct ph_info_dch {
struct ph_info_ch ch;
int16_t state;
int16_t num_bch;
};
struct ph_info {
struct ph_info_dch dch;
struct ph_info_ch bch[];
};
/* timer device ioctl */
#define IMADDTIMER _IOR('I', 64, int)
#define IMDELTIMER _IOR('I', 65, int)
/* socket ioctls */
#define IMGETVERSION _IOR('I', 66, int)
#define IMGETCOUNT _IOR('I', 67, int)
#define IMGETDEVINFO _IOR('I', 68, int)
#define IMCTRLREQ _IOR('I', 69, int)
#define IMCLEAR_L2 _IOR('I', 70, int)
#define IMSETDEVNAME _IOR('I', 71, struct mISDN_devrename)
static inline int
test_channelmap(u_int nr, u_char *map)
{
if (nr <= MISDN_MAX_CHANNEL)
return map[nr >> 3] & (1 << (nr & 7));
else
return 0;
}
static inline void
set_channelmap(u_int nr, u_char *map)
{
map[nr >> 3] |= (1 << (nr & 7));
}
static inline void
clear_channelmap(u_int nr, u_char *map)
{
map[nr >> 3] &= ~(1 << (nr & 7));
}
/* CONTROL_CHANNEL parameters */
#define MISDN_CTRL_GETOP 0x0000
#define MISDN_CTRL_LOOP 0x0001
#define MISDN_CTRL_CONNECT 0x0002
#define MISDN_CTRL_DISCONNECT 0x0004
#define MISDN_CTRL_PCMCONNECT 0x0010
#define MISDN_CTRL_PCMDISCONNECT 0x0020
#define MISDN_CTRL_SETPEER 0x0040
#define MISDN_CTRL_UNSETPEER 0x0080
#define MISDN_CTRL_RX_OFF 0x0100
#define MISDN_CTRL_FILL_EMPTY 0x0200
#define MISDN_CTRL_GETPEER 0x0400
#define MISDN_CTRL_HW_FEATURES_OP 0x2000
#define MISDN_CTRL_HW_FEATURES 0x2001
#define MISDN_CTRL_HFC_OP 0x4000
#define MISDN_CTRL_HFC_PCM_CONN 0x4001
#define MISDN_CTRL_HFC_PCM_DISC 0x4002
#define MISDN_CTRL_HFC_CONF_JOIN 0x4003
#define MISDN_CTRL_HFC_CONF_SPLIT 0x4004
#define MISDN_CTRL_HFC_RECEIVE_OFF 0x4005
#define MISDN_CTRL_HFC_RECEIVE_ON 0x4006
#define MISDN_CTRL_HFC_ECHOCAN_ON 0x4007
#define MISDN_CTRL_HFC_ECHOCAN_OFF 0x4008
/* socket options */
#define MISDN_TIME_STAMP 0x0001
struct mISDN_ctrl_req {
int op;
int channel;
int p1;
int p2;
};
/* muxer options */
#define MISDN_OPT_ALL 1
#define MISDN_OPT_TEIMGR 2
#endif /* mISDNIF_H */

View File

@ -8,11 +8,7 @@ noinst_HEADERS = \
auth.h \
bsc_msc.h \
bsc_msg_filter.h \
bsc_nat.h \
bsc_nat_callstats.h \
bsc_nat_sccp.h \
bsc_rll.h \
bsc_subscriber.h \
bss.h \
bts_ipaccess_nanobts_omlattr.h \
chan_alloc.h \
@ -24,19 +20,6 @@ noinst_HEADERS = \
db.h \
debug.h \
e1_config.h \
gb_proxy.h \
gprs_gb_parse.h \
gprs_gmm.h \
gprs_llc.h \
gprs_llc_xid.h \
gprs_sgsn.h \
gprs_sndcp.h \
gprs_sndcp_comp.h \
gprs_sndcp_dcomp.h \
gprs_sndcp_pcomp.h \
gprs_sndcp_xid.h \
gprs_subscriber.h \
gprs_utils.h \
gsm_04_08.h \
gsm_04_11.h \
gsm_04_14.h \
@ -45,7 +28,6 @@ noinst_HEADERS = \
gsm_data_shared.h \
gsm_subscriber.h \
gsup_client.h \
gtphub.h \
handover.h \
handover_decision.h \
ipaccess.h \
@ -58,7 +40,6 @@ noinst_HEADERS = \
mncc.h \
mncc_int.h \
msc_ifaces.h \
nat_rewrite_trie.h \
network_listen.h \
oap_client.h \
openbscdefines.h \
@ -71,13 +52,10 @@ noinst_HEADERS = \
bsc_msc_data.h \
osmux.h \
paging.h \
pcu_if.h \
pcuif_proto.h \
rest_octets.h \
rrlp.h \
rs232.h \
rtp_proxy.h \
sgsn.h \
signal.h \
silent_call.h \
slhc.h \
@ -91,8 +69,6 @@ noinst_HEADERS = \
ussd.h \
vlr.h \
vty.h \
v42bis.h \
v42bis_private.h \
$(NULL)
openbsc_HEADERS = \

View File

@ -1,462 +0,0 @@
/*
* (C) 2010-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010-2012 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BSC_NAT_H
#define BSC_NAT_H
#include <osmocom/legacy_mgcp/mgcp.h>
#include "bsc_msg_filter.h"
#include <osmocom/core/select.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/msgfile.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/statistics.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <regex.h>
#include <stdbool.h>
#define DIR_BSC 1
#define DIR_MSC 2
#define PAGIN_GROUP_UNASSIGNED -1
struct sccp_source_reference;
struct nat_sccp_connection;
struct bsc_nat_parsed;
struct bsc_nat;
struct bsc_nat_ussd_con;
struct nat_rewrite_rule;
/*
* Is this terminated to the MSC, to the local machine (release
* handling for IMSI filtering) or to a USSD provider?
*/
enum {
NAT_CON_END_MSC,
NAT_CON_END_LOCAL,
NAT_CON_END_USSD,
};
/*
* Pending command entry
*/
struct bsc_cmd_list {
struct llist_head list_entry;
struct osmo_timer_list timeout;
/* The NATed ID used on the bsc_con*/
int nat_id;
/* The control connection from which the command originated */
struct ctrl_connection *ccon;
/* The command from the control connection */
struct ctrl_cmd *cmd;
};
/*
* Per BSC data structure
*/
struct bsc_connection {
struct llist_head list_entry;
/* do we know anything about this BSC? */
int authenticated;
uint8_t last_rand[16];
/* the fd we use to communicate */
struct osmo_wqueue write_queue;
/* incoming message buffer */
struct msgb *pending_msg;
/* the BSS associated */
struct bsc_config *cfg;
/* a timeout node */
struct osmo_timer_list id_timeout;
/* pong timeout */
struct osmo_timer_list ping_timeout;
struct osmo_timer_list pong_timeout;
/* mgcp related code */
char *_endpoint_status;
int number_multiplexes;
int max_endpoints;
int last_endpoint;
int next_transaction;
uint32_t pending_dlcx_count;
struct llist_head pending_dlcx;
/* track the pending commands for this BSC */
struct llist_head cmd_pending;
int last_id;
/* a back pointer */
struct bsc_nat *nat;
};
/**
* Stats per BSC
*/
struct bsc_config_stats {
struct rate_ctr_group *ctrg;
};
enum bsc_cfg_ctr {
BCFG_CTR_SCCP_CONN,
BCFG_CTR_SCCP_CALLS,
BCFG_CTR_NET_RECONN,
BCFG_CTR_DROPPED_SCCP,
BCFG_CTR_DROPPED_CALLS,
BCFG_CTR_REJECTED_CR,
BCFG_CTR_REJECTED_MSG,
BCFG_CTR_ILL_PACKET,
BCFG_CTR_CON_TYPE_LU,
BCFG_CTR_CON_CMSERV_RQ,
BCFG_CTR_CON_PAG_RESP,
BCFG_CTR_CON_SSA,
BCFG_CTR_CON_OTHER,
};
/**
* One BSC entry in the config
*/
struct bsc_config {
struct llist_head entry;
uint8_t key[16];
uint8_t key_present;
char *token;
int nr;
char *description;
/* imsi white and blacklist */
char *acc_lst_name;
int forbid_paging;
int paging_group;
/* audio handling */
int max_endpoints;
/* used internally for reload handling */
bool remove;
bool token_updated;
/* backpointer */
struct bsc_nat *nat;
struct bsc_config_stats stats;
struct llist_head lac_list;
/* Osmux is enabled/disabled per BSC */
int osmux;
};
struct bsc_lac_entry {
struct llist_head entry;
uint16_t lac;
};
struct bsc_nat_paging_group {
struct llist_head entry;
/* list of lac entries */
struct llist_head lists;
int nr;
};
/**
* BSCs point of view of endpoints
*/
struct bsc_endpoint {
/* the operation that is carried out */
int transaction_state;
/* the pending transaction id */
char *transaction_id;
/* the bsc we are talking to */
struct bsc_connection *bsc;
};
/**
* Statistic for the nat.
*/
struct bsc_nat_statistics {
struct {
struct osmo_counter *conn;
struct osmo_counter *calls;
} sccp;
struct {
struct osmo_counter *reconn;
struct osmo_counter *auth_fail;
} bsc;
struct {
struct osmo_counter *reconn;
} msc;
struct {
struct osmo_counter *reconn;
} ussd;
};
/**
* the structure of the "nat" network
*/
struct bsc_nat {
/* active SCCP connections that need patching */
struct llist_head sccp_connections;
/* active BSC connections that need patching */
struct llist_head bsc_connections;
/* access lists */
struct llist_head access_lists;
/* paging groups */
struct llist_head paging_groups;
/* known BSC's */
struct llist_head bsc_configs;
int num_bsc;
int bsc_ip_dscp;
/* MGCP config */
struct mgcp_config *mgcp_cfg;
uint8_t mgcp_msg[4096];
int mgcp_length;
int mgcp_ipa;
int sdp_ensure_amr_mode_set;
/* msc things */
struct llist_head dests;
struct bsc_msc_dest *main_dest;
struct bsc_msc_connection *msc_con;
char *token;
/* timeouts */
int auth_timeout;
int ping_timeout;
int pong_timeout;
struct bsc_endpoint *bsc_endpoints;
/* path to file with BSC config */
char *include_file;
char *include_base;
char *resolved_path;
/* filter */
char *acc_lst_name;
/* Barring of subscribers with a rb tree */
struct rb_root imsi_black_list;
char *imsi_black_list_fn;
/* number rewriting */
char *num_rewr_name;
struct llist_head num_rewr;
char *num_rewr_post_name;
struct llist_head num_rewr_post;
char *smsc_rewr_name;
struct llist_head smsc_rewr;
char *tpdest_match_name;
struct llist_head tpdest_match;
char *sms_clear_tp_srr_name;
struct llist_head sms_clear_tp_srr;
char *sms_num_rewr_name;
struct llist_head sms_num_rewr;
/* more rewriting */
char *num_rewr_trie_name;
struct nat_rewrite *num_rewr_trie;
/* USSD messages we want to match */
char *ussd_lst_name;
char *ussd_query;
regex_t ussd_query_re;
char *ussd_token;
char *ussd_local;
struct osmo_fd ussd_listen;
struct bsc_nat_ussd_con *ussd_con;
/* for maintainenance */
int blocked;
/* statistics */
struct bsc_nat_statistics stats;
/* control interface */
struct ctrl_handle *ctrl;
};
struct bsc_nat_ussd_con {
struct osmo_wqueue queue;
struct bsc_nat *nat;
int authorized;
struct msgb *pending_msg;
struct osmo_timer_list auth_timeout;
};
/* create and init the structures */
struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token,
unsigned int number);
struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num);
struct bsc_config *bsc_config_by_token(struct bsc_nat *nat, const char *token, int len);
void bsc_config_free(struct bsc_config *);
void bsc_config_add_lac(struct bsc_config *cfg, int lac);
void bsc_config_del_lac(struct bsc_config *cfg, int lac);
int bsc_config_handles_lac(struct bsc_config *cfg, int lac);
struct bsc_nat *bsc_nat_alloc(void);
struct bsc_connection *bsc_connection_alloc(struct bsc_nat *nat);
void bsc_nat_set_msc_ip(struct bsc_nat *bsc, const char *ip);
void sccp_connection_destroy(struct nat_sccp_connection *);
void bsc_close_connection(struct bsc_connection *);
const char *bsc_con_type_to_string(int type);
/**
* parse the given message into the above structure
*/
struct bsc_nat_parsed *bsc_nat_parse(struct msgb *msg);
/**
* filter based on IP Access header in both directions
*/
int bsc_nat_filter_ipa(int direction, struct msgb *msg, struct bsc_nat_parsed *parsed);
int bsc_nat_vty_init(struct bsc_nat *nat);
int bsc_nat_find_paging(struct msgb *msg, const uint8_t **,int *len);
/**
* SCCP patching and handling
*/
struct nat_sccp_connection *create_sccp_src_ref(struct bsc_connection *bsc, struct bsc_nat_parsed *parsed);
int update_sccp_src_ref(struct nat_sccp_connection *sccp, struct bsc_nat_parsed *parsed);
void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed);
struct nat_sccp_connection *patch_sccp_src_ref_to_bsc(struct msgb *, struct bsc_nat_parsed *, struct bsc_nat *);
struct nat_sccp_connection *patch_sccp_src_ref_to_msc(struct msgb *, struct bsc_nat_parsed *, struct bsc_connection *);
struct nat_sccp_connection *bsc_nat_find_con_by_bsc(struct bsc_nat *, struct sccp_source_reference *);
/**
* MGCP/Audio handling
*/
int bsc_mgcp_nr_multiplexes(int max_endpoints);
int bsc_write_mgcp(struct bsc_connection *bsc, const uint8_t *data, unsigned int length);
int bsc_mgcp_assign_patch(struct nat_sccp_connection *, struct msgb *msg);
void bsc_mgcp_init(struct nat_sccp_connection *);
void bsc_mgcp_dlcx(struct nat_sccp_connection *);
void bsc_mgcp_free_endpoints(struct bsc_nat *nat);
int bsc_mgcp_nat_init(struct bsc_nat *nat);
struct nat_sccp_connection *bsc_mgcp_find_con(struct bsc_nat *, int endpoint_number);
struct msgb *bsc_mgcp_rewrite(char *input, int length, int endp, const char *ip,
int port, int osmux, int *first_payload_type, int mode_set);
void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg);
void bsc_mgcp_clear_endpoints_for(struct bsc_connection *bsc);
int bsc_mgcp_parse_response(const char *str, int *code, char transaction[60]);
uint32_t bsc_mgcp_extract_ci(const char *resp);
int bsc_write(struct bsc_connection *bsc, struct msgb *msg, int id);
int bsc_do_write(struct osmo_wqueue *queue, struct msgb *msg, int id);
int bsc_write_msg(struct osmo_wqueue *queue, struct msgb *msg);
int bsc_write_cb(struct osmo_fd *bfd, struct msgb *msg);
int bsc_nat_msc_is_connected(struct bsc_nat *nat);
int bsc_conn_type_to_ctr(struct nat_sccp_connection *conn);
struct gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed, struct msgb *msg, uint32_t *len);
/** USSD filtering */
int bsc_ussd_init(struct bsc_nat *nat);
int bsc_ussd_check(struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed, struct msgb *msg);
int bsc_ussd_close_connections(struct bsc_nat *nat);
struct msgb *bsc_nat_rewrite_msg(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *, const char *imsi);
/** paging group handling */
struct bsc_nat_paging_group *bsc_nat_paging_group_num(struct bsc_nat *nat, int group);
struct bsc_nat_paging_group *bsc_nat_paging_group_create(struct bsc_nat *nat, int group);
void bsc_nat_paging_group_delete(struct bsc_nat_paging_group *);
void bsc_nat_paging_group_add_lac(struct bsc_nat_paging_group *grp, int lac);
void bsc_nat_paging_group_del_lac(struct bsc_nat_paging_group *grp, int lac);
/**
* Number rewriting support below
*/
struct bsc_nat_num_rewr_entry {
struct llist_head list;
regex_t msisdn_reg;
regex_t num_reg;
char *replace;
uint8_t is_prefix_lookup;
};
void bsc_nat_num_rewr_entry_adapt(void *ctx, struct llist_head *head, const struct osmo_config_list *);
void bsc_nat_send_mgcp_to_msc(struct bsc_nat *bsc_nat, struct msgb *msg);
void bsc_nat_handle_mgcp(struct bsc_nat *bsc, struct msgb *msg);
struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat,
const char *bind_addr, int port);
void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending);
int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg);
int bsc_nat_extract_lac(struct bsc_connection *bsc, struct nat_sccp_connection *con,
struct bsc_nat_parsed *parsed, struct msgb *msg);
int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg,
struct bsc_nat_parsed *, int *con_type, char **imsi,
struct bsc_filter_reject_cause *cause);
int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg,
struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed,
struct bsc_filter_reject_cause *cause);
/**
* CTRL interface helper
*/
void bsc_nat_inform_reject(struct bsc_connection *bsc, const char *imsi);
/*
* Use for testing
*/
void bsc_nat_free(struct bsc_nat *nat);
#endif

View File

@ -1,55 +0,0 @@
/*
* (C) 2010-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010-2012 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BSC_NAT_CALLSTATS_H
#define BSC_NAT_CALLSTATS_H
#include <osmocom/core/linuxlist.h>
#include <osmocom/sccp/sccp_types.h>
struct bsc_nat_call_stats {
struct llist_head entry;
struct sccp_source_reference remote_ref;
struct sccp_source_reference src_ref; /* as seen by the MSC */
/* mgcp options */
uint32_t ci;
int bts_rtp_port;
int net_rtp_port;
struct in_addr bts_addr;
struct in_addr net_addr;
/* as witnessed by the NAT */
uint32_t net_ps;
uint32_t net_os;
uint32_t bts_pr;
uint32_t bts_or;
uint32_t bts_expected;
uint32_t bts_jitter;
int bts_loss;
uint32_t trans_id;
int msc_endpoint;
};
#endif

View File

@ -1,105 +0,0 @@
/* NAT utilities using SCCP types */
/*
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef BSC_NAT_SCCP_H
#define BSC_NAT_SCCP_H
#include "bsc_msg_filter.h"
#include <osmocom/sccp/sccp_types.h>
/*
* For the NAT we will need to analyze and later patch
* the received message. This would require us to parse
* the IPA and SCCP header twice. Instead of doing this
* we will have one analyze structure and have the patching
* and filter operate on the same structure.
*/
struct bsc_nat_parsed {
/* ip access prototype */
int ipa_proto;
/* source local reference */
struct sccp_source_reference *src_local_ref;
/* destination local reference */
struct sccp_source_reference *dest_local_ref;
/* original value */
struct sccp_source_reference original_dest_ref;
/* called ssn number */
int called_ssn;
/* calling ssn number */
int calling_ssn;
/* sccp message type */
int sccp_type;
/* bssap type, e.g. 0 for BSS Management */
int bssap;
/* the gsm0808 message type */
int gsm_type;
};
/*
* Per SCCP source local reference patch table. It needs to
* be updated on new SCCP connections, connection confirm and reject,
* and on the loss of the BSC connection.
*/
struct nat_sccp_connection {
struct llist_head list_entry;
struct bsc_connection *bsc;
struct bsc_msc_connection *msc_con;
struct sccp_source_reference real_ref;
struct sccp_source_reference patched_ref;
struct sccp_source_reference remote_ref;
int has_remote_ref;
/* status */
int con_local;
int authorized;
struct bsc_filter_state filter_state;
uint16_t lac;
uint16_t ci;
/* remember which Transactions we run over the bypass */
char ussd_ti[8];
/*
* audio handling. Remember if we have ever send a CRCX,
* remember the endpoint used by the MSC and BSC.
*/
int msc_endp;
int bsc_endp;
/* timeout handling */
struct timespec creation_time;
};
#endif

View File

@ -1,43 +0,0 @@
/* GSM subscriber details for use in BSC land */
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
struct log_target;
struct bsc_subscr {
struct llist_head entry;
int use_count;
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
uint32_t tmsi;
uint16_t lac;
};
const char *bsc_subscr_name(struct bsc_subscr *bsub);
struct bsc_subscr *bsc_subscr_find_or_create_by_imsi(struct llist_head *list,
const char *imsi);
struct bsc_subscr *bsc_subscr_find_or_create_by_tmsi(struct llist_head *list,
uint32_t tmsi);
struct bsc_subscr *bsc_subscr_find_by_imsi(struct llist_head *list,
const char *imsi);
struct bsc_subscr *bsc_subscr_find_by_tmsi(struct llist_head *list,
uint32_t tmsi);
void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi);
struct bsc_subscr *_bsc_subscr_get(struct bsc_subscr *bsub,
const char *file, int line);
struct bsc_subscr *_bsc_subscr_put(struct bsc_subscr *bsub,
const char *file, int line);
#define bsc_subscr_get(bsub) _bsc_subscr_get(bsub, __BASE_FILE__, __LINE__)
#define bsc_subscr_put(bsub) _bsc_subscr_put(bsub, __BASE_FILE__, __LINE__)
void log_set_filter_bsc_subscr(struct log_target *target,
struct bsc_subscr *bsub);

View File

@ -1,288 +0,0 @@
#ifndef _GB_PROXY_H
#define _GB_PROXY_H
#include <osmocom/core/msgb.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/vty/command.h>
#include <sys/types.h>
#include <regex.h>
#define GBPROXY_INIT_VU_GEN_TX 256
struct rate_ctr_group;
struct gprs_gb_parse_context;
struct tlv_parsed;
enum gbproxy_global_ctr {
GBPROX_GLOB_CTR_INV_BVCI,
GBPROX_GLOB_CTR_INV_LAI,
GBPROX_GLOB_CTR_INV_RAI,
GBPROX_GLOB_CTR_INV_NSEI,
GBPROX_GLOB_CTR_PROTO_ERR_BSS,
GBPROX_GLOB_CTR_PROTO_ERR_SGSN,
GBPROX_GLOB_CTR_NOT_SUPPORTED_BSS,
GBPROX_GLOB_CTR_NOT_SUPPORTED_SGSN,
GBPROX_GLOB_CTR_RESTART_RESET_SGSN,
GBPROX_GLOB_CTR_TX_ERR_SGSN,
GBPROX_GLOB_CTR_OTHER_ERR,
GBPROX_GLOB_CTR_PATCH_PEER_ERR,
};
enum gbproxy_peer_ctr {
GBPROX_PEER_CTR_BLOCKED,
GBPROX_PEER_CTR_UNBLOCKED,
GBPROX_PEER_CTR_DROPPED,
GBPROX_PEER_CTR_INV_NSEI,
GBPROX_PEER_CTR_TX_ERR,
GBPROX_PEER_CTR_RAID_PATCHED_BSS,
GBPROX_PEER_CTR_RAID_PATCHED_SGSN,
GBPROX_PEER_CTR_APN_PATCHED,
GBPROX_PEER_CTR_TLLI_PATCHED_BSS,
GBPROX_PEER_CTR_TLLI_PATCHED_SGSN,
GBPROX_PEER_CTR_PTMSI_PATCHED_BSS,
GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN,
GBPROX_PEER_CTR_PATCH_CRYPT_ERR,
GBPROX_PEER_CTR_PATCH_ERR,
GBPROX_PEER_CTR_ATTACH_REQS,
GBPROX_PEER_CTR_ATTACH_REJS,
GBPROX_PEER_CTR_ATTACH_ACKS,
GBPROX_PEER_CTR_ATTACH_COMPLS,
GBPROX_PEER_CTR_RA_UPD_REQS,
GBPROX_PEER_CTR_RA_UPD_REJS,
GBPROX_PEER_CTR_RA_UPD_ACKS,
GBPROX_PEER_CTR_RA_UPD_COMPLS,
GBPROX_PEER_CTR_GMM_STATUS_BSS,
GBPROX_PEER_CTR_GMM_STATUS_SGSN,
GBPROX_PEER_CTR_DETACH_REQS,
GBPROX_PEER_CTR_DETACH_ACKS,
GBPROX_PEER_CTR_PDP_ACT_REQS,
GBPROX_PEER_CTR_PDP_ACT_REJS,
GBPROX_PEER_CTR_PDP_ACT_ACKS,
GBPROX_PEER_CTR_PDP_DEACT_REQS,
GBPROX_PEER_CTR_PDP_DEACT_ACKS,
GBPROX_PEER_CTR_TLLI_UNKNOWN,
GBPROX_PEER_CTR_TLLI_CACHE_SIZE,
GBPROX_PEER_CTR_LAST,
};
enum gbproxy_keep_mode {
GBPROX_KEEP_NEVER,
GBPROX_KEEP_REATTACH,
GBPROX_KEEP_IDENTIFIED,
GBPROX_KEEP_ALWAYS,
};
enum gbproxy_match_id {
GBPROX_MATCH_PATCHING,
GBPROX_MATCH_ROUTING,
GBPROX_MATCH_LAST
};
struct gbproxy_match {
int enable;
char *re_str;
regex_t re_comp;
};
struct gbproxy_config {
/* parsed from config file */
uint16_t nsip_sgsn_nsei;
/* misc */
struct gprs_ns_inst *nsi;
/* Linked list of all Gb peers (except SGSN) */
struct llist_head bts_peers;
/* Counter */
struct rate_ctr_group *ctrg;
/* force mcc/mnc */
int core_mnc;
int core_mcc;
uint8_t* core_apn;
size_t core_apn_size;
int tlli_max_age;
int tlli_max_len;
/* Experimental config */
int patch_ptmsi;
int acquire_imsi;
int route_to_sgsn2;
uint16_t nsip_sgsn2_nsei;
enum gbproxy_keep_mode keep_link_infos;
/* IMSI checking/matching */
struct gbproxy_match matches[GBPROX_MATCH_LAST];
};
struct gbproxy_patch_state {
int local_mnc;
int local_mcc;
/* List of TLLIs for which patching is enabled */
struct llist_head logical_links;
int logical_link_count;
};
struct gbproxy_peer {
struct llist_head list;
/* point back to the config */
struct gbproxy_config *cfg;
/* NSEI of the peer entity */
uint16_t nsei;
/* BVCI used for Point-to-Point to this peer */
uint16_t bvci;
int blocked;
/* Routeing Area that this peer is part of (raw 04.08 encoding) */
uint8_t ra[6];
/* Counter */
struct rate_ctr_group *ctrg;
struct gbproxy_patch_state patch_state;
};
struct gbproxy_tlli_state {
uint32_t current;
uint32_t assigned;
int bss_validated;
int net_validated;
uint32_t ptmsi;
};
struct gbproxy_link_info {
struct llist_head list;
struct gbproxy_tlli_state tlli;
struct gbproxy_tlli_state sgsn_tlli;
uint32_t sgsn_nsei;
time_t timestamp;
uint8_t *imsi;
size_t imsi_len;
int imsi_acq_pending;
struct llist_head stored_msgs;
unsigned vu_gen_tx_bss;
int is_deregistered;
int is_matching[GBPROX_MATCH_LAST];
};
/* gb_proxy_vty .c */
int gbproxy_vty_init(void);
int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg);
/* gb_proxy.c */
int gbproxy_init_config(struct gbproxy_config *cfg);
/* Main input function for Gb proxy */
int gbprox_rcvmsg(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, uint16_t ns_bvci, uint16_t nsvci);
int gbprox_signal(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data);
/* Reset all persistent NS-VC's */
int gbprox_reset_persistent_nsvcs(struct gprs_ns_inst *nsi);
void gbprox_reset(struct gbproxy_config *cfg);
/* TLLI info handling */
void gbproxy_delete_link_infos(struct gbproxy_peer *peer);
struct gbproxy_link_info *gbproxy_update_link_state_ul(
struct gbproxy_peer *peer, time_t now,
struct gprs_gb_parse_context *parse_ctx);
struct gbproxy_link_info *gbproxy_update_link_state_dl(
struct gbproxy_peer *peer, time_t now,
struct gprs_gb_parse_context *parse_ctx);
int gbproxy_update_link_state_after(
struct gbproxy_peer *peer, struct gbproxy_link_info *link_info,
time_t now, struct gprs_gb_parse_context *parse_ctx);
int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now);
void gbproxy_delete_link_info(struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info);
void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info);
void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now,
struct gbproxy_link_info *link_info);
void gbproxy_update_link_info(struct gbproxy_link_info *link_info,
const uint8_t *imsi, size_t imsi_len);
void gbproxy_detach_link_info(struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info);
struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer);
struct gbproxy_link_info *gbproxy_link_info_by_tlli(
struct gbproxy_peer *peer, uint32_t tlli);
struct gbproxy_link_info *gbproxy_link_info_by_imsi(
struct gbproxy_peer *peer, const uint8_t *imsi, size_t imsi_len);
struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli(
struct gbproxy_peer *peer, uint32_t tlli);
struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli(
struct gbproxy_peer *peer,
uint32_t tlli, uint32_t sgsn_nsei);
struct gbproxy_link_info *gbproxy_link_info_by_ptmsi(
struct gbproxy_peer *peer,
uint32_t ptmsi);
int gbproxy_imsi_matches(
struct gbproxy_config *cfg,
enum gbproxy_match_id match_id,
struct gbproxy_link_info *link_info);
uint32_t gbproxy_map_tlli(
uint32_t other_tlli, struct gbproxy_link_info *link_info, int to_bss);
/* needed by gb_proxy_tlli.h */
uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, uint32_t sgsn_ptmsi);
uint32_t gbproxy_make_sgsn_tlli(
struct gbproxy_peer *peer, struct gbproxy_link_info *link_info,
uint32_t bss_tlli);
void gbproxy_reset_link(struct gbproxy_link_info *link_info);
int gbproxy_check_imsi(
struct gbproxy_match *match, const uint8_t *imsi, size_t imsi_len);
/* Message patching */
void gbproxy_patch_bssgp(
struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
struct gbproxy_peer *peer, struct gbproxy_link_info *link_info,
int *len_change, struct gprs_gb_parse_context *parse_ctx);
int gbproxy_patch_llc(
struct msgb *msg, uint8_t *llc, size_t llc_len,
struct gbproxy_peer *peer, struct gbproxy_link_info *link_info,
int *len_change, struct gprs_gb_parse_context *parse_ctx);
int gbproxy_set_patch_filter(
struct gbproxy_match *match, const char *filter, const char **err_msg);
void gbproxy_clear_patch_filter(struct gbproxy_match *match);
/* Peer handling */
struct gbproxy_peer *gbproxy_peer_by_bvci(
struct gbproxy_config *cfg, uint16_t bvci);
struct gbproxy_peer *gbproxy_peer_by_nsei(
struct gbproxy_config *cfg, uint16_t nsei);
struct gbproxy_peer *gbproxy_peer_by_rai(
struct gbproxy_config *cfg, const uint8_t *ra);
struct gbproxy_peer *gbproxy_peer_by_lai(
struct gbproxy_config *cfg, const uint8_t *la);
struct gbproxy_peer *gbproxy_peer_by_lac(
struct gbproxy_config *cfg, const uint8_t *la);
struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv(
struct gbproxy_config *cfg, struct tlv_parsed *tp);
struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci);
void gbproxy_peer_free(struct gbproxy_peer *peer);
int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci);
#endif

View File

@ -1,59 +0,0 @@
#pragma once
#include <openbsc/gprs_llc.h>
#include <sys/types.h>
struct gprs_gb_parse_context {
/* Pointer to protocol specific parts */
struct gsm48_hdr *g48_hdr;
struct bssgp_normal_hdr *bgp_hdr;
struct bssgp_ud_hdr *bud_hdr;
uint8_t *bssgp_data;
size_t bssgp_data_len;
uint8_t *llc;
size_t llc_len;
/* Extracted information */
struct gprs_llc_hdr_parsed llc_hdr_parsed;
struct tlv_parsed bssgp_tp;
int to_bss;
uint8_t *tlli_enc;
uint8_t *old_tlli_enc;
uint8_t *imsi;
size_t imsi_len;
uint8_t *apn_ie;
size_t apn_ie_len;
uint8_t *ptmsi_enc;
uint8_t *new_ptmsi_enc;
uint8_t *raid_enc;
uint8_t *old_raid_enc;
uint8_t *bssgp_raid_enc;
uint8_t *bssgp_ptmsi_enc;
/* General info */
const char *llc_msg_name;
int invalidate_tlli;
int await_reattach;
int need_decryption;
uint32_t tlli;
int pdu_type;
int old_raid_is_foreign;
int peer_nsei;
};
int gprs_gb_parse_dtap(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx);
int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len,
struct gprs_gb_parse_context *parse_ctx);
int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len,
struct gprs_gb_parse_context *parse_ctx);
const char *gprs_gb_message_name(const struct gprs_gb_parse_context *parse_ctx,
const char *default_msg_name);
void gprs_gb_log_parse_context(int log_level,
struct gprs_gb_parse_context *parse_ctx,
const char *default_msg_name);

View File

@ -1,35 +0,0 @@
#ifndef _GPRS_GMM_H
#define _GPRS_GMM_H
#include <osmocom/core/msgb.h>
#include <openbsc/gprs_sgsn.h>
#include <stdbool.h>
int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause);
int gsm48_tx_gsm_act_pdp_rej(struct sgsn_mm_ctx *mm, uint8_t tid,
uint8_t cause, uint8_t pco_len, uint8_t *pco_v);
int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp);
int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp);
int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme,
bool drop_cipherable);
int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id,
uint16_t *sai);
int gsm0408_gprs_force_reattach(struct sgsn_mm_ctx *mmctx);
int gsm0408_gprs_force_reattach_oldmsg(struct msgb *msg,
struct gprs_llc_llme *llme);
void gsm0408_gprs_access_granted(struct sgsn_mm_ctx *mmctx);
void gsm0408_gprs_access_denied(struct sgsn_mm_ctx *mmctx, int gmm_cause);
void gsm0408_gprs_access_cancelled(struct sgsn_mm_ctx *mmctx, int gmm_cause);
void gsm0408_gprs_authenticate(struct sgsn_mm_ctx *mmctx);
int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli);
int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli,
uint8_t suspend_ref);
time_t gprs_max_time_to_idle(void);
int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp);
#endif /* _GPRS_GMM_H */

View File

@ -1,284 +0,0 @@
#ifndef _GPRS_LLC_H
#define _GPRS_LLC_H
#include <stdint.h>
#include <stdbool.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_llc_xid.h>
/* Section 4.7 LLC Layer Structure */
enum gprs_llc_sapi {
GPRS_SAPI_GMM = 1,
GPRS_SAPI_TOM2 = 2,
GPRS_SAPI_SNDCP3 = 3,
GPRS_SAPI_SNDCP5 = 5,
GPRS_SAPI_SMS = 7,
GPRS_SAPI_TOM8 = 8,
GPRS_SAPI_SNDCP9 = 9,
GPRS_SAPI_SNDCP11 = 11,
};
/* Section 6.4 Commands and Responses */
enum gprs_llc_u_cmd {
GPRS_LLC_U_DM_RESP = 0x01,
GPRS_LLC_U_DISC_CMD = 0x04,
GPRS_LLC_U_UA_RESP = 0x06,
GPRS_LLC_U_SABM_CMD = 0x07,
GPRS_LLC_U_FRMR_RESP = 0x08,
GPRS_LLC_U_XID = 0x0b,
GPRS_LLC_U_NULL_CMD = 0x00,
};
/* Section 6.4.1.6 / Table 6 */
enum gprs_llc_xid_type {
GPRS_LLC_XID_T_VERSION = 0,
GPRS_LLC_XID_T_IOV_UI = 1,
GPRS_LLC_XID_T_IOV_I = 2,
GPRS_LLC_XID_T_T200 = 3,
GPRS_LLC_XID_T_N200 = 4,
GPRS_LLC_XID_T_N201_U = 5,
GPRS_LLC_XID_T_N201_I = 6,
GPRS_LLC_XID_T_mD = 7,
GPRS_LLC_XID_T_mU = 8,
GPRS_LLC_XID_T_kD = 9,
GPRS_LLC_XID_T_kU = 10,
GPRS_LLC_XID_T_L3_PAR = 11,
GPRS_LLC_XID_T_RESET = 12,
};
extern const struct value_string gprs_llc_xid_type_names[];
/* TS 04.64 Section 7.1.2 Table 7: LLC layer primitives (GMM/SNDCP/SMS/TOM) */
/* TS 04.65 Section 5.1.2 Table 2: Service primitives used by SNDCP */
enum gprs_llc_primitive {
/* GMM <-> LLME */
LLGMM_ASSIGN_REQ, /* GMM tells us new TLLI: TLLI old, TLLI new, Kc, CiphAlg */
LLGMM_RESET_REQ, /* GMM tells us to perform XID negotiation: TLLI */
LLGMM_RESET_CNF, /* LLC informs GMM that XID has completed: TLLI */
LLGMM_SUSPEND_REQ, /* GMM tells us MS has suspended: TLLI, Page */
LLGMM_RESUME_REQ, /* GMM tells us MS has resumed: TLLI */
LLGMM_PAGE_IND, /* LLC asks GMM to page MS: TLLI */
LLGMM_IOV_REQ, /* GMM tells us to perform XID: TLLI */
LLGMM_STATUS_IND, /* LLC informs GMM about error: TLLI, Cause */
/* LLE <-> (GMM/SNDCP/SMS/TOM) */
LL_RESET_IND, /* TLLI */
LL_ESTABLISH_REQ, /* TLLI, XID Req */
LL_ESTABLISH_IND, /* TLLI, XID Req, N201-I, N201-U */
LL_ESTABLISH_RESP, /* TLLI, XID Negotiated */
LL_ESTABLISH_CONF, /* TLLI, XID Neg, N201-i, N201-U */
LL_RELEASE_REQ, /* TLLI, Local */
LL_RELEASE_IND, /* TLLI, Cause */
LL_RELEASE_CONF, /* TLLI */
LL_XID_REQ, /* TLLI, XID Requested */
LL_XID_IND, /* TLLI, XID Req, N201-I, N201-U */
LL_XID_RESP, /* TLLI, XID Negotiated */
LL_XID_CONF, /* TLLI, XID Neg, N201-I, N201-U */
LL_DATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */
LL_DATA_IND, /* TLLI, SN-PDU */
LL_DATA_CONF, /* TLLI, Ref */
LL_UNITDATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */
LL_UNITDATA_IND, /* TLLI, SN-PDU */
LL_STATUS_IND, /* TLLI, Cause */
};
/* Section 4.5.2 Logical Link States + Annex C.2 */
enum gprs_llc_lle_state {
GPRS_LLES_UNASSIGNED = 1, /* No TLLI yet */
GPRS_LLES_ASSIGNED_ADM = 2, /* TLLI assigned */
GPRS_LLES_LOCAL_EST = 3, /* Local Establishment */
GPRS_LLES_REMOTE_EST = 4, /* Remote Establishment */
GPRS_LLES_ABM = 5,
GPRS_LLES_LOCAL_REL = 6, /* Local Release */
GPRS_LLES_TIMER_REC = 7, /* Timer Recovery */
};
enum gprs_llc_llme_state {
GPRS_LLMS_UNASSIGNED = 1, /* No TLLI yet */
GPRS_LLMS_ASSIGNED = 2, /* TLLI assigned */
};
/* Section 8.9.9 LLC layer parameter default values */
struct gprs_llc_params {
uint16_t iov_i_exp;
uint16_t t200_201;
uint16_t n200;
uint16_t n201_u;
uint16_t n201_i;
uint16_t mD;
uint16_t mU;
uint16_t kD;
uint16_t kU;
};
/* Section 4.7.1: Logical Link Entity: One per DLCI (TLLI + SAPI) */
struct gprs_llc_lle {
struct llist_head list;
uint32_t sapi;
struct gprs_llc_llme *llme;
enum gprs_llc_lle_state state;
struct osmo_timer_list t200;
struct osmo_timer_list t201; /* wait for acknowledgement */
uint16_t v_sent;
uint16_t v_ack;
uint16_t v_recv;
uint16_t vu_send;
uint16_t vu_recv;
/* non-standard LLC state */
uint16_t vu_recv_last;
uint16_t vu_recv_duplicates;
/* Overflow Counter for ABM */
uint32_t oc_i_send;
uint32_t oc_i_recv;
/* Overflow Counter for unconfirmed transfer */
uint32_t oc_ui_send;
uint32_t oc_ui_recv;
unsigned int retrans_ctr;
struct gprs_llc_params params;
};
#define NUM_SAPIS 16
struct gprs_llc_llme {
struct llist_head list;
enum gprs_llc_llme_state state;
uint32_t tlli;
uint32_t old_tlli;
/* Crypto parameters */
enum gprs_ciph_algo algo;
uint8_t kc[16];
uint8_t cksn;
/* 3GPP TS 44.064 § 8.9.2: */
uint32_t iov_ui;
/* over which BSSGP BTS ctx do we need to transmit */
uint16_t bvci;
uint16_t nsei;
struct gprs_llc_lle lle[NUM_SAPIS];
/* Copy of the XID fields we have sent with the last
* network originated XID-Request. Since the phone
* may strip the optional fields in the confirmation
* we need to remeber those fields in order to be
* able to create the compression entity. */
struct llist_head *xid;
/* Compression entities */
struct {
/* In these two list_heads we will store the
* data and protocol compression entities,
* together with their compression states */
struct llist_head *proto;
struct llist_head *data;
} comp;
/* Internal management */
uint32_t age_timestamp;
};
#define GPRS_LLME_RESET_AGE (0)
extern struct llist_head gprs_llc_llmes;
/* LLC low level types */
enum gprs_llc_cmd {
GPRS_LLC_NULL,
GPRS_LLC_RR,
GPRS_LLC_ACK,
GPRS_LLC_RNR,
GPRS_LLC_SACK,
GPRS_LLC_DM,
GPRS_LLC_DISC,
GPRS_LLC_UA,
GPRS_LLC_SABM,
GPRS_LLC_FRMR,
GPRS_LLC_XID,
GPRS_LLC_UI,
};
struct gprs_llc_hdr_parsed {
uint8_t sapi;
uint8_t is_cmd:1,
ack_req:1,
is_encrypted:1;
uint32_t seq_rx;
uint32_t seq_tx;
uint32_t fcs;
uint32_t fcs_calc;
uint8_t *data;
uint16_t data_len;
uint16_t crc_length;
enum gprs_llc_cmd cmd;
};
/* BSSGP-UL-UNITDATA.ind */
int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv);
/* LL-UNITDATA.req */
int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command,
struct sgsn_mm_ctx *mmctx, bool encryptable);
/* Chapter 7.2.1.2 LLGMM-RESET.req */
int gprs_llgmm_reset(struct gprs_llc_llme *llme);
int gprs_llgmm_reset_oldmsg(struct msgb* oldmsg, uint8_t sapi,
struct gprs_llc_llme *llme);
/* Set of LL-XID negotiation (See also: TS 101 351, Section 7.2.2.4) */
int gprs_ll_xid_req(struct gprs_llc_lle *lle,
struct gprs_llc_xid_field *l3_xid_field);
/* 04.64 Chapter 7.2.1.1 LLGMM-ASSIGN */
int gprs_llgmm_assign(struct gprs_llc_llme *llme,
uint32_t old_tlli, uint32_t new_tlli);
int gprs_llgmm_unassign(struct gprs_llc_llme *llme);
int gprs_llc_init(const char *cipher_plugin_path);
int gprs_llc_vty_init(void);
/**
* \short Check if N(U) should be considered a retransmit
*
* Implements the range check as of GSM 04.64 8.4.2
* Receipt of unacknowledged information.
*
* @returns Returns 1 if (V(UR)-32) <= N(U) < V(UR)
* @param nu N(U) unconfirmed sequence number of the UI frame
* @param vur V(UR) unconfirmend received state variable
*/
static inline int gprs_llc_is_retransmit(uint16_t nu, uint16_t vur)
{
int delta = (vur - nu) & 0x1ff;
return 0 < delta && delta < 32;
}
/* LLC low level functions */
void gprs_llme_copy_key(struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme);
/* parse a GPRS LLC header, also check for invalid frames */
int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp,
uint8_t *llc_hdr, int len);
void gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph, struct gprs_llc_lle *lle);
int gprs_llc_fcs(uint8_t *data, unsigned int len);
/* LLME handling routines */
struct llist_head *gprs_llme_list(void);
struct gprs_llc_lle *gprs_lle_get_or_create(const uint32_t tlli, uint8_t sapi);
#endif

View File

@ -1,57 +0,0 @@
/* GPRS LLC XID field encoding/decoding as per 3GPP TS 44.064 */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
/* 3GPP TS 44.064 6.4.1.6 Exchange Identification (XID)
command/response parameter field */
struct gprs_llc_xid_field {
struct llist_head list;
uint8_t type; /* See also Table 6: LLC layer parameter
negotiation */
uint8_t *data; /* Payload data (memory is owned by the
* creator of the struct) */
unsigned int data_len; /* Payload length */
};
/* Transform a list with XID fields into a XID message (dst) */
int gprs_llc_compile_xid(uint8_t *dst, int dst_maxlen,
const struct llist_head *xid_fields);
/* Transform a XID message (dst) into a list of XID fields */
struct llist_head *gprs_llc_parse_xid(const void *ctx, const uint8_t *src,
int src_len);
/* Create a duplicate of an XID-Field */
struct gprs_llc_xid_field *gprs_llc_dup_xid_field(const void *ctx,
const struct gprs_llc_xid_field *xid_field);
/* Copy an llist with xid fields */
struct llist_head *gprs_llc_copy_xid(const void *ctx,
const struct llist_head *xid_fields);
/* Dump a list with XID fields (Debug) */
void gprs_llc_dump_xid_fields(const struct llist_head *xid_fields,
unsigned int logl);

View File

@ -1,478 +0,0 @@
#ifndef _GPRS_SGSN_H
#define _GPRS_SGSN_H
#include <stdint.h>
#include <netinet/in.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/crypt/gprs_cipher.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <openbsc/gsm_data.h>
#define GSM_EXTENSION_LENGTH 15
#define GSM_APN_LENGTH 102
struct gprs_llc_lle;
struct ctrl_handle;
struct gprs_subscr;
enum gsm48_gsm_cause;
/* TS 04.08 4.1.3.3 GMM mobility management states on the network side */
enum gprs_gmm_state {
GMM_DEREGISTERED, /* 4.1.3.3.1.1 */
GMM_COMMON_PROC_INIT, /* 4.1.3.3.1.2 */
GMM_REGISTERED_NORMAL, /* 4.1.3.3.2.1 */
GMM_REGISTERED_SUSPENDED, /* 4.1.3.3.2.2 */
GMM_DEREGISTERED_INIT, /* 4.1.3.3.1.4 */
};
/* TS 23.060 6.1.1 and 6.1.2 Mobility management states A/Gb and Iu mode */
enum gprs_pmm_state {
PMM_DETACHED,
PMM_CONNECTED,
PMM_IDLE,
MM_IDLE,
MM_READY,
MM_STANDBY,
};
enum gprs_mm_ctr {
GMM_CTR_PKTS_SIG_IN,
GMM_CTR_PKTS_SIG_OUT,
GMM_CTR_PKTS_UDATA_IN,
GMM_CTR_PKTS_UDATA_OUT,
GMM_CTR_BYTES_UDATA_IN,
GMM_CTR_BYTES_UDATA_OUT,
GMM_CTR_PDP_CTX_ACT,
GMM_CTR_SUSPEND,
GMM_CTR_PAGING_PS,
GMM_CTR_PAGING_CS,
GMM_CTR_RA_UPDATE,
};
enum gprs_pdp_ctx {
PDP_CTR_PKTS_UDATA_IN,
PDP_CTR_PKTS_UDATA_OUT,
PDP_CTR_BYTES_UDATA_IN,
PDP_CTR_BYTES_UDATA_OUT,
};
enum gprs_t3350_mode {
GMM_T3350_MODE_NONE,
GMM_T3350_MODE_ATT,
GMM_T3350_MODE_RAU,
GMM_T3350_MODE_PTMSI_REALL,
};
/* Authorization/ACL handling */
enum sgsn_auth_state {
SGSN_AUTH_UNKNOWN,
SGSN_AUTH_AUTHENTICATE,
SGSN_AUTH_UMTS_RESYNC,
SGSN_AUTH_ACCEPTED,
SGSN_AUTH_REJECTED
};
#define MS_RADIO_ACCESS_CAPA
enum sgsn_ggsn_lookup_state {
SGSN_GGSN_2DIGIT,
SGSN_GGSN_3DIGIT,
};
struct sgsn_ggsn_lookup {
int state;
struct sgsn_mm_ctx *mmctx;
/* APN string */
char apn_str[GSM_APN_LENGTH];
/* the original data */
struct msgb *orig_msg;
struct tlv_parsed tp;
/* for dealing with re-transmissions */
uint8_t nsapi;
uint8_t sapi;
uint8_t ti;
};
enum sgsn_ran_type {
/* GPRS/EDGE via Gb */
MM_CTX_T_GERAN_Gb,
/* UMTS via Iu */
MM_CTX_T_UTRAN_Iu,
/* GPRS/EDGE via Iu */
MM_CTX_T_GERAN_Iu,
};
struct service_info {
uint8_t type;
uint16_t pdp_status;
};
struct ranap_ue_conn_ctx;
/* According to TS 03.60, Table 5: SGSN MM and PDP Contexts */
/* Extended by 3GPP TS 23.060, Table 6: SGSN MM and PDP Contexts */
struct sgsn_mm_ctx {
struct llist_head list;
enum sgsn_ran_type ran_type;
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
enum gprs_gmm_state gmm_state;
enum gprs_pmm_state pmm_state; /* Iu: page when in PMM-IDLE mode */
uint32_t p_tmsi;
uint32_t p_tmsi_old; /* old P-TMSI before new is confirmed */
uint32_t p_tmsi_sig;
char imei[GSM23003_IMEISV_NUM_DIGITS+1];
/* Opt: Software Version Numbber / TS 23.195 */
char msisdn[GSM_EXTENSION_LENGTH];
struct gprs_ra_id ra;
struct {
uint16_t cell_id; /* Gb only */
uint32_t cell_id_age; /* Gb only */
uint8_t radio_prio_sms;
/* Additional bits not present in the GSM TS */
uint16_t nsei;
uint16_t bvci;
struct gprs_llc_llme *llme;
uint32_t tlli;
uint32_t tlli_new;
} gb;
struct {
int new_key;
uint16_t sac; /* Iu: Service Area Code */
uint32_t sac_age; /* Iu: Service Area Code age */
/* CSG ID */
/* CSG Membership */
/* Access Mode */
/* Seelected CN Operator ID (TS 23.251) */
/* CSG Subscription Data */
/* LIPA Allowed */
/* Voice Support Match Indicator */
struct ranap_ue_conn_ctx *ue_ctx;
struct service_info service;
} iu;
/* VLR number */
uint32_t new_sgsn_addr;
/* Authentication Triplet */
struct gsm_auth_tuple auth_triplet;
/* Kc */
/* Iu: CK, IK, KSI */
/* CKSN */
enum gprs_ciph_algo ciph_algo;
/* Auth & Ciphering Request reference from 3GPP TS 24.008 § 10.5.5.19: */
uint8_t ac_ref_nr_used;
struct {
uint8_t len;
uint8_t buf[50]; /* GSM 04.08 10.5.5.12a, extended in TS 24.008 */
} ms_radio_access_capa;
/* Supported Codecs (SRVCC) */
struct {
uint8_t len;
uint8_t buf[8]; /* GSM 04.08 10.5.5.12, extended in TS 24.008 */
} ms_network_capa;
/* UE Netowrk Capability (E-UTRAN) */
uint16_t drx_parms;
/* Active Time value for PSM */
int mnrg; /* MS reported to HLR? */
int ngaf; /* MS reported to MSC/VLR? */
int ppf; /* paging for GPRS + non-GPRS? */
/* Subscribed Charging Characteristics */
/* Trace Reference */
/* Trace Type */
/* Trigger ID */
/* OMC Identity */
/* SMS Parameters */
int recovery;
/* Access Restriction */
/* GPRS CSI (CAMEL) */
/* MG-CSI (CAMEL) */
/* Subscribed UE-AMBR */
/* UE-AMBR */
/* APN Subscribed */
struct llist_head pdp_list;
struct rate_ctr_group *ctrg;
struct osmo_timer_list timer;
unsigned int T; /* Txxxx number */
unsigned int num_T_exp; /* number of consecutive T expirations */
enum gprs_t3350_mode t3350_mode;
uint8_t t3370_id_type;
uint8_t pending_req; /* the request's message type */
/* TODO: There isn't much semantic difference between t3350_mode
* (refers to the timer) and pending_req (refers to the procedure),
* where mm->T == 3350 => mm->t3350_mode == f(mm->pending_req). Check
* whether one of them can be dropped. */
enum sgsn_auth_state auth_state;
int is_authenticated;
/* the string representation of the current hlr */
char hlr[GSM_EXTENSION_LENGTH];
/* the current GGSN look-up operation */
struct sgsn_ggsn_lookup *ggsn_lookup;
struct gprs_subscr *subscr;
};
#define LOGMMCTXP(level, mm, fmt, args...) \
LOGP(DMM, level, "MM(%s/%08x) " fmt, (mm) ? (mm)->imsi : "---", \
(mm) ? (mm)->p_tmsi : GSM_RESERVED_TMSI, ## args)
/* look-up a SGSN MM context based on TLLI + RAI */
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
const struct gprs_ra_id *raid);
struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t tmsi);
struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi);
struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx);
/* look-up by matching TLLI and P-TMSI (think twice before using this) */
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli,
const struct gprs_ra_id *raid);
/* Allocate a new SGSN MM context */
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli,
const struct gprs_ra_id *raid);
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx);
void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx);
struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx,
struct tlv_parsed *tp,
enum gsm48_gsm_cause *gsm_cause,
char *apn_str);
enum pdp_ctx_state {
PDP_STATE_NONE,
PDP_STATE_CR_REQ,
PDP_STATE_CR_CONF,
/* 04.08 / Figure 6.2 / 6.1.2.2 */
PDP_STATE_INACT_PEND,
PDP_STATE_INACTIVE = PDP_STATE_NONE,
};
enum pdp_type {
PDP_TYPE_NONE,
PDP_TYPE_ETSI_PPP,
PDP_TYPE_IANA_IPv4,
PDP_TYPE_IANA_IPv6,
};
struct sgsn_pdp_ctx {
struct llist_head list; /* list_head for mmctx->pdp_list */
struct llist_head g_list; /* list_head for global list */
struct sgsn_mm_ctx *mm; /* back pointer to MM CTX */
int destroy_ggsn; /* destroy it on destruction */
struct sgsn_ggsn_ctx *ggsn; /* which GGSN serves this PDP */
struct rate_ctr_group *ctrg;
//unsigned int id;
struct pdp_t *lib; /* pointer to libgtp PDP ctx */
enum pdp_ctx_state state;
enum pdp_type type;
uint32_t address;
char *apn_subscribed;
//char *apn_used;
uint16_t nsapi; /* SNDCP */
uint16_t sapi; /* LLC */
uint8_t ti; /* transaction identifier */
int vplmn_allowed;
uint32_t qos_profile_subscr;
//uint32_t qos_profile_req;
//uint32_t qos_profile_neg;
uint8_t radio_prio;
//uint32_t charging_id;
struct osmo_timer_list timer;
unsigned int T; /* Txxxx number */
unsigned int num_T_exp; /* number of consecutive T expirations */
struct osmo_timer_list cdr_timer; /* CDR record wird timer */
struct timespec cdr_start; /* The start of the CDR */
uint64_t cdr_bytes_in;
uint64_t cdr_bytes_out;
uint32_t cdr_charging_id;
};
#define LOGPDPCTXP(level, pdp, fmt, args...) \
LOGP(DGPRS, level, "PDP(%s/%u) " \
fmt, (pdp)->mm ? (pdp)->mm->imsi : "---", (pdp)->ti, ## args)
/* look up PDP context by MM context and NSAPI */
struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
uint8_t nsapi);
/* look up PDP context by MM context and transaction ID */
struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
uint8_t tid);
struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
uint8_t nsapi);
void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp);
void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp);
struct sgsn_ggsn_ctx {
struct llist_head list;
uint32_t id;
unsigned int gtp_version;
struct in_addr remote_addr;
int remote_restart_ctr;
struct gsn_t *gsn;
};
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id);
void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc);
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id);
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr);
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id);
struct apn_ctx {
struct llist_head list;
struct sgsn_ggsn_ctx *ggsn;
char *name;
char *imsi_prefix;
char *description;
};
struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix);
void sgsn_apn_ctx_free(struct apn_ctx *actx);
struct apn_ctx *sgsn_apn_ctx_by_name(const char *name, const char *imsi_prefix);
struct apn_ctx *sgsn_apn_ctx_match(const char *name, const char *imsi_prefix);
extern struct llist_head sgsn_mm_ctxts;
extern struct llist_head sgsn_ggsn_ctxts;
extern struct llist_head sgsn_apn_ctxts;
extern struct llist_head sgsn_pdp_ctxts;
uint32_t sgsn_alloc_ptmsi(void);
void sgsn_inst_init(void);
/* High-level function to be called in case a GGSN has disappeared or
* ottherwise lost state (recovery procedure) */
int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn);
char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len);
/*
* ctrl interface related work
*/
struct gsm_network;
struct ctrl_handle *sgsn_controlif_setup(struct gsm_network *,
const char *bind_addr, uint16_t port);
int sgsn_ctrl_cmds_install(void);
/*
* Authorization/ACL handling
*/
struct imsi_acl_entry {
struct llist_head list;
char imsi[16+1];
};
/* see GSM 09.02, 17.7.1, PDP-Context and GPRSSubscriptionData */
/* see GSM 09.02, B.1, gprsSubscriptionData */
struct sgsn_subscriber_pdp_data {
struct llist_head list;
unsigned int context_id;
uint16_t pdp_type;
char apn_str[GSM_APN_LENGTH];
uint8_t qos_subscribed[20];
size_t qos_subscribed_len;
uint8_t pdp_charg[2];
bool has_pdp_charg;
};
struct sgsn_subscriber_data {
struct sgsn_mm_ctx *mm;
struct gsm_auth_tuple auth_triplets[5];
int auth_triplets_updated;
struct llist_head pdp_list;
int error_cause;
uint8_t msisdn[9];
size_t msisdn_len;
uint8_t hlr[9];
size_t hlr_len;
uint8_t pdp_charg[2];
bool has_pdp_charg;
};
#define SGSN_ERROR_CAUSE_NONE (-1)
#define LOGGSUBSCRP(level, subscr, fmt, args...) \
LOGP(DGPRS, level, "SUBSCR(%s) " fmt, \
(subscr) ? (subscr)->imsi : "---", \
## args)
struct sgsn_config;
struct sgsn_instance;
extern const struct value_string *sgsn_auth_state_names;
void sgsn_auth_init(void);
struct imsi_acl_entry *sgsn_acl_lookup(const char *imsi, struct sgsn_config *cfg);
int sgsn_acl_add(const char *imsi, struct sgsn_config *cfg);
int sgsn_acl_del(const char *imsi, struct sgsn_config *cfg);
/* Request authorization */
int sgsn_auth_request(struct sgsn_mm_ctx *mm);
enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mm);
void sgsn_auth_update(struct sgsn_mm_ctx *mm);
struct gsm_auth_tuple *sgsn_auth_get_tuple(struct sgsn_mm_ctx *mmctx,
unsigned key_seq);
/*
* GPRS subscriber data
*/
#define GPRS_SUBSCRIBER_FIRST_CONTACT 0x00000001
#define GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING (1 << 16)
#define GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING (1 << 17)
#define GPRS_SUBSCRIBER_CANCELLED (1 << 18)
#define GPRS_SUBSCRIBER_ENABLE_PURGE (1 << 19)
#define GPRS_SUBSCRIBER_UPDATE_PENDING_MASK ( \
GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING | \
GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING \
)
int gprs_subscr_init(struct sgsn_instance *sgi);
int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx);
int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx,
const uint8_t *auts,
const uint8_t *auts_rand);
int gprs_subscr_auth_sync(struct gprs_subscr *subscr,
const uint8_t *auts, const uint8_t *auts_rand);
void gprs_subscr_cleanup(struct gprs_subscr *subscr);
struct gprs_subscr *gprs_subscr_get_or_create(const char *imsi);
struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx( struct sgsn_mm_ctx *mmctx);
struct gprs_subscr *gprs_subscr_get_by_imsi(const char *imsi);
void gprs_subscr_cancel(struct gprs_subscr *subscr);
void gprs_subscr_update(struct gprs_subscr *subscr);
void gprs_subscr_update_auth_info(struct gprs_subscr *subscr);
int gprs_subscr_rx_gsup_message(struct msgb *msg);
/* Called on subscriber data updates */
void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx);
int gprs_sndcp_vty_init(void);
struct sgsn_instance;
int sgsn_gtp_init(struct sgsn_instance *sgi);
void sgsn_rate_ctr_init();
#endif /* _GPRS_SGSN_H */

View File

@ -1,79 +0,0 @@
#ifndef _INT_SNDCP_H
#define _INT_SNDCP_H
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
/* A fragment queue header, maintaining list of fragments for one N-PDU */
struct defrag_state {
/* PDU number for which the defragmentation state applies */
uint16_t npdu;
/* highest segment number we have received so far */
uint8_t highest_seg;
/* bitmask of the segments we already have */
uint32_t seg_have;
/* do we still expect more segments? */
unsigned int no_more;
/* total length of all segments together */
unsigned int tot_len;
/* linked list of defrag_queue_entry: one for each fragment */
struct llist_head frag_list;
struct osmo_timer_list timer;
/* Holds state to know which compression mode is used
* when the packet is re-assembled */
uint8_t pcomp;
uint8_t dcomp;
/* Holds the pointers to the compression entity list
* that is used when the re-assembled packet is decompressed */
struct llist_head *proto;
struct llist_head *data;
};
/* See 6.7.1.2 Reassembly */
enum sndcp_rx_state {
SNDCP_RX_S_FIRST,
SNDCP_RX_S_SUBSEQ,
SNDCP_RX_S_DISCARD,
};
struct gprs_sndcp_entity {
struct llist_head list;
/* FIXME: move this RA_ID up to the LLME or even higher */
struct gprs_ra_id ra_id;
/* reference to the LLC Entity below this SNDCP entity */
struct gprs_llc_lle *lle;
/* The NSAPI we shall use on top of LLC */
uint8_t nsapi;
/* NPDU number for the GTP->SNDCP side */
uint16_t tx_npdu_nr;
/* SNDCP eeceiver state */
enum sndcp_rx_state rx_state;
/* The defragmentation queue */
struct defrag_state defrag;
};
extern struct llist_head gprs_sndcp_entities;
/* Set of SNDCP-XID negotiation (See also: TS 144 065,
* Section 6.8 XID parameter negotiation) */
int sndcp_sn_xid_req(struct gprs_llc_lle *lle, uint8_t nsapi);
/* Process SNDCP-XID indication (See also: TS 144 065,
* Section 6.8 XID parameter negotiation) */
int sndcp_sn_xid_ind(struct gprs_llc_xid_field *xid_field_indication,
struct gprs_llc_xid_field *xid_field_response,
struct gprs_llc_lle *lle);
/* Process SNDCP-XID indication
* (See also: TS 144 065, Section 6.8 XID parameter negotiation) */
int sndcp_sn_xid_conf(struct gprs_llc_xid_field *xid_field_conf,
struct gprs_llc_xid_field *xid_field_request,
struct gprs_llc_lle *lle);
#endif /* INT_SNDCP_H */

View File

@ -1,82 +0,0 @@
/* GPRS SNDCP header compression entity management tools */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <openbsc/gprs_sndcp_xid.h>
/* Header / Data compression entity */
struct gprs_sndcp_comp {
struct llist_head list;
/* Serves as an ID in case we want to delete this entity later */
unsigned int entity; /* see also: 6.5.1.1.3 and 6.6.1.1.3 */
/* Specifies to which NSAPIs the compression entity is assigned */
uint8_t nsapi_len; /* Number of applicable NSAPIs (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
/* Assigned pcomp values */
uint8_t comp_len; /* Number of contained PCOMP / DCOMP values */
uint8_t comp[MAX_COMP]; /* see also: 6.5.1.1.5 and 6.6.1.1.5 */
/* Algorithm parameters */
int algo; /* Algorithm type (see gprs_sndcp_xid.h) */
int compclass; /* See gprs_sndcp_xid.h/c */
void *state; /* Algorithm status and parameters */
};
#define MAX_COMP 16 /* Maximum number of possible pcomp/dcomp values */
#define MAX_NSAPI 11 /* Maximum number usable NSAPIs */
/* Allocate a compression enitiy list */
struct llist_head *gprs_sndcp_comp_alloc(const void *ctx);
/* Free a compression entitiy list */
void gprs_sndcp_comp_free(struct llist_head *comp_entities);
/* Delete a compression entity */
void gprs_sndcp_comp_delete(struct llist_head *comp_entities, unsigned int entity);
/* Create and Add a new compression entity
* (returns a pointer to the compression entity that has just been created) */
struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx,
struct llist_head *comp_entities,
const struct gprs_sndcp_comp_field
*comp_field);
/* Find which compression entity handles the specified pcomp/dcomp */
struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head
*comp_entities, uint8_t comp);
/* Find which compression entity handles the specified nsapi */
struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head
*comp_entities, uint8_t nsapi);
/* Find a comp_index for a given pcomp/dcomp value */
uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity,
uint8_t comp);
/* Find a pcomp/dcomp value for a given comp_index */
uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
uint8_t comp_index);

View File

@ -1,53 +0,0 @@
/* GPRS SNDCP data compression handler */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <openbsc/gprs_sndcp_comp.h>
/* Note: The decompressed packet may have a maximum size of:
* Return value * MAX_DATADECOMPR_FAC */
#define MAX_DATADECOMPR_FAC 10
/* Note: In unacknowledged mode (SN_UNITDATA), the comression state is reset
* for every NPDU. The compressor needs a reasonably large payload to operate
* effectively (yield positive compression gain). For packets shorter than 100
* byte, no positive compression gain can be expected so we will skip the
* compression for short packets. */
#define MIN_COMPR_PAYLOAD 100
/* Initalize data compression */
int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
const struct gprs_sndcp_comp_field *comp_field);
/* Terminate data compression */
void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity);
/* Expand packet */
int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
const struct llist_head *comp_entities);
/* Compress packet */
int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
const struct llist_head *comp_entities,
uint8_t nsapi);

View File

@ -1,46 +0,0 @@
/* GPRS SNDCP header compression handler */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <openbsc/gprs_sndcp_comp.h>
/* Note: The decompressed packet may have a maximum size of:
* Return value + MAX_DECOMPR_INCR */
#define MAX_HDRDECOMPR_INCR 64
/* Initalize header compression */
int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
const struct gprs_sndcp_comp_field *comp_field);
/* Terminate header compression */
void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity);
/* Expand packet header */
int gprs_sndcp_pcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
const struct llist_head *comp_entities);
/* Compress packet header */
int gprs_sndcp_pcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
const struct llist_head *comp_entities,
uint8_t nsapi);

View File

@ -1,218 +0,0 @@
/* GPRS SNDCP XID field encoding/decoding as per 3GPP TS 44.065 */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#define DEFAULT_SNDCP_VERSION 0 /* See 3GPP TS 44.065, clause 8 */
#define MAX_ENTITIES 32 /* 3GPP TS 44.065 reserves 5 bit
* for compression enitity number */
#define MAX_COMP 16 /* Maximum number of possible pcomp/dcomp values */
#define MAX_NSAPI 11 /* Maximum number usable NSAPIs */
#define MAX_ROHC 16 /* Maximum number of ROHC compression profiles */
/* According to: 3GPP TS 44.065, 6.5.1.1 Format of the protocol control
* information compression field (Figure 7) and 3GPP TS 44.065,
* 6.6.1.1 Format of the data compression field (Figure 9) */
struct gprs_sndcp_comp_field {
struct llist_head list;
/* Propose bit (P), see also: 6.5.1.1.2 and 6.6.1.1.2 */
unsigned int p;
/* Entity number, see also: 6.5.1.1.3 and 6.6.1.1.3 */
unsigned int entity;
/* Algorithm identifier, see also: 6.5.1.1.4 and 6.6.1.1.4 */
int algo;
/* Number of contained PCOMP / DCOMP values */
uint8_t comp_len;
/* PCOMP / DCOMP values, see also: 6.5.1.1.5 and 6.6.1.1.5 */
uint8_t comp[MAX_COMP];
/* Note: Only one of the following struct pointers may,
be used. Unused pointers must be set to NULL! */
struct gprs_sndcp_pcomp_rfc1144_params *rfc1144_params;
struct gprs_sndcp_pcomp_rfc2507_params *rfc2507_params;
struct gprs_sndcp_pcomp_rohc_params *rohc_params;
struct gprs_sndcp_dcomp_v42bis_params *v42bis_params;
struct gprs_sndcp_dcomp_v44_params *v44_params;
};
/* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */
enum gprs_sndcp_hdr_comp_algo {
RFC_1144, /* TCP/IP header compression, see also 6.5.2 */
RFC_2507, /* TCP/UDP/IP header compression, see also: 6.5.3 */
ROHC /* Robust Header Compression, see also 6.5.4 */
};
/* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */
enum gprs_sndcp_data_comp_algo {
V42BIS, /* V.42bis data compression, see also 6.6.2 */
V44 /* V44 data compression, see also: 6.6.3 */
};
/* According to: 3GPP TS 44.065, 8 SNDCP XID parameters */
enum gprs_sndcp_xid_param_types {
SNDCP_XID_VERSION_NUMBER,
SNDCP_XID_DATA_COMPRESSION, /* See also: subclause 6.6.1 */
SNDCP_XID_PROTOCOL_COMPRESSION, /* See also: subclause 6.5.1 */
};
/* According to: 3GPP TS 44.065, 6.5.2.1 Parameters (Table 5) */
struct gprs_sndcp_pcomp_rfc1144_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int s01; /* (default 15) */
};
/* According to: 3GPP TS 44.065, 6.5.2.2 Assignment of PCOMP values */
enum gprs_sndcp_pcomp_rfc1144_pcomp {
RFC1144_PCOMP1, /* Uncompressed TCP */
RFC1144_PCOMP2, /* Compressed TCP */
RFC1144_PCOMP_NUM /* Number of pcomp values */
};
/* According to: 3GPP TS 44.065, 6.5.3.1 Parameters (Table 6) */
struct gprs_sndcp_pcomp_rfc2507_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int f_max_period; /* (default 256) */
int f_max_time; /* (default 5) */
int max_header; /* (default 168) */
int tcp_space; /* (default 15) */
int non_tcp_space; /* (default 15) */
};
/* According to: 3GPP TS 44.065, 6.5.3.2 Assignment of PCOMP values for RFC2507 */
enum gprs_sndcp_pcomp_rfc2507_pcomp {
RFC2507_PCOMP1, /* Full Header */
RFC2507_PCOMP2, /* Compressed TCP */
RFC2507_PCOMP3, /* Compressed TCP non delta */
RFC2507_PCOMP4, /* Compressed non TCP */
RFC2507_PCOMP5, /* Context state */
RFC2507_PCOMP_NUM /* Number of pcomp values */
};
/* According to: 3GPP TS 44.065, 6.5.4.1 Parameter (Table 10) */
struct gprs_sndcp_pcomp_rohc_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int max_cid; /* (default 15) */
int max_header; /* (default 168) */
uint8_t profile_len; /* (default 1) */
uint16_t profile[MAX_ROHC]; /* (default 0, ROHC uncompressed) */
};
/* According to: 3GPP TS 44.065, 6.5.4.2 Assignment of PCOMP values for ROHC */
enum gprs_sndcp_pcomp_rohc_pcomp {
ROHC_PCOMP1, /* ROHC small CIDs */
ROHC_PCOMP2, /* ROHC large CIDs */
ROHC_PCOMP_NUM /* Number of pcomp values */
};
/* ROHC compression profiles, see also:
http://www.iana.org/assignments/rohc-pro-ids/rohc-pro-ids.xhtml */
enum gprs_sndcp_xid_rohc_profiles {
ROHC_UNCOMPRESSED = 0x0000, /* ROHC uncompressed [RFC5795] */
ROHC_RTP = 0x0001, /* ROHC RTP [RFC3095] */
ROHCV2_RTP = 0x0101, /* ROHCv2 RTP [RFC5225] */
ROHC_UDP = 0x0002, /* ROHC UDP [RFC3095] */
ROHCv2_UDP = 0x0102, /* ROHCv2 UDP [RFC5225] */
ROHC_ESP = 0x0003, /* ROHC ESP [RFC3095] */
ROHCV2_ESP = 0x0103, /* ROHCv2 ESP [RFC5225] */
ROHC_IP = 0x0004, /* ROHC IP [RFC3843] */
ROHCV2_IP = 0x0104, /* ROHCv2 IP [RFC5225] */
ROHC_LLA = 0x0005, /* ROHC LLA [RFC4362] */
ROHC_LLA_WITH_R_MODE = 0x0105, /* ROHC LLA with R-mode [RFC3408] */
ROHC_TCP = 0x0006, /* ROHC TCP [RFC6846] */
ROHC_RTP_UDP_LITE = 0x0007, /* ROHC RTP/UDP-Lite [RFC4019] */
ROHCV2_RTP_UDP_LITE = 0x0107, /* ROHCv2 RTP/UDP-Lite [RFC5225] */
ROHC_UDP_LITE = 0x0008, /* ROHC UDP-Lite [RFC4019] */
ROHCV2_UDP_LITE = 0x0108, /* ROHCv2 UDP-Lite [RFC5225] */
};
/* According to: 3GPP TS 44.065, 6.6.2.1 Parameters (Table 7a) */
struct gprs_sndcp_dcomp_v42bis_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int p0; /* (default 3) */
int p1; /* (default 2048) */
int p2; /* (default 20) */
};
/* According to: 3GPP TS 44.065, 6.6.2.2 Assignment of DCOMP values */
enum gprs_sndcp_dcomp_v42bis_dcomp {
V42BIS_DCOMP1, /* V.42bis enabled */
V42BIS_DCOMP_NUM /* Number of dcomp values */
};
/* According to: 3GPP TS 44.065, 6.6.3.1 Parameters (Table 7c) */
struct gprs_sndcp_dcomp_v44_params {
uint8_t nsapi_len; /* Number of applicable NSAPIs
* (default 0) */
uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */
int c0; /* (default 10000000) */
int p0; /* (default 3) */
int p1t; /* Refer to subclause 6.6.3.1.4 */
int p1r; /* Refer to subclause 6.6.3.1.5 */
int p3t; /* (default 3 x p1t) */
int p3r; /* (default 3 x p1r) */
};
/* According to: 3GPP TS 44.065, 6.6.3.2 Assignment of DCOMP values */
enum gprs_sndcp_dcomp_v44_dcomp {
V44_DCOMP1, /* Packet method compressed */
V44_DCOMP2, /* Multi packet method compressed */
V44_DCOMP_NUM /* Number of dcomp values */
};
/* Transform a list with compression fields into an SNDCP-XID message (dst) */
int gprs_sndcp_compile_xid(uint8_t *dst, unsigned int dst_maxlen,
const struct llist_head *comp_fields, int version);
/* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */
struct llist_head *gprs_sndcp_parse_xid(int *version,
const void *ctx,
const uint8_t *src,
unsigned int src_len,
const struct llist_head
*comp_fields_req);
/* Find out to which compression class the specified comp-field belongs
* (header compression or data compression?) */
int gprs_sndcp_get_compression_class(
const struct gprs_sndcp_comp_field *comp_field);
/* Dump a list with SNDCP-XID fields (Debug) */
void gprs_sndcp_dump_comp_fields(const struct llist_head *comp_fields,
unsigned int logl);

View File

@ -1,31 +0,0 @@
/* GPRS subscriber details for use in SGSN land */
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
extern struct llist_head * const gprs_subscribers;
struct gprs_subscr {
struct llist_head entry;
int use_count;
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
uint32_t tmsi;
char imei[GSM23003_IMEISV_NUM_DIGITS+1];
bool authorized;
bool keep_in_ram;
uint32_t flags;
uint16_t lac;
struct sgsn_subscriber_data *sgsn_data;
};
struct gprs_subscr *_gprs_subscr_get(struct gprs_subscr *gsub,
const char *file, int line);
struct gprs_subscr *_gprs_subscr_put(struct gprs_subscr *gsub,
const char *file, int line);
#define gprs_subscr_get(gsub) _gprs_subscr_get(gsub, __BASE_FILE__, __LINE__)
#define gprs_subscr_put(gsub) _gprs_subscr_put(gsub, __BASE_FILE__, __LINE__)

View File

@ -1,44 +0,0 @@
/* GPRS utility functions */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010-2014 by On-Waves
* (C) 2013 by Holger Hans Peter Freyther
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <stdint.h>
#include <sys/types.h>
struct msgb;
struct gprs_ra_id;
struct msgb *gprs_msgb_copy(const struct msgb *msg, const char *name);
int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area,
size_t old_size, size_t new_size);
int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str);
/* GSM 04.08, 10.5.7.3 GPRS Timer */
int gprs_tmr_to_secs(uint8_t tmr);
uint8_t gprs_secs_to_tmr_floor(int secs);
int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len);
int gprs_is_mi_imsi(const uint8_t *value, size_t value_len);
int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi);
void gprs_parse_tmsi(const uint8_t *value, uint32_t *tmsi);
int gprs_ra_id_equals(const struct gprs_ra_id *id1, const struct gprs_ra_id *id2);

View File

@ -1,47 +0,0 @@
/*
* (C) 2013 by On-Waves
* (C) 2013 by Holger Hans Peter Freyther <zecke@selfish.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NAT_REWRITE_FILE_H
#define NAT_REWRITE_FILE_H
#include <osmocom/core/linuxrbtree.h>
struct vty;
struct nat_rewrite_rule {
/* For digits 0-9 and + */
struct nat_rewrite_rule *rules[11];
char empty;
char prefix[14];
char rewrite[6];
};
struct nat_rewrite {
struct nat_rewrite_rule rule;
size_t prefixes;
};
struct nat_rewrite *nat_rewrite_parse(void *ctx, const char *filename);
struct nat_rewrite_rule *nat_rewrite_lookup(struct nat_rewrite *, const char *prefix);
void nat_rewrite_dump(struct nat_rewrite *rewr);
void nat_rewrite_dump_vty(struct vty *vty, struct nat_rewrite *rewr);
#endif

View File

@ -27,7 +27,6 @@
#include <osmocom/core/timer.h>
#include <openbsc/gsm_data.h>
#include <openbsc/bsc_subscriber.h>
/**
* A pending paging request

View File

@ -1,35 +0,0 @@
#ifndef _PCU_IF_H
#define _PCU_IF_H
#include <osmocom/gsm/l1sap.h>
extern int pcu_direct;
struct pcu_sock_state {
struct gsm_network *net;
struct osmo_fd listen_bfd; /* fd for listen socket */
struct osmo_fd conn_bfd; /* fd for connection to lcr */
struct llist_head upqueue; /* queue for sending messages */
};
/* PCU relevant information has changed; Inform PCU (if connected) */
void pcu_info_update(struct gsm_bts *bts);
/* Forward rach indication to PCU */
int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn,
uint8_t is_11bit, enum ph_burst_type burst_type);
/* Confirm the sending of an immediate assignment to the pcu */
int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli);
/* Confirm the sending of an immediate assignment to the pcu */
int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli);
/* Open connection to PCU */
int pcu_sock_init(const char *path, struct gsm_bts *bts);
/* Close connection to PCU */
void pcu_sock_exit(struct gsm_bts *bts);
#endif /* _PCU_IF_H */

View File

@ -1,176 +0,0 @@
#ifndef _PCUIF_PROTO_H
#define _PCUIF_PROTO_H
#define PCU_IF_VERSION 0x08
/* msg_type */
#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */
#define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */
#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */
#define PCU_IF_MSG_DATA_CNF_DT 0x11 /* confirm (with direct tlli) */
#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */
#define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */
#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */
#define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */
#define PCU_IF_MSG_TIME_IND 0x52 /* GSM time indication */
#define PCU_IF_MSG_PAG_REQ 0x60 /* paging request */
/* sapi */
#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */
#define PCU_IF_SAPI_AGCH 0x02 /* assignment on AGCH */
#define PCU_IF_SAPI_PCH 0x03 /* paging/assignment on PCH */
#define PCU_IF_SAPI_BCCH 0x04 /* SI on BCCH */
#define PCU_IF_SAPI_PDTCH 0x05 /* packet data/control/ccch block */
#define PCU_IF_SAPI_PRACH 0x06 /* packet random access channel */
#define PCU_IF_SAPI_PTCCH 0x07 /* packet TA control channel */
#define PCU_IF_SAPI_AGCH_DT 0x08 /* assignment on AGCH but with additional TLLI */
/* flags */
#define PCU_IF_FLAG_ACTIVE (1 << 0)/* BTS is active */
#define PCU_IF_FLAG_SYSMO (1 << 1)/* access PDCH of sysmoBTS directly */
#define PCU_IF_FLAG_CS1 (1 << 16)
#define PCU_IF_FLAG_CS2 (1 << 17)
#define PCU_IF_FLAG_CS3 (1 << 18)
#define PCU_IF_FLAG_CS4 (1 << 19)
#define PCU_IF_FLAG_MCS1 (1 << 20)
#define PCU_IF_FLAG_MCS2 (1 << 21)
#define PCU_IF_FLAG_MCS3 (1 << 22)
#define PCU_IF_FLAG_MCS4 (1 << 23)
#define PCU_IF_FLAG_MCS5 (1 << 24)
#define PCU_IF_FLAG_MCS6 (1 << 25)
#define PCU_IF_FLAG_MCS7 (1 << 26)
#define PCU_IF_FLAG_MCS8 (1 << 27)
#define PCU_IF_FLAG_MCS9 (1 << 28)
struct gsm_pcu_if_data {
uint8_t sapi;
uint8_t len;
uint8_t data[162];
uint32_t fn;
uint16_t arfcn;
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t block_nr;
int8_t rssi;
uint16_t ber10k; /*!< \brief BER in units of 0.01% */
int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */
int16_t lqual_cb; /* !< \brief Link quality in centiBel */
} __attribute__ ((packed));
/* data confirmation with direct tlli (instead of raw mac block with tlli) */
struct gsm_pcu_if_data_cnf_dt {
uint8_t sapi;
uint32_t tlli;
uint32_t fn;
uint16_t arfcn;
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t block_nr;
int8_t rssi;
uint16_t ber10k; /*!< \brief BER in units of 0.01% */
int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */
int16_t lqual_cb; /* !< \brief Link quality in centiBel */
} __attribute__ ((packed));
struct gsm_pcu_if_rts_req {
uint8_t sapi;
uint8_t spare[3];
uint32_t fn;
uint16_t arfcn;
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t block_nr;
} __attribute__ ((packed));
struct gsm_pcu_if_rach_ind {
uint8_t sapi;
uint16_t ra;
int16_t qta;
uint32_t fn;
uint16_t arfcn;
uint8_t is_11bit;
uint8_t burst_type;
} __attribute__ ((packed));
struct gsm_pcu_if_info_trx {
uint16_t arfcn;
uint8_t pdch_mask; /* PDCH channels per TS */
uint8_t spare;
uint8_t tsc[8]; /* TSC per channel */
uint32_t hlayer1;
} __attribute__ ((packed));
struct gsm_pcu_if_info_ind {
uint32_t version;
uint32_t flags;
struct gsm_pcu_if_info_trx trx[8]; /* TRX infos per BTS */
uint8_t bsic;
/* RAI */
uint16_t mcc, mnc, lac, rac;
/* NSE */
uint16_t nsei;
uint8_t nse_timer[7];
uint8_t cell_timer[11];
/* cell */
uint16_t cell_id;
uint16_t repeat_time;
uint8_t repeat_count;
uint16_t bvci;
uint8_t t3142;
uint8_t t3169;
uint8_t t3191;
uint8_t t3193_10ms;
uint8_t t3195;
uint8_t n3101;
uint8_t n3103;
uint8_t n3105;
uint8_t cv_countdown;
uint16_t dl_tbf_ext;
uint16_t ul_tbf_ext;
uint8_t initial_cs;
uint8_t initial_mcs;
/* NSVC */
uint16_t nsvci[2];
uint16_t local_port[2];
uint16_t remote_port[2];
uint32_t remote_ip[2];
} __attribute__ ((packed));
struct gsm_pcu_if_act_req {
uint8_t activate;
uint8_t trx_nr;
uint8_t ts_nr;
uint8_t spare;
} __attribute__ ((packed));
struct gsm_pcu_if_time_ind {
uint32_t fn;
} __attribute__ ((packed));
struct gsm_pcu_if_pag_req {
uint8_t sapi;
uint8_t chan_needed;
uint8_t identity_lv[9];
} __attribute__ ((packed));
struct gsm_pcu_if {
/* context based information */
uint8_t msg_type; /* message type */
uint8_t bts_nr; /* bts number */
uint8_t spare[2];
union {
struct gsm_pcu_if_data data_req;
struct gsm_pcu_if_data data_cnf;
struct gsm_pcu_if_data_cnf_dt data_cnf_dt;
struct gsm_pcu_if_data data_ind;
struct gsm_pcu_if_rts_req rts_req;
struct gsm_pcu_if_rach_ind rach_ind;
struct gsm_pcu_if_info_ind info_ind;
struct gsm_pcu_if_act_req act_req;
struct gsm_pcu_if_time_ind time_ind;
struct gsm_pcu_if_pag_req pag_req;
} u;
} __attribute__ ((packed));
#endif /* _PCUIF_PROTO_H */

View File

@ -1,191 +0,0 @@
#ifndef _SGSN_H
#define _SGSN_H
#include <osmocom/core/msgb.h>
#include <osmocom/crypt/gprs_cipher.h>
#include <osmocom/gprs/gprs_ns.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/oap_client.h>
#include <openbsc/common.h>
#include <ares.h>
struct gprs_gsup_client;
struct hostent;
enum sgsn_auth_policy {
SGSN_AUTH_POLICY_OPEN,
SGSN_AUTH_POLICY_CLOSED,
SGSN_AUTH_POLICY_ACL_ONLY,
SGSN_AUTH_POLICY_REMOTE
};
enum sgsn_rate_ctr_keys {
CTR_LLC_DL_BYTES,
CTR_LLC_UL_BYTES,
CTR_LLC_DL_PACKETS,
CTR_LLC_UL_PACKETS,
CTR_GPRS_ATTACH_REQUEST,
CTR_GPRS_ATTACH_ACKED,
CTR_GPRS_ATTACH_REJECTED,
CTR_GPRS_DETACH_REQUEST,
CTR_GPRS_DETACH_ACKED,
CTR_GPRS_ROUTING_AREA_REQUEST,
CTR_GPRS_ROUTING_AREA_ACKED,
CTR_GPRS_ROUTING_AREA_REJECT,
/* PDP single packet counter / GSM 04.08 9.5.1 - 9.5.9 */
CTR_PDP_ACTIVATE_REQUEST,
CTR_PDP_ACTIVATE_REJECT,
CTR_PDP_ACTIVATE_ACCEPT,
CTR_PDP_REQUEST_ACTIVATE, /* unused */
CTR_PDP_REQUEST_ACTIVATE_REJ, /* unused */
CTR_PDP_MODIFY_REQUEST, /* unsued */
CTR_PDP_MODIFY_ACCEPT, /* unused */
CTR_PDP_DL_DEACTIVATE_REQUEST,
CTR_PDP_DL_DEACTIVATE_ACCEPT,
CTR_PDP_UL_DEACTIVATE_REQUEST,
CTR_PDP_UL_DEACTIVATE_ACCEPT,
};
struct sgsn_cdr {
char *filename;
int interval;
};
struct sgsn_config {
/* parsed from config file */
char *gtp_statedir;
struct sockaddr_in gtp_listenaddr;
/* misc */
struct gprs_ns_inst *nsi;
enum sgsn_auth_policy auth_policy;
enum gprs_ciph_algo cipher;
struct llist_head imsi_acl;
struct sockaddr_in gsup_server_addr;
int gsup_server_port;
int require_authentication;
int require_update_location;
/* CDR configuration */
struct sgsn_cdr cdr;
struct {
int T3312;
int T3322;
int T3350;
int T3360;
int T3370;
int T3313;
int T3314;
int T3316;
int T3385;
int T3386;
int T3395;
int T3397;
} timers;
int dynamic_lookup;
struct oap_client_config oap;
/* RFC1144 TCP/IP header compression */
struct {
int active;
int passive;
int s01;
} pcomp_rfc1144;
/* V.42vis data compression */
struct {
int active;
int passive;
int p0;
int p1;
int p2;
} dcomp_v42bis;
struct {
int rab_assign_addr_enc;
} iu;
};
struct sgsn_instance {
char *config_file;
struct sgsn_config cfg;
/* File descriptor wrappers for LibGTP */
struct osmo_fd gtp_fd0;
struct osmo_fd gtp_fd1c;
struct osmo_fd gtp_fd1u;
/* Timer for libGTP */
struct osmo_timer_list gtp_timer;
/* GSN instance for libgtp */
struct gsn_t *gsn;
/* Subscriber */
struct gsup_client *gsup_client;
/* LLME inactivity timer */
struct osmo_timer_list llme_timer;
/* c-ares event loop integration */
struct osmo_timer_list ares_timer;
struct llist_head ares_fds;
ares_channel ares_channel;
struct ares_addr_node *ares_servers;
struct rate_ctr_group *rate_ctrs;
};
extern struct sgsn_instance *sgsn;
/* sgsn_vty.c */
int sgsn_vty_init(struct sgsn_config *cfg);
int sgsn_parse_config(const char *config_file);
/* sgsn.c */
/* Main input function for Gb proxy */
int sgsn_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci);
struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
struct sgsn_mm_ctx *mmctx,
uint16_t nsapi,
struct tlv_parsed *tp);
int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx);
void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen);
/* gprs_sndcp.c */
/* Entry point for the SNSM-ACTIVATE.indication */
int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi);
/* Entry point for the SNSM-DEACTIVATE.indication */
int sndcp_sm_deactivate_ind(struct gprs_llc_lle *lle, uint8_t nsapi);
/* Called by SNDCP when it has received/re-assembled a N-PDU */
int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi,
struct msgb *msg, uint32_t npdu_len, uint8_t *npdu);
int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi,
void *mmcontext);
int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle,
uint8_t *hdr, uint16_t len);
/*
* CDR related functionality
*/
int sgsn_cdr_init(struct sgsn_instance *sgsn);
/*
* C-ARES related functionality
*/
int sgsn_ares_init(struct sgsn_instance *sgsn);
int sgsn_ares_query(struct sgsn_instance *sgsm, const char *name, ares_host_callback cb, void *data);
#endif

View File

@ -1,147 +0,0 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* v42bis.h
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2005, 2011 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*! \page v42bis_page V.42bis modem data compression
\section v42bis_page_sec_1 What does it do?
The v.42bis specification defines a data compression scheme, to work in
conjunction with the error correction scheme defined in V.42.
\section v42bis_page_sec_2 How does it work?
*/
#include <stdint.h>
#if !defined(_SPANDSP_V42BIS_H_)
#define _SPANDSP_V42BIS_H_
#define SPAN_DECLARE(x) x
#define V42BIS_MIN_STRING_SIZE 6
#define V42BIS_MAX_STRING_SIZE 250
#define V42BIS_MIN_DICTIONARY_SIZE 512
#define V42BIS_MAX_BITS 12
#define V42BIS_MAX_CODEWORDS 4096 /* 2^V42BIS_MAX_BITS */
#define V42BIS_MAX_OUTPUT_LENGTH 1024
enum
{
V42BIS_P0_NEITHER_DIRECTION = 0,
V42BIS_P0_INITIATOR_RESPONDER,
V42BIS_P0_RESPONDER_INITIATOR,
V42BIS_P0_BOTH_DIRECTIONS
};
enum
{
V42BIS_COMPRESSION_MODE_DYNAMIC = 0,
V42BIS_COMPRESSION_MODE_ALWAYS,
V42BIS_COMPRESSION_MODE_NEVER
};
typedef void (*put_msg_func_t)(void *user_data, const uint8_t *msg, int len);
/*!
V.42bis compression/decompression descriptor. This defines the working state for a
single instance of V.42bis compress/decompression.
*/
typedef struct v42bis_state_s v42bis_state_t;
#if defined(__cplusplus)
extern "C"
{
#endif
/*! Compress a block of octets.
\param s The V.42bis context.
\param buf The data to be compressed.
\param len The length of the data buffer.
\return 0 */
SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *s, const uint8_t buf[], int len);
/*! Flush out any data remaining in a compression buffer.
\param s The V.42bis context.
\return 0 */
SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *s);
/*! Decompress a block of octets.
\param s The V.42bis context.
\param buf The data to be decompressed.
\param len The length of the data buffer.
\return 0 */
SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *s, const uint8_t buf[], int len);
/*! Flush out any data remaining in the decompression buffer.
\param s The V.42bis context.
\return 0 */
SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *s);
/*! Set the compression mode.
\param s The V.42bis context.
\param mode One of the V.42bis compression modes -
V42BIS_COMPRESSION_MODE_DYNAMIC,
V42BIS_COMPRESSION_MODE_ALWAYS,
V42BIS_COMPRESSION_MODE_NEVER */
SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode);
/*! Initialise a V.42bis context.
\param s The V.42bis context.
\param negotiated_p0 The negotiated P0 parameter, from the V.42bis spec.
\param negotiated_p1 The negotiated P1 parameter, from the V.42bis spec.
\param negotiated_p2 The negotiated P2 parameter, from the V.42bis spec.
\param encode_handler Encode callback handler.
\param encode_user_data An opaque pointer passed to the encode callback handler.
\param max_encode_len The maximum length that should be passed to the encode handler.
\param decode_handler Decode callback handler.
\param decode_user_data An opaque pointer passed to the decode callback handler.
\param max_decode_len The maximum length that should be passed to the decode handler.
\return The V.42bis context. */
SPAN_DECLARE(v42bis_state_t *) v42bis_init(const void *ctx,
v42bis_state_t *s,
int negotiated_p0,
int negotiated_p1,
int negotiated_p2,
put_msg_func_t encode_handler,
void *encode_user_data,
int max_encode_len,
put_msg_func_t decode_handler,
void *decode_user_data,
int max_decode_len);
/*! Release a V.42bis context.
\param s The V.42bis context.
\return 0 if OK */
SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s);
/*! Free a V.42bis context.
\param s The V.42bis context.
\return 0 if OK */
SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s);
#if defined(__cplusplus)
}
#endif
#endif
/*- End of file ------------------------------------------------------------*/

View File

@ -1,126 +0,0 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* private/v42bis.h
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2005 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(_SPANDSP_PRIVATE_V42BIS_H_)
#define _SPANDSP_PRIVATE_V42BIS_H_
/*!
V.42bis dictionary node.
Note that 0 is not a valid node to point to (0 is always a control code), so 0 is used
as a "no such value" marker in this structure.
*/
typedef struct
{
/*! \brief The value of the octet represented by the current dictionary node */
uint8_t node_octet;
/*! \brief The parent of this node */
uint16_t parent;
/*! \brief The first child of this node */
uint16_t child;
/*! \brief The next node at the same depth */
uint16_t next;
} v42bis_dict_node_t;
/*!
V.42bis compression or decompression. This defines the working state for a single instance
of V.42bis compression or decompression.
*/
typedef struct
{
/*! \brief Compression enabled. */
int v42bis_parm_p0;
/*! \brief Compression mode. */
int compression_mode;
/*! \brief Callback function to handle output data. */
put_msg_func_t handler;
/*! \brief An opaque pointer passed in calls to the data handler. */
void *user_data;
/*! \brief The maximum amount to be passed to the data handler. */
int max_output_len;
/*! \brief TRUE if we are in transparent (i.e. uncompressable) mode */
int transparent;
/*! \brief Next empty dictionary entry */
uint16_t v42bis_parm_c1;
/*! \brief Current codeword size */
uint16_t v42bis_parm_c2;
/*! \brief Threshold for codeword size change */
uint16_t v42bis_parm_c3;
/*! \brief The current update point in the dictionary */
uint16_t update_at;
/*! \brief The last entry matched in the dictionary */
uint16_t last_matched;
/*! \brief The last entry added to the dictionary */
uint16_t last_added;
/*! \brief Total number of codewords in the dictionary */
int v42bis_parm_n2;
/*! \brief Maximum permitted string length */
int v42bis_parm_n7;
/*! \brief The dictionary */
v42bis_dict_node_t dict[V42BIS_MAX_CODEWORDS];
/*! \brief The octet string in progress */
uint8_t string[V42BIS_MAX_STRING_SIZE];
/*! \brief The current length of the octet string in progress */
int string_length;
/*! \brief The amount of the octet string in progress which has already
been flushed out of the buffer */
int flushed_length;
/*! \brief Compression performance metric */
uint16_t compression_performance;
/*! \brief Outgoing bit buffer (compression), or incoming bit buffer (decompression) */
uint32_t bit_buffer;
/*! \brief Outgoing bit count (compression), or incoming bit count (decompression) */
int bit_count;
/*! \brief The output composition buffer */
uint8_t output_buf[V42BIS_MAX_OUTPUT_LENGTH];
/*! \brief The length of the contents of the output composition buffer */
int output_octet_count;
/*! \brief The current value of the escape code */
uint8_t escape_code;
/*! \brief TRUE if we just hit an escape code, and are waiting for the following octet */
int escaped;
} v42bis_comp_state_t;
/*!
V.42bis compression/decompression descriptor. This defines the working state for a
single instance of V.42bis compress/decompression.
*/
struct v42bis_state_s
{
/*! \brief Compression state. */
v42bis_comp_state_t compress;
/*! \brief Decompression state. */
v42bis_comp_state_t decompress;
/*! \brief Error and flow logging control */
};
#endif
/*- End of file ------------------------------------------------------------*/

View File

@ -28,25 +28,14 @@ nitb_e1_configs = [
app_configs = {
"osmo-bsc": ["doc/examples/osmo-bsc/osmo-bsc.cfg"],
"nat": ["doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"],
"gbproxy": ["doc/examples/osmo-gbproxy/osmo-gbproxy.cfg",
"doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg"],
"sgsn": ["doc/examples/osmo-sgsn/osmo-sgsn.cfg"],
"msc": ["doc/examples/osmo-msc/osmo-msc.cfg"],
"gtphub": ["doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg"]
}
apps = [(4242, "src/osmo-bsc/osmo-bsc", "OsmoBSC", "osmo-bsc"),
(4244, "src/osmo-bsc_nat/osmo-bsc_nat", "OsmoBSCNAT", "nat"),
(4246, "src/gprs/osmo-gbproxy", "OsmoGbProxy", "gbproxy"),
(4245, "src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn"),
(4254, "src/osmo-msc/osmo-msc", "OsmoMSC", "msc"),
(4253, "src/gprs/osmo-gtphub", "OsmoGTPhub", "gtphub")
apps = [(4254, "src/osmo-msc/osmo-msc", "OsmoMSC", "msc"),
]
vty_command = ["./src/osmo-msc/osmo-msc", "-c",
"doc/examples/osmo-msc/osmo-msc.cfg"]
vty_app = apps[4] # reference apps[] entry for osmo-msc
vty_app = apps[0] # reference apps[] entry for osmo-msc

View File

@ -23,10 +23,7 @@ AM_LDFLAGS = \
SUBDIRS = \
libcommon \
libvlr \
libbsc \
libmsc \
libtrau \
libfilter \
libcommon-cs \
$(NULL)
@ -34,19 +31,4 @@ SUBDIRS = \
SUBDIRS += \
osmo-msc \
utils \
ipaccess \
gprs \
$(NULL)
# Conditional Programs
if BUILD_NAT
SUBDIRS += \
osmo-bsc_nat \
$(NULL)
endif
if BUILD_BSC
SUBDIRS += \
osmo-bsc \
$(NULL)
endif

2
src/gprs/.gitignore vendored
View File

@ -1,2 +0,0 @@
gsn_restart
osmo_*.cfg*

View File

@ -1,133 +0,0 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
AM_CFLAGS = \
-Wall \
-fno-strict-aliasing \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(LIBOSMOGB_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(LIBCARES_CFLAGS) \
$(LIBCRYPTO_CFLAGS) \
$(LIBGTP_CFLAGS) \
$(NULL)
if BUILD_IU
AM_CFLAGS += \
$(LIBASN1C_CFLAGS) \
$(LIBOSMOSIGTRAN_CFLAGS) \
$(LIBOSMORANAP_CFLAGS) \
$(NULL)
endif
OSMO_LIBS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOGB_LIBS) \
$(LIBGTP_LIBS) \
$(LIBOSMOSIGTRAN_LIBS) \
$(NULL)
bin_PROGRAMS = \
osmo-gbproxy \
$(NULL)
if HAVE_LIBGTP
if HAVE_LIBCARES
bin_PROGRAMS += \
osmo-sgsn \
osmo-gtphub \
$(NULL)
endif
endif
osmo_gbproxy_SOURCES = \
gb_proxy.c \
gb_proxy_main.c \
gb_proxy_vty.c \
gb_proxy_patch.c \
gb_proxy_tlli.c \
gb_proxy_peer.c \
gprs_gb_parse.c \
gprs_llc_parse.c \
crc24.c \
gprs_utils.c \
$(NULL)
osmo_gbproxy_LDADD = \
$(top_builddir)/src/libcommon/libcommon.a \
$(OSMO_LIBS) \
$(LIBCRYPTO_LIBS) \
-lrt \
$(NULL)
osmo_sgsn_SOURCES = \
gprs_gmm.c \
gprs_sgsn.c \
gprs_sndcp.c \
gprs_sndcp_comp.c \
gprs_sndcp_dcomp.c \
gprs_sndcp_pcomp.c \
gprs_sndcp_vty.c \
gprs_sndcp_xid.c \
sgsn_main.c \
sgsn_vty.c \
sgsn_libgtp.c \
gprs_llc.c \
gprs_llc_parse.c \
gprs_llc_vty.c \
crc24.c \
sgsn_ctrl.c \
sgsn_auth.c \
gprs_subscriber.c \
gprs_utils.c \
sgsn_cdr.c \
sgsn_ares.c \
slhc.c \
gprs_llc_xid.c \
v42bis.c \
$(NULL)
osmo_sgsn_LDADD = \
$(top_builddir)/src/libcommon/libcommon.a \
$(OSMO_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBCARES_LIBS) \
$(LIBCRYPTO_LIBS) \
$(LIBGTP_LIBS) \
-lrt \
-lm \
$(NULL)
if BUILD_IU
osmo_sgsn_LDADD += \
$(LIBOSMOSIGTRAN_LIBS) \
$(LIBOSMORANAP_LIBS) \
$(LIBASN1C_LIBS) \
$(NULL)
endif
osmo_gtphub_SOURCES = \
gtphub_main.c \
gtphub.c \
gtphub_sock.c \
gtphub_ares.c \
gtphub_vty.c \
sgsn_ares.c \
gprs_utils.c \
$(NULL)
osmo_gtphub_LDADD = \
$(top_builddir)/src/libcommon/libcommon.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBCARES_LIBS) \
$(LIBGTP_LIBS) \
$(LIBOSMOSIGTRAN_LIBS) \
-lrt \
$(NULL)

View File

@ -1,67 +0,0 @@
/* GPRS LLC CRC-24 Implementation */
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <openbsc/crc24.h>
/* CRC24 table - FCS */
static const uint32_t tbl_crc24[256] = {
0x00000000, 0x00d6a776, 0x00f64557, 0x0020e221, 0x00b78115, 0x00612663, 0x0041c442, 0x00976334,
0x00340991, 0x00e2aee7, 0x00c24cc6, 0x0014ebb0, 0x00838884, 0x00552ff2, 0x0075cdd3, 0x00a36aa5,
0x00681322, 0x00beb454, 0x009e5675, 0x0048f103, 0x00df9237, 0x00093541, 0x0029d760, 0x00ff7016,
0x005c1ab3, 0x008abdc5, 0x00aa5fe4, 0x007cf892, 0x00eb9ba6, 0x003d3cd0, 0x001ddef1, 0x00cb7987,
0x00d02644, 0x00068132, 0x00266313, 0x00f0c465, 0x0067a751, 0x00b10027, 0x0091e206, 0x00474570,
0x00e42fd5, 0x003288a3, 0x00126a82, 0x00c4cdf4, 0x0053aec0, 0x008509b6, 0x00a5eb97, 0x00734ce1,
0x00b83566, 0x006e9210, 0x004e7031, 0x0098d747, 0x000fb473, 0x00d91305, 0x00f9f124, 0x002f5652,
0x008c3cf7, 0x005a9b81, 0x007a79a0, 0x00acded6, 0x003bbde2, 0x00ed1a94, 0x00cdf8b5, 0x001b5fc3,
0x00fb4733, 0x002de045, 0x000d0264, 0x00dba512, 0x004cc626, 0x009a6150, 0x00ba8371, 0x006c2407,
0x00cf4ea2, 0x0019e9d4, 0x00390bf5, 0x00efac83, 0x0078cfb7, 0x00ae68c1, 0x008e8ae0, 0x00582d96,
0x00935411, 0x0045f367, 0x00651146, 0x00b3b630, 0x0024d504, 0x00f27272, 0x00d29053, 0x00043725,
0x00a75d80, 0x0071faf6, 0x005118d7, 0x0087bfa1, 0x0010dc95, 0x00c67be3, 0x00e699c2, 0x00303eb4,
0x002b6177, 0x00fdc601, 0x00dd2420, 0x000b8356, 0x009ce062, 0x004a4714, 0x006aa535, 0x00bc0243,
0x001f68e6, 0x00c9cf90, 0x00e92db1, 0x003f8ac7, 0x00a8e9f3, 0x007e4e85, 0x005eaca4, 0x00880bd2,
0x00437255, 0x0095d523, 0x00b53702, 0x00639074, 0x00f4f340, 0x00225436, 0x0002b617, 0x00d41161,
0x00777bc4, 0x00a1dcb2, 0x00813e93, 0x005799e5, 0x00c0fad1, 0x00165da7, 0x0036bf86, 0x00e018f0,
0x00ad85dd, 0x007b22ab, 0x005bc08a, 0x008d67fc, 0x001a04c8, 0x00cca3be, 0x00ec419f, 0x003ae6e9,
0x00998c4c, 0x004f2b3a, 0x006fc91b, 0x00b96e6d, 0x002e0d59, 0x00f8aa2f, 0x00d8480e, 0x000eef78,
0x00c596ff, 0x00133189, 0x0033d3a8, 0x00e574de, 0x007217ea, 0x00a4b09c, 0x008452bd, 0x0052f5cb,
0x00f19f6e, 0x00273818, 0x0007da39, 0x00d17d4f, 0x00461e7b, 0x0090b90d, 0x00b05b2c, 0x0066fc5a,
0x007da399, 0x00ab04ef, 0x008be6ce, 0x005d41b8, 0x00ca228c, 0x001c85fa, 0x003c67db, 0x00eac0ad,
0x0049aa08, 0x009f0d7e, 0x00bfef5f, 0x00694829, 0x00fe2b1d, 0x00288c6b, 0x00086e4a, 0x00dec93c,
0x0015b0bb, 0x00c317cd, 0x00e3f5ec, 0x0035529a, 0x00a231ae, 0x007496d8, 0x005474f9, 0x0082d38f,
0x0021b92a, 0x00f71e5c, 0x00d7fc7d, 0x00015b0b, 0x0096383f, 0x00409f49, 0x00607d68, 0x00b6da1e,
0x0056c2ee, 0x00806598, 0x00a087b9, 0x007620cf, 0x00e143fb, 0x0037e48d, 0x001706ac, 0x00c1a1da,
0x0062cb7f, 0x00b46c09, 0x00948e28, 0x0042295e, 0x00d54a6a, 0x0003ed1c, 0x00230f3d, 0x00f5a84b,
0x003ed1cc, 0x00e876ba, 0x00c8949b, 0x001e33ed, 0x008950d9, 0x005ff7af, 0x007f158e, 0x00a9b2f8,
0x000ad85d, 0x00dc7f2b, 0x00fc9d0a, 0x002a3a7c, 0x00bd5948, 0x006bfe3e, 0x004b1c1f, 0x009dbb69,
0x0086e4aa, 0x005043dc, 0x0070a1fd, 0x00a6068b, 0x003165bf, 0x00e7c2c9, 0x00c720e8, 0x0011879e,
0x00b2ed3b, 0x00644a4d, 0x0044a86c, 0x00920f1a, 0x00056c2e, 0x00d3cb58, 0x00f32979, 0x00258e0f,
0x00eef788, 0x003850fe, 0x0018b2df, 0x00ce15a9, 0x0059769d, 0x008fd1eb, 0x00af33ca, 0x007994bc,
0x00dafe19, 0x000c596f, 0x002cbb4e, 0x00fa1c38, 0x006d7f0c, 0x00bbd87a, 0x009b3a5b, 0x004d9d2d
};
#define INIT_CRC24 0xffffff
uint32_t crc24_calc(uint32_t fcs, uint8_t *cp, unsigned int len)
{
while (len--)
fcs = (fcs >> 8) ^ tbl_crc24[(fcs ^ *cp++) & 0xff];
return fcs;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,317 +0,0 @@
/* NS-over-IP proxy */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <signal.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stats.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <openbsc/signal.h>
#include <openbsc/debug.h>
#include <openbsc/vty.h>
#include <openbsc/gb_proxy.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/ports.h>
#include "../../bscconfig.h"
#define _GNU_SOURCE
#include <getopt.h>
void *tall_bsc_ctx;
const char *openbsc_copyright =
"Copyright (C) 2010 Harald Welte and On-Waves\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
static char *config_file = "osmo_gbproxy.cfg";
struct gbproxy_config gbcfg = {0};
static int daemonize = 0;
/* Pointer to the SGSN peer */
extern struct gbprox_peer *gbprox_peer_sgsn;
/* call-back function for the NS protocol */
static int proxy_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
struct msgb *msg, uint16_t bvci)
{
int rc = 0;
switch (event) {
case GPRS_NS_EVT_UNIT_DATA:
rc = gbprox_rcvmsg(&gbcfg, msg, nsvc->nsei, bvci, nsvc->nsvci);
break;
default:
LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
if (msg)
msgb_free(msg);
rc = -EIO;
break;
}
return rc;
}
static void signal_handler(int signal)
{
fprintf(stdout, "signal %u received\n", signal);
switch (signal) {
case SIGINT:
case SIGTERM:
osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
sleep(1);
exit(0);
break;
case SIGABRT:
/* in case of abort, we want to obtain a talloc report
* and then return to the caller, who will abort the process */
case SIGUSR1:
talloc_report(tall_vty_ctx, stderr);
talloc_report_full(tall_bsc_ctx, stderr);
break;
case SIGUSR2:
talloc_report_full(tall_vty_ctx, stderr);
break;
default:
break;
}
}
static void print_usage()
{
printf("Usage: bsc_hack\n");
}
static void print_help()
{
printf(" Some useful help...\n");
printf(" -h --help this text\n");
printf(" -d option --debug=DNS:DGPRS,0:0 enable debugging\n");
printf(" -D --daemonize Fork the process into a background daemon\n");
printf(" -c --config-file filename The config file to use.\n");
printf(" -s --disable-color\n");
printf(" -T --timestamp Prefix every log line with a timestamp\n");
printf(" -V --version. Print the version of OpenBSC.\n");
printf(" -e --log-level number. Set a global loglevel.\n");
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{ "help", 0, 0, 'h' },
{ "debug", 1, 0, 'd' },
{ "daemonize", 0, 0, 'D' },
{ "config-file", 1, 0, 'c' },
{ "disable-color", 0, 0, 's' },
{ "timestamp", 0, 0, 'T' },
{ "version", 0, 0, 'V' },
{ "log-level", 1, 0, 'e' },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "hd:Dc:sTVe:",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
print_usage();
print_help();
exit(0);
case 's':
log_set_use_color(osmo_stderr_target, 0);
break;
case 'd':
log_parse_category_mask(osmo_stderr_target, optarg);
break;
case 'D':
daemonize = 1;
break;
case 'c':
config_file = optarg;
break;
case 'T':
log_set_print_timestamp(osmo_stderr_target, 1);
break;
case 'e':
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
case 'V':
print_version(1);
exit(0);
break;
default:
break;
}
}
}
extern int bsc_vty_go_parent(struct vty *vty);
static struct vty_app_info vty_info = {
.name = "OsmoGbProxy",
.version = PACKAGE_VERSION,
.go_parent_cb = bsc_vty_go_parent,
.is_config_node = bsc_vty_is_config_node,
};
/* default categories */
static struct log_info_cat gprs_categories[] = {
[DGPRS] = {
.name = "DGPRS",
.description = "GPRS Packet Service",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DNS] = {
.name = "DNS",
.description = "GPRS Network Service (NS)",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DBSSGP] = {
.name = "DBSSGP",
.description = "GPRS BSS Gateway Protocol (BSSGP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
};
static const struct log_info gprs_log_info = {
.filter_fn = gprs_log_filter_fn,
.cat = gprs_categories,
.num_cat = ARRAY_SIZE(gprs_categories),
};
int main(int argc, char **argv)
{
struct gsm_network dummy_network;
int rc;
tall_bsc_ctx = talloc_named_const(NULL, 0, "nsip_proxy");
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
signal(SIGINT, &signal_handler);
signal(SIGTERM, &signal_handler);
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
osmo_init_logging(&gprs_log_info);
vty_info.copyright = openbsc_copyright;
vty_init(&vty_info);
logging_vty_add_cmds(NULL);
osmo_stats_vty_add_cmds(&gprs_log_info);
gbproxy_vty_init();
handle_options(argc, argv);
rate_ctr_init(tall_bsc_ctx);
osmo_stats_init(tall_bsc_ctx);
bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb, tall_bsc_ctx);
if (!bssgp_nsi) {
LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
exit(1);
}
gbproxy_init_config(&gbcfg);
gbcfg.nsi = bssgp_nsi;
gprs_ns_vty_init(bssgp_nsi);
gprs_ns_set_log_ss(DNS);
bssgp_set_log_ss(DBSSGP);
osmo_signal_register_handler(SS_L_NS, &gbprox_signal, &gbcfg);
rc = gbproxy_parse_config(config_file, &gbcfg);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n");
exit(2);
}
/* start telnet after reading config for vty_get_bind_addr() */
rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network,
vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY);
if (rc < 0)
exit(1);
if (!gprs_nsvc_by_nsei(gbcfg.nsi, gbcfg.nsip_sgsn_nsei)) {
LOGP(DGPRS, LOGL_FATAL, "You cannot proxy to NSEI %u "
"without creating that NSEI before\n",
gbcfg.nsip_sgsn_nsei);
exit(2);
}
rc = gprs_ns_nsip_listen(bssgp_nsi);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n");
exit(2);
}
rc = gprs_ns_frgre_listen(bssgp_nsi);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE "
"socket. Do you have CAP_NET_RAW?\n");
exit(2);
}
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
perror("Error during daemonize");
exit(1);
}
}
/* Reset all the persistent NS-VCs that we've read from the config */
gbprox_reset_persistent_nsvcs(bssgp_nsi);
while (1) {
rc = osmo_select_main(0);
if (rc < 0)
exit(3);
}
exit(0);
}

View File

@ -1,459 +0,0 @@
/* Gb-proxy message patching */
/* (C) 2014 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <openbsc/gb_proxy.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/gprs_gb_parse.h>
#include <openbsc/gsm_data.h>
#include <openbsc/debug.h>
#include <osmocom/gprs/protocol/gsm_08_18.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gsm/apn.h>
/* patch RA identifier in place */
static void gbproxy_patch_raid(uint8_t *raid_enc, struct gbproxy_peer *peer,
int to_bss, const char *log_text)
{
struct gbproxy_patch_state *state = &peer->patch_state;
int old_mcc;
int old_mnc;
struct gprs_ra_id raid;
enum gbproxy_peer_ctr counter =
to_bss ?
GBPROX_PEER_CTR_RAID_PATCHED_SGSN :
GBPROX_PEER_CTR_RAID_PATCHED_BSS;
if (!state->local_mcc || !state->local_mnc)
return;
gsm48_parse_ra(&raid, raid_enc);
old_mcc = raid.mcc;
old_mnc = raid.mnc;
if (!to_bss) {
/* BSS -> SGSN */
if (state->local_mcc)
raid.mcc = peer->cfg->core_mcc;
if (state->local_mnc)
raid.mnc = peer->cfg->core_mnc;
} else {
/* SGSN -> BSS */
if (state->local_mcc)
raid.mcc = state->local_mcc;
if (state->local_mnc)
raid.mnc = state->local_mnc;
}
LOGP(DGPRS, LOGL_DEBUG,
"Patching %s to %s: "
"%d-%d-%d-%d -> %d-%d-%d-%d\n",
log_text,
to_bss ? "BSS" : "SGSN",
old_mcc, old_mnc, raid.lac, raid.rac,
raid.mcc, raid.mnc, raid.lac, raid.rac);
gsm48_construct_ra(raid_enc, &raid);
rate_ctr_inc(&peer->ctrg->ctr[counter]);
}
static void gbproxy_patch_apn_ie(struct msgb *msg,
uint8_t *apn_ie, size_t apn_ie_len,
struct gbproxy_peer *peer,
size_t *new_apn_ie_len, const char *log_text)
{
struct apn_ie_hdr {
uint8_t iei;
uint8_t apn_len;
uint8_t apn[0];
} *hdr = (void *)apn_ie;
size_t apn_len = hdr->apn_len;
uint8_t *apn = hdr->apn;
OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr));
OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102);
if (peer->cfg->core_apn_size == 0) {
char str1[110];
/* Remove the IE */
LOGP(DGPRS, LOGL_DEBUG,
"Patching %s to SGSN: Removing APN '%s'\n",
log_text,
osmo_apn_to_str(str1, apn, apn_len));
*new_apn_ie_len = 0;
gprs_msgb_resize_area(msg, apn_ie, apn_ie_len, 0);
} else {
/* Resize the IE */
char str1[110];
char str2[110];
OSMO_ASSERT(peer->cfg->core_apn_size <= 100);
LOGP(DGPRS, LOGL_DEBUG,
"Patching %s to SGSN: "
"Replacing APN '%s' -> '%s'\n",
log_text,
osmo_apn_to_str(str1, apn, apn_len),
osmo_apn_to_str(str2, peer->cfg->core_apn,
peer->cfg->core_apn_size));
*new_apn_ie_len = peer->cfg->core_apn_size + 2;
gprs_msgb_resize_area(msg, apn, apn_len, peer->cfg->core_apn_size);
memcpy(apn, peer->cfg->core_apn, peer->cfg->core_apn_size);
hdr->apn_len = peer->cfg->core_apn_size;
}
rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]);
}
static int gbproxy_patch_tlli(uint8_t *tlli_enc,
struct gbproxy_peer *peer,
uint32_t new_tlli,
int to_bss, const char *log_text)
{
uint32_t tlli_be;
uint32_t tlli;
enum gbproxy_peer_ctr counter =
to_bss ?
GBPROX_PEER_CTR_TLLI_PATCHED_SGSN :
GBPROX_PEER_CTR_TLLI_PATCHED_BSS;
memcpy(&tlli_be, tlli_enc, sizeof(tlli_be));
tlli = ntohl(tlli_be);
if (tlli == new_tlli)
return 0;
LOGP(DGPRS, LOGL_DEBUG,
"Patching %ss: "
"Replacing %08x -> %08x\n",
log_text, tlli, new_tlli);
tlli_be = htonl(new_tlli);
memcpy(tlli_enc, &tlli_be, sizeof(tlli_be));
rate_ctr_inc(&peer->ctrg->ctr[counter]);
return 1;
}
static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc,
struct gbproxy_peer *peer,
uint32_t new_ptmsi,
int to_bss, const char *log_text)
{
uint32_t ptmsi_be;
uint32_t ptmsi;
enum gbproxy_peer_ctr counter =
to_bss ?
GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN :
GBPROX_PEER_CTR_PTMSI_PATCHED_BSS;
memcpy(&ptmsi_be, ptmsi_enc, sizeof(ptmsi_be));
ptmsi = ntohl(ptmsi_be);
if (ptmsi == new_ptmsi)
return 0;
LOGP(DGPRS, LOGL_DEBUG,
"Patching %ss: "
"Replacing %08x -> %08x\n",
log_text, ptmsi, new_ptmsi);
ptmsi_be = htonl(new_ptmsi);
memcpy(ptmsi_enc, &ptmsi_be, sizeof(ptmsi_be));
rate_ctr_inc(&peer->ctrg->ctr[counter]);
return 1;
}
int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info, int *len_change,
struct gprs_gb_parse_context *parse_ctx)
{
struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed;
int have_patched = 0;
int fcs;
struct gbproxy_config *cfg = peer->cfg;
if (parse_ctx->ptmsi_enc && link_info &&
!parse_ctx->old_raid_is_foreign && peer->cfg->patch_ptmsi) {
uint32_t ptmsi;
if (parse_ctx->to_bss)
ptmsi = link_info->tlli.ptmsi;
else
ptmsi = link_info->sgsn_tlli.ptmsi;
if (ptmsi != GSM_RESERVED_TMSI) {
if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer,
ptmsi, parse_ctx->to_bss, "P-TMSI"))
have_patched = 1;
} else {
/* TODO: invalidate old RAI if present (see below) */
}
}
if (parse_ctx->new_ptmsi_enc && link_info && cfg->patch_ptmsi) {
uint32_t ptmsi;
if (parse_ctx->to_bss)
ptmsi = link_info->tlli.ptmsi;
else
ptmsi = link_info->sgsn_tlli.ptmsi;
OSMO_ASSERT(ptmsi);
if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer,
ptmsi, parse_ctx->to_bss, "new P-TMSI"))
have_patched = 1;
}
if (parse_ctx->raid_enc) {
gbproxy_patch_raid(parse_ctx->raid_enc, peer, parse_ctx->to_bss,
parse_ctx->llc_msg_name);
have_patched = 1;
}
if (parse_ctx->old_raid_enc && !parse_ctx->old_raid_is_foreign) {
/* TODO: Patch to invalid if P-TMSI unknown. */
gbproxy_patch_raid(parse_ctx->old_raid_enc, peer, parse_ctx->to_bss,
parse_ctx->llc_msg_name);
have_patched = 1;
}
if (parse_ctx->apn_ie &&
cfg->core_apn &&
!parse_ctx->to_bss &&
gbproxy_imsi_matches(cfg, GBPROX_MATCH_PATCHING, link_info) &&
cfg->core_apn) {
size_t new_len;
gbproxy_patch_apn_ie(msg,
parse_ctx->apn_ie, parse_ctx->apn_ie_len,
peer, &new_len, parse_ctx->llc_msg_name);
*len_change += (int)new_len - (int)parse_ctx->apn_ie_len;
have_patched = 1;
}
if (have_patched) {
llc_len += *len_change;
ghp->crc_length += *len_change;
/* Fix FCS */
fcs = gprs_llc_fcs(llc, ghp->crc_length);
LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n",
ghp->fcs, fcs);
llc[llc_len - 3] = fcs & 0xff;
llc[llc_len - 2] = (fcs >> 8) & 0xff;
llc[llc_len - 1] = (fcs >> 16) & 0xff;
}
return have_patched;
}
/* patch BSSGP message to use core_mcc/mnc on the SGSN side */
void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info, int *len_change,
struct gprs_gb_parse_context *parse_ctx)
{
const char *err_info = NULL;
int err_ctr = -1;
if (parse_ctx->bssgp_raid_enc)
gbproxy_patch_raid(parse_ctx->bssgp_raid_enc, peer,
parse_ctx->to_bss, "BSSGP");
if (parse_ctx->need_decryption &&
(peer->cfg->patch_ptmsi || peer->cfg->core_apn)) {
/* Patching LLC messages has been requested
* explicitly, but the message (including the
* type) is encrypted, so we possibly fail to
* patch the LLC part of the message. */
err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR;
err_info = "GMM message is encrypted";
goto patch_error;
}
if (!link_info && parse_ctx->tlli_enc && parse_ctx->to_bss) {
/* Happens with unknown (not cached) TLLI coming from
* the SGSN */
/* TODO: What shall be done with the message in this case? */
err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN;
err_info = "TLLI sent by the SGSN is unknown";
goto patch_error;
}
if (!link_info)
return;
if (parse_ctx->tlli_enc && peer->cfg->patch_ptmsi) {
uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli,
link_info, parse_ctx->to_bss);
if (tlli) {
gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli,
parse_ctx->to_bss, "TLLI");
parse_ctx->tlli = tlli;
} else {
/* Internal error */
err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
err_info = "Replacement TLLI is 0";
goto patch_error;
}
}
if (parse_ctx->bssgp_ptmsi_enc && peer->cfg->patch_ptmsi) {
uint32_t ptmsi;
if (parse_ctx->to_bss)
ptmsi = link_info->tlli.ptmsi;
else
ptmsi = link_info->sgsn_tlli.ptmsi;
if (ptmsi != GSM_RESERVED_TMSI)
gbproxy_patch_ptmsi(
parse_ctx->bssgp_ptmsi_enc, peer,
ptmsi, parse_ctx->to_bss, "BSSGP P-TMSI");
}
if (parse_ctx->llc) {
uint8_t *llc = parse_ctx->llc;
size_t llc_len = parse_ctx->llc_len;
int llc_len_change = 0;
gbproxy_patch_llc(msg, llc, llc_len, peer, link_info,
&llc_len_change, parse_ctx);
/* Note that the APN might have been resized here, but no
* pointer int the parse_ctx will refer to an adress after the
* APN. So it's possible to patch first and do the TLLI
* handling afterwards. */
if (llc_len_change) {
llc_len += llc_len_change;
/* Fix LLC IE len */
/* TODO: This is a kludge, but the a pointer to the
* start of the IE is not available here */
if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) {
/* most probably a one byte length */
if (llc_len > 127) {
err_info = "Cannot increase size";
err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
goto patch_error;
}
llc[-1] = llc_len | 0x80;
} else {
llc[-2] = (llc_len >> 8) & 0x7f;
llc[-1] = llc_len & 0xff;
}
*len_change += llc_len_change;
}
/* Note that the tp struct might contain invalid pointers here
* if the LLC field has changed its size */
parse_ctx->llc_len = llc_len;
}
return;
patch_error:
OSMO_ASSERT(err_ctr >= 0);
rate_ctr_inc(&peer->ctrg->ctr[err_ctr]);
LOGP(DGPRS, LOGL_ERROR,
"NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n",
msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS",
err_info);
}
void gbproxy_clear_patch_filter(struct gbproxy_match *match)
{
if (match->enable) {
regfree(&match->re_comp);
match->enable = 0;
}
talloc_free(match->re_str);
match->re_str = NULL;
}
int gbproxy_set_patch_filter(struct gbproxy_match *match, const char *filter,
const char **err_msg)
{
static char err_buf[300];
int rc;
gbproxy_clear_patch_filter(match);
if (!filter)
return 0;
rc = regcomp(&match->re_comp, filter,
REG_EXTENDED | REG_NOSUB | REG_ICASE);
if (rc == 0) {
match->enable = 1;
match->re_str = talloc_strdup(tall_bsc_ctx, filter);
return 0;
}
if (err_msg) {
regerror(rc, &match->re_comp,
err_buf, sizeof(err_buf));
*err_msg = err_buf;
}
return -1;
}
int gbproxy_check_imsi(struct gbproxy_match *match,
const uint8_t *imsi, size_t imsi_len)
{
char mi_buf[200];
int rc;
if (!match->enable)
return 1;
rc = gprs_is_mi_imsi(imsi, imsi_len);
if (rc > 0)
rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len);
if (rc <= 0) {
LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n",
osmo_hexdump(imsi, imsi_len));
return -1;
}
LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc);
rc = regexec(&match->re_comp, mi_buf, 0, NULL, 0);
if (rc == REG_NOMATCH) {
LOGP(DGPRS, LOGL_INFO,
"IMSI '%s' doesn't match pattern '%s'\n",
mi_buf, match->re_str);
return 0;
}
return 1;
}

View File

@ -1,222 +0,0 @@
/* Gb proxy peer handling */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010-2013 by On-Waves
* (C) 2013 by Holger Hans Peter Freyther
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <openbsc/gb_proxy.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_data_shared.h>
#include <openbsc/debug.h>
#include <osmocom/gprs/protocol/gsm_08_18.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/talloc.h>
#include <string.h>
static const struct rate_ctr_desc peer_ctr_description[] = {
{ "blocked", "BVC Block " },
{ "unblocked", "BVC Unblock " },
{ "dropped", "BVC blocked, dropped packet " },
{ "inv-nsei", "NSEI mismatch " },
{ "tx-err", "NS Transmission error " },
{ "raid-mod.bss", "RAID patched (BSS )" },
{ "raid-mod.sgsn", "RAID patched (SGSN)" },
{ "apn-mod.sgsn", "APN patched " },
{ "tlli-mod.bss", "TLLI patched (BSS )" },
{ "tlli-mod.sgsn", "TLLI patched (SGSN)" },
{ "ptmsi-mod.bss", "P-TMSI patched (BSS )" },
{ "ptmsi-mod.sgsn","P-TMSI patched (SGSN)" },
{ "mod-crypt-err", "Patch error: encrypted " },
{ "mod-err", "Patch error: other " },
{ "attach-reqs", "Attach Request count " },
{ "attach-rejs", "Attach Reject count " },
{ "attach-acks", "Attach Accept count " },
{ "attach-cpls", "Attach Completed count " },
{ "ra-upd-reqs", "RoutingArea Update Request count" },
{ "ra-upd-rejs", "RoutingArea Update Reject count " },
{ "ra-upd-acks", "RoutingArea Update Accept count " },
{ "ra-upd-cpls", "RoutingArea Update Compltd count" },
{ "gmm-status", "GMM Status count (BSS)" },
{ "gmm-status", "GMM Status count (SGSN)" },
{ "detach-reqs", "Detach Request count " },
{ "detach-acks", "Detach Accept count " },
{ "pdp-act-reqs", "PDP Activation Request count " },
{ "pdp-act-rejs", "PDP Activation Reject count " },
{ "pdp-act-acks", "PDP Activation Accept count " },
{ "pdp-deact-reqs","PDP Deactivation Request count " },
{ "pdp-deact-acks","PDP Deactivation Accept count " },
{ "tlli-unknown", "TLLI from SGSN unknown " },
{ "tlli-cache", "TLLI cache size " },
};
osmo_static_assert(ARRAY_SIZE(peer_ctr_description) == GBPROX_PEER_CTR_LAST, everything_described);
static const struct rate_ctr_group_desc peer_ctrg_desc = {
.group_name_prefix = "gbproxy.peer",
.group_description = "GBProxy Peer Statistics",
.num_ctr = ARRAY_SIZE(peer_ctr_description),
.ctr_desc = peer_ctr_description,
.class_id = OSMO_STATS_CLASS_PEER,
};
/* Find the gbprox_peer by its BVCI */
struct gbproxy_peer *gbproxy_peer_by_bvci(struct gbproxy_config *cfg, uint16_t bvci)
{
struct gbproxy_peer *peer;
llist_for_each_entry(peer, &cfg->bts_peers, list) {
if (peer->bvci == bvci)
return peer;
}
return NULL;
}
/* Find the gbprox_peer by its NSEI */
struct gbproxy_peer *gbproxy_peer_by_nsei(struct gbproxy_config *cfg,
uint16_t nsei)
{
struct gbproxy_peer *peer;
llist_for_each_entry(peer, &cfg->bts_peers, list) {
if (peer->nsei == nsei)
return peer;
}
return NULL;
}
/* look-up a peer by its Routeing Area Identification (RAI) */
struct gbproxy_peer *gbproxy_peer_by_rai(struct gbproxy_config *cfg,
const uint8_t *ra)
{
struct gbproxy_peer *peer;
llist_for_each_entry(peer, &cfg->bts_peers, list) {
if (!memcmp(peer->ra, ra, 6))
return peer;
}
return NULL;
}
/* look-up a peer by its Location Area Identification (LAI) */
struct gbproxy_peer *gbproxy_peer_by_lai(struct gbproxy_config *cfg,
const uint8_t *la)
{
struct gbproxy_peer *peer;
llist_for_each_entry(peer, &cfg->bts_peers, list) {
if (!memcmp(peer->ra, la, 5))
return peer;
}
return NULL;
}
/* look-up a peer by its Location Area Code (LAC) */
struct gbproxy_peer *gbproxy_peer_by_lac(struct gbproxy_config *cfg,
const uint8_t *la)
{
struct gbproxy_peer *peer;
llist_for_each_entry(peer, &cfg->bts_peers, list) {
if (!memcmp(peer->ra + 3, la + 3, 2))
return peer;
}
return NULL;
}
struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv(struct gbproxy_config *cfg,
struct tlv_parsed *tp)
{
if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
uint16_t bvci;
bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
if (bvci >= 2)
return gbproxy_peer_by_bvci(cfg, bvci);
}
if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
uint8_t *rai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA);
/* Only compare LAC part, since MCC/MNC are possibly patched.
* Since the LAC of different BSS must be different when
* MCC/MNC are patched, collisions shouldn't happen. */
return gbproxy_peer_by_lac(cfg, rai);
}
if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) {
uint8_t *lai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA);
return gbproxy_peer_by_lac(cfg, lai);
}
return NULL;
}
struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci)
{
struct gbproxy_peer *peer;
peer = talloc_zero(tall_bsc_ctx, struct gbproxy_peer);
if (!peer)
return NULL;
peer->bvci = bvci;
peer->ctrg = rate_ctr_group_alloc(peer, &peer_ctrg_desc, bvci);
if (!peer->ctrg) {
talloc_free(peer);
return NULL;
}
peer->cfg = cfg;
llist_add(&peer->list, &cfg->bts_peers);
INIT_LLIST_HEAD(&peer->patch_state.logical_links);
return peer;
}
void gbproxy_peer_free(struct gbproxy_peer *peer)
{
llist_del(&peer->list);
gbproxy_delete_link_infos(peer);
rate_ctr_group_free(peer->ctrg);
peer->ctrg = NULL;
talloc_free(peer);
}
int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci)
{
int counter = 0;
struct gbproxy_peer *peer, *tmp;
llist_for_each_entry_safe(peer, tmp, &cfg->bts_peers, list) {
if (peer->nsei != nsei)
continue;
if (bvci && peer->bvci != bvci)
continue;
gbproxy_peer_free(peer);
counter += 1;
}
return counter;
}

View File

@ -1,723 +0,0 @@
/* Gb-proxy TLLI state handling */
/* (C) 2014 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/gsm/gsm48.h>
#include <openbsc/gb_proxy.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/gprs_gb_parse.h>
#include <openbsc/debug.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/talloc.h>
struct gbproxy_link_info *gbproxy_link_info_by_tlli(struct gbproxy_peer *peer,
uint32_t tlli)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &peer->patch_state;
if (!tlli)
return NULL;
llist_for_each_entry(link_info, &state->logical_links, list)
if (link_info->tlli.current == tlli ||
link_info->tlli.assigned == tlli)
return link_info;
return NULL;
}
struct gbproxy_link_info *gbproxy_link_info_by_ptmsi(
struct gbproxy_peer *peer,
uint32_t ptmsi)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &peer->patch_state;
if (ptmsi == GSM_RESERVED_TMSI)
return NULL;
llist_for_each_entry(link_info, &state->logical_links, list)
if (link_info->tlli.ptmsi == ptmsi)
return link_info;
return NULL;
}
struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli(
struct gbproxy_peer *peer,
uint32_t tlli)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &peer->patch_state;
if (!tlli)
return NULL;
/* Don't care about the NSEI */
llist_for_each_entry(link_info, &state->logical_links, list)
if (link_info->sgsn_tlli.current == tlli ||
link_info->sgsn_tlli.assigned == tlli)
return link_info;
return NULL;
}
struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli(
struct gbproxy_peer *peer,
uint32_t tlli, uint32_t sgsn_nsei)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &peer->patch_state;
if (!tlli)
return NULL;
llist_for_each_entry(link_info, &state->logical_links, list)
if ((link_info->sgsn_tlli.current == tlli ||
link_info->sgsn_tlli.assigned == tlli) &&
link_info->sgsn_nsei == sgsn_nsei)
return link_info;
return NULL;
}
struct gbproxy_link_info *gbproxy_link_info_by_imsi(
struct gbproxy_peer *peer,
const uint8_t *imsi,
size_t imsi_len)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &peer->patch_state;
if (!gprs_is_mi_imsi(imsi, imsi_len))
return NULL;
llist_for_each_entry(link_info, &state->logical_links, list) {
if (link_info->imsi_len != imsi_len)
continue;
if (memcmp(link_info->imsi, imsi, imsi_len) != 0)
continue;
return link_info;
}
return NULL;
}
void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info)
{
struct msgb *msg, *nxt;
llist_for_each_entry_safe(msg, nxt, &link_info->stored_msgs, list) {
llist_del(&msg->list);
msgb_free(msg);
}
}
void gbproxy_delete_link_info(struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info)
{
struct gbproxy_patch_state *state = &peer->patch_state;
gbproxy_link_info_discard_messages(link_info);
llist_del(&link_info->list);
talloc_free(link_info);
state->logical_link_count -= 1;
peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
state->logical_link_count;
}
void gbproxy_delete_link_infos(struct gbproxy_peer *peer)
{
struct gbproxy_link_info *link_info, *nxt;
struct gbproxy_patch_state *state = &peer->patch_state;
llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list)
gbproxy_delete_link_info(peer, link_info);
OSMO_ASSERT(state->logical_link_count == 0);
OSMO_ASSERT(llist_empty(&state->logical_links));
}
void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now,
struct gbproxy_link_info *link_info)
{
struct gbproxy_patch_state *state = &peer->patch_state;
link_info->timestamp = now;
llist_add(&link_info->list, &state->logical_links);
state->logical_link_count += 1;
peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
state->logical_link_count;
}
int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now)
{
struct gbproxy_patch_state *state = &peer->patch_state;
int exceeded_max_len = 0;
int deleted_count = 0;
int check_for_age;
if (peer->cfg->tlli_max_len > 0)
exceeded_max_len =
state->logical_link_count - peer->cfg->tlli_max_len;
check_for_age = peer->cfg->tlli_max_age > 0;
for (; exceeded_max_len > 0; exceeded_max_len--) {
struct gbproxy_link_info *link_info;
OSMO_ASSERT(!llist_empty(&state->logical_links));
link_info = llist_entry(state->logical_links.prev,
struct gbproxy_link_info,
list);
LOGP(DGPRS, LOGL_INFO,
"Removing TLLI %08x from list "
"(stale, length %d, max_len exceeded)\n",
link_info->tlli.current, state->logical_link_count);
gbproxy_delete_link_info(peer, link_info);
deleted_count += 1;
}
while (check_for_age && !llist_empty(&state->logical_links)) {
time_t age;
struct gbproxy_link_info *link_info;
link_info = llist_entry(state->logical_links.prev,
struct gbproxy_link_info,
list);
age = now - link_info->timestamp;
/* age < 0 only happens after system time jumps, discard entry */
if (age <= peer->cfg->tlli_max_age && age >= 0) {
check_for_age = 0;
continue;
}
LOGP(DGPRS, LOGL_INFO,
"Removing TLLI %08x from list "
"(stale, age %d, max_age exceeded)\n",
link_info->tlli.current, (int)age);
gbproxy_delete_link_info(peer, link_info);
deleted_count += 1;
}
return deleted_count;
}
struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer)
{
struct gbproxy_link_info *link_info;
link_info = talloc_zero(peer, struct gbproxy_link_info);
link_info->tlli.ptmsi = GSM_RESERVED_TMSI;
link_info->sgsn_tlli.ptmsi = GSM_RESERVED_TMSI;
link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX;
INIT_LLIST_HEAD(&link_info->stored_msgs);
return link_info;
}
void gbproxy_detach_link_info(
struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info)
{
struct gbproxy_patch_state *state = &peer->patch_state;
llist_del(&link_info->list);
OSMO_ASSERT(state->logical_link_count > 0);
state->logical_link_count -= 1;
peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
state->logical_link_count;
}
void gbproxy_update_link_info(struct gbproxy_link_info *link_info,
const uint8_t *imsi, size_t imsi_len)
{
if (!gprs_is_mi_imsi(imsi, imsi_len))
return;
link_info->imsi_len = imsi_len;
link_info->imsi =
talloc_realloc_size(link_info, link_info->imsi, imsi_len);
OSMO_ASSERT(link_info->imsi != NULL);
memcpy(link_info->imsi, imsi, imsi_len);
}
void gbproxy_reassign_tlli(struct gbproxy_tlli_state *tlli_state,
struct gbproxy_peer *peer, uint32_t new_tlli)
{
if (new_tlli == tlli_state->current)
return;
LOGP(DGPRS, LOGL_INFO,
"The TLLI has been reassigned from %08x to %08x\n",
tlli_state->current, new_tlli);
/* Remember assigned TLLI */
tlli_state->assigned = new_tlli;
tlli_state->bss_validated = 0;
tlli_state->net_validated = 0;
}
uint32_t gbproxy_map_tlli(uint32_t other_tlli,
struct gbproxy_link_info *link_info, int to_bss)
{
uint32_t tlli = 0;
struct gbproxy_tlli_state *src, *dst;
if (to_bss) {
src = &link_info->sgsn_tlli;
dst = &link_info->tlli;
} else {
src = &link_info->tlli;
dst = &link_info->sgsn_tlli;
}
if (src->current == other_tlli)
tlli = dst->current;
else if (src->assigned == other_tlli)
tlli = dst->assigned;
return tlli;
}
static void gbproxy_validate_tlli(struct gbproxy_tlli_state *tlli_state,
uint32_t tlli, int to_bss)
{
LOGP(DGPRS, LOGL_DEBUG,
"%s({current = %08x, assigned = %08x, net_vld = %d, bss_vld = %d}, %08x)\n",
__func__, tlli_state->current, tlli_state->assigned,
tlli_state->net_validated, tlli_state->bss_validated, tlli);
if (!tlli_state->assigned || tlli_state->assigned != tlli)
return;
/* TODO: Is this ok? Check spec */
if (gprs_tlli_type(tlli) != TLLI_LOCAL)
return;
/* See GSM 04.08, 4.7.1.5 */
if (to_bss)
tlli_state->net_validated = 1;
else
tlli_state->bss_validated = 1;
if (!tlli_state->bss_validated || !tlli_state->net_validated)
return;
LOGP(DGPRS, LOGL_INFO,
"The TLLI %08x has been validated (was %08x)\n",
tlli_state->assigned, tlli_state->current);
tlli_state->current = tlli;
tlli_state->assigned = 0;
}
static void gbproxy_touch_link_info(struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info,
time_t now)
{
gbproxy_detach_link_info(peer, link_info);
gbproxy_attach_link_info(peer, now, link_info);
}
static int gbproxy_unregister_link_info(struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info)
{
if (!link_info)
return 1;
if (link_info->tlli.ptmsi == GSM_RESERVED_TMSI && !link_info->imsi_len) {
LOGP(DGPRS, LOGL_INFO,
"Removing TLLI %08x from list (P-TMSI or IMSI are not set)\n",
link_info->tlli.current);
gbproxy_delete_link_info(peer, link_info);
return 1;
}
link_info->tlli.current = 0;
link_info->tlli.assigned = 0;
link_info->sgsn_tlli.current = 0;
link_info->sgsn_tlli.assigned = 0;
link_info->is_deregistered = 1;
gbproxy_reset_link(link_info);
return 0;
}
int gbproxy_imsi_matches(struct gbproxy_config *cfg,
enum gbproxy_match_id match_id,
struct gbproxy_link_info *link_info)
{
struct gbproxy_match *match;
OSMO_ASSERT(match_id >= 0 && match_id < ARRAY_SIZE(cfg->matches));
match = &cfg->matches[match_id];
if (!match->enable)
return 1;
return link_info != NULL && link_info->is_matching[match_id];
}
void gbproxy_assign_imsi(struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info,
struct gprs_gb_parse_context *parse_ctx)
{
int imsi_matches;
struct gbproxy_link_info *other_link_info;
enum gbproxy_match_id match_id;
/* Make sure that there is a second entry with the same IMSI */
other_link_info = gbproxy_link_info_by_imsi(
peer, parse_ctx->imsi, parse_ctx->imsi_len);
if (other_link_info && other_link_info != link_info) {
char mi_buf[200];
mi_buf[0] = '\0';
gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
parse_ctx->imsi, parse_ctx->imsi_len);
LOGP(DGPRS, LOGL_INFO,
"Removing TLLI %08x from list (IMSI %s re-used)\n",
other_link_info->tlli.current, mi_buf);
gbproxy_delete_link_info(peer, other_link_info);
}
/* Update the IMSI field */
gbproxy_update_link_info(link_info,
parse_ctx->imsi, parse_ctx->imsi_len);
/* Check, whether the IMSI matches */
OSMO_ASSERT(ARRAY_SIZE(link_info->is_matching) ==
ARRAY_SIZE(peer->cfg->matches));
for (match_id = 0; match_id < ARRAY_SIZE(link_info->is_matching);
++match_id) {
imsi_matches = gbproxy_check_imsi(
&peer->cfg->matches[match_id],
parse_ctx->imsi, parse_ctx->imsi_len);
if (imsi_matches >= 0)
link_info->is_matching[match_id] = imsi_matches;
}
}
static int gbproxy_tlli_match(const struct gbproxy_tlli_state *a,
const struct gbproxy_tlli_state *b)
{
if (a->current && a->current == b->current)
return 1;
if (a->assigned && a->assigned == b->assigned)
return 1;
if (a->ptmsi != GSM_RESERVED_TMSI && a->ptmsi == b->ptmsi)
return 1;
return 0;
}
static void gbproxy_remove_matching_link_infos(
struct gbproxy_peer *peer, struct gbproxy_link_info *link_info)
{
struct gbproxy_link_info *info, *nxt;
struct gbproxy_patch_state *state = &peer->patch_state;
/* Make sure that there is no second entry with the same P-TMSI or TLLI */
llist_for_each_entry_safe(info, nxt, &state->logical_links, list) {
if (info == link_info)
continue;
if (!gbproxy_tlli_match(&link_info->tlli, &info->tlli) &&
(link_info->sgsn_nsei != info->sgsn_nsei ||
!gbproxy_tlli_match(&link_info->sgsn_tlli, &info->sgsn_tlli)))
continue;
LOGP(DGPRS, LOGL_INFO,
"Removing TLLI %08x from list (P-TMSI/TLLI re-used)\n",
info->tlli.current);
gbproxy_delete_link_info(peer, info);
}
}
static struct gbproxy_link_info *gbproxy_get_link_info_ul(
struct gbproxy_peer *peer,
int *tlli_is_valid,
struct gprs_gb_parse_context *parse_ctx)
{
struct gbproxy_link_info *link_info = NULL;
if (parse_ctx->tlli_enc) {
link_info = gbproxy_link_info_by_tlli(peer, parse_ctx->tlli);
if (link_info) {
*tlli_is_valid = 1;
return link_info;
}
}
*tlli_is_valid = 0;
if (!link_info && parse_ctx->imsi) {
link_info = gbproxy_link_info_by_imsi(
peer, parse_ctx->imsi, parse_ctx->imsi_len);
}
if (!link_info && parse_ctx->ptmsi_enc && !parse_ctx->old_raid_is_foreign) {
uint32_t bss_ptmsi;
gprs_parse_tmsi(parse_ctx->ptmsi_enc, &bss_ptmsi);
link_info = gbproxy_link_info_by_ptmsi(peer, bss_ptmsi);
}
if (!link_info)
return NULL;
link_info->is_deregistered = 0;
return link_info;
}
struct gbproxy_link_info *gbproxy_update_link_state_ul(
struct gbproxy_peer *peer,
time_t now,
struct gprs_gb_parse_context *parse_ctx)
{
struct gbproxy_link_info *link_info;
int tlli_is_valid;
link_info = gbproxy_get_link_info_ul(peer, &tlli_is_valid, parse_ctx);
if (parse_ctx->tlli_enc && parse_ctx->llc) {
uint32_t sgsn_tlli;
if (!link_info) {
LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n",
parse_ctx->tlli);
link_info = gbproxy_link_info_alloc(peer);
gbproxy_attach_link_info(peer, now, link_info);
/* Setup TLLIs */
sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info,
parse_ctx->tlli);
link_info->sgsn_tlli.current = sgsn_tlli;
link_info->tlli.current = parse_ctx->tlli;
} else if (!tlli_is_valid) {
/* New TLLI (info found by IMSI or P-TMSI) */
link_info->tlli.current = parse_ctx->tlli;
link_info->tlli.assigned = 0;
link_info->sgsn_tlli.current =
gbproxy_make_sgsn_tlli(peer, link_info,
parse_ctx->tlli);
link_info->sgsn_tlli.assigned = 0;
gbproxy_touch_link_info(peer, link_info, now);
} else {
sgsn_tlli = gbproxy_map_tlli(parse_ctx->tlli, link_info, 0);
if (!sgsn_tlli)
sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info,
parse_ctx->tlli);
gbproxy_validate_tlli(&link_info->tlli,
parse_ctx->tlli, 0);
gbproxy_validate_tlli(&link_info->sgsn_tlli,
sgsn_tlli, 0);
gbproxy_touch_link_info(peer, link_info, now);
}
} else if (link_info) {
gbproxy_touch_link_info(peer, link_info, now);
}
if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
gbproxy_assign_imsi(peer, link_info, parse_ctx);
return link_info;
}
static struct gbproxy_link_info *gbproxy_get_link_info_dl(
struct gbproxy_peer *peer,
struct gprs_gb_parse_context *parse_ctx)
{
struct gbproxy_link_info *link_info = NULL;
/* Which key to use depends on its availability only, if that fails, do
* not retry it with another key (e.g. IMSI). */
if (parse_ctx->tlli_enc)
link_info = gbproxy_link_info_by_sgsn_tlli(peer, parse_ctx->tlli,
parse_ctx->peer_nsei);
/* TODO: Get link_info by (SGSN) P-TMSI if that is available (see
* GSM 08.18, 7.2) instead of using the IMSI as key. */
else if (parse_ctx->imsi)
link_info = gbproxy_link_info_by_imsi(
peer, parse_ctx->imsi, parse_ctx->imsi_len);
if (link_info)
link_info->is_deregistered = 0;
return link_info;
}
struct gbproxy_link_info *gbproxy_update_link_state_dl(
struct gbproxy_peer *peer,
time_t now,
struct gprs_gb_parse_context *parse_ctx)
{
struct gbproxy_link_info *link_info = NULL;
link_info = gbproxy_get_link_info_dl(peer, parse_ctx);
if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && link_info) {
/* A new P-TMSI has been signalled in the message,
* register new TLLI */
uint32_t new_sgsn_ptmsi;
uint32_t new_bss_ptmsi = GSM_RESERVED_TMSI;
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_sgsn_ptmsi);
if (link_info->sgsn_tlli.ptmsi == new_sgsn_ptmsi)
new_bss_ptmsi = link_info->tlli.ptmsi;
if (new_bss_ptmsi == GSM_RESERVED_TMSI)
new_bss_ptmsi = gbproxy_make_bss_ptmsi(peer, new_sgsn_ptmsi);
LOGP(DGPRS, LOGL_INFO,
"Got new PTMSI %08x from SGSN, using %08x for BSS\n",
new_sgsn_ptmsi, new_bss_ptmsi);
/* Setup PTMSIs */
link_info->sgsn_tlli.ptmsi = new_sgsn_ptmsi;
link_info->tlli.ptmsi = new_bss_ptmsi;
} else if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && !link_info &&
!peer->cfg->patch_ptmsi) {
/* A new P-TMSI has been signalled in the message with an unknown
* TLLI, create a new link_info */
/* TODO: Add a test case for this branch */
uint32_t new_ptmsi;
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
LOGP(DGPRS, LOGL_INFO,
"Adding TLLI %08x to list (SGSN, new P-TMSI is %08x)\n",
parse_ctx->tlli, new_ptmsi);
link_info = gbproxy_link_info_alloc(peer);
link_info->sgsn_tlli.current = parse_ctx->tlli;
link_info->tlli.current = parse_ctx->tlli;
link_info->sgsn_tlli.ptmsi = new_ptmsi;
link_info->tlli.ptmsi = new_ptmsi;
gbproxy_attach_link_info(peer, now, link_info);
} else if (parse_ctx->tlli_enc && parse_ctx->llc && !link_info &&
!peer->cfg->patch_ptmsi) {
/* Unknown SGSN TLLI, create a new link_info */
uint32_t new_ptmsi;
link_info = gbproxy_link_info_alloc(peer);
LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN)\n",
parse_ctx->tlli);
gbproxy_attach_link_info(peer, now, link_info);
/* Setup TLLIs */
link_info->sgsn_tlli.current = parse_ctx->tlli;
link_info->tlli.current = parse_ctx->tlli;
if (!parse_ctx->new_ptmsi_enc)
return link_info;
/* A new P-TMSI has been signalled in the message */
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
LOGP(DGPRS, LOGL_INFO,
"Assigning new P-TMSI %08x\n", new_ptmsi);
/* Setup P-TMSIs */
link_info->sgsn_tlli.ptmsi = new_ptmsi;
link_info->tlli.ptmsi = new_ptmsi;
} else if (parse_ctx->tlli_enc && parse_ctx->llc && link_info) {
uint32_t bss_tlli = gbproxy_map_tlli(parse_ctx->tlli,
link_info, 1);
gbproxy_validate_tlli(&link_info->sgsn_tlli, parse_ctx->tlli, 1);
gbproxy_validate_tlli(&link_info->tlli, bss_tlli, 1);
gbproxy_touch_link_info(peer, link_info, now);
} else if (link_info) {
gbproxy_touch_link_info(peer, link_info, now);
}
if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
gbproxy_assign_imsi(peer, link_info, parse_ctx);
return link_info;
}
int gbproxy_update_link_state_after(
struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info,
time_t now,
struct gprs_gb_parse_context *parse_ctx)
{
int rc = 0;
if (parse_ctx->invalidate_tlli && link_info) {
int keep_info =
peer->cfg->keep_link_infos == GBPROX_KEEP_ALWAYS ||
(peer->cfg->keep_link_infos == GBPROX_KEEP_REATTACH &&
parse_ctx->await_reattach) ||
(peer->cfg->keep_link_infos == GBPROX_KEEP_IDENTIFIED &&
link_info->imsi_len > 0);
if (keep_info) {
LOGP(DGPRS, LOGL_INFO, "Unregistering TLLI %08x\n",
link_info->tlli.current);
rc = gbproxy_unregister_link_info(peer, link_info);
} else {
LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list\n",
link_info->tlli.current);
gbproxy_delete_link_info(peer, link_info);
rc = 1;
}
} else if (parse_ctx->to_bss && parse_ctx->tlli_enc &&
parse_ctx->new_ptmsi_enc && link_info) {
/* A new PTMSI has been signaled in the message,
* register new TLLI */
uint32_t new_sgsn_ptmsi = link_info->sgsn_tlli.ptmsi;
uint32_t new_bss_ptmsi = link_info->tlli.ptmsi;
uint32_t new_sgsn_tlli;
uint32_t new_bss_tlli = 0;
new_sgsn_tlli = gprs_tmsi2tlli(new_sgsn_ptmsi, TLLI_LOCAL);
if (new_bss_ptmsi != GSM_RESERVED_TMSI)
new_bss_tlli = gprs_tmsi2tlli(new_bss_ptmsi, TLLI_LOCAL);
LOGP(DGPRS, LOGL_INFO,
"Assigning new TLLI %08x to SGSN, %08x to BSS\n",
new_sgsn_tlli, new_bss_tlli);
gbproxy_reassign_tlli(&link_info->sgsn_tlli,
peer, new_sgsn_tlli);
gbproxy_reassign_tlli(&link_info->tlli,
peer, new_bss_tlli);
gbproxy_remove_matching_link_infos(peer, link_info);
}
gbproxy_remove_stale_link_infos(peer, now);
return rc;
}

View File

@ -1,853 +0,0 @@
/*
* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <time.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/rate_ctr.h>
#include <openbsc/gsm_04_08.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gsm/apn.h>
#include <openbsc/debug.h>
#include <openbsc/gb_proxy.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/misc.h>
static struct gbproxy_config *g_cfg = NULL;
/*
* vty code for mgcp below
*/
static struct cmd_node gbproxy_node = {
GBPROXY_NODE,
"%s(config-gbproxy)# ",
1,
};
static const struct value_string keep_modes[] = {
{GBPROX_KEEP_NEVER, "never"},
{GBPROX_KEEP_REATTACH, "re-attach"},
{GBPROX_KEEP_IDENTIFIED, "identified"},
{GBPROX_KEEP_ALWAYS, "always"},
{0, NULL}
};
static const struct value_string match_ids[] = {
{GBPROX_MATCH_PATCHING, "patching"},
{GBPROX_MATCH_ROUTING, "routing"},
{0, NULL}
};
static void gbprox_vty_print_peer(struct vty *vty, struct gbproxy_peer *peer)
{
struct gprs_ra_id raid;
gsm48_parse_ra(&raid, peer->ra);
vty_out(vty, "NSEI %5u, PTP-BVCI %5u, "
"RAI %u-%u-%u-%u",
peer->nsei, peer->bvci,
raid.mcc, raid.mnc, raid.lac, raid.rac);
if (peer->blocked)
vty_out(vty, " [BVC-BLOCKED]");
vty_out(vty, "%s", VTY_NEWLINE);
}
static int config_write_gbproxy(struct vty *vty)
{
enum gbproxy_match_id match_id;
vty_out(vty, "gbproxy%s", VTY_NEWLINE);
vty_out(vty, " sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei,
VTY_NEWLINE);
if (g_cfg->core_mcc > 0)
vty_out(vty, " core-mobile-country-code %d%s",
g_cfg->core_mcc, VTY_NEWLINE);
if (g_cfg->core_mnc > 0)
vty_out(vty, " core-mobile-network-code %d%s",
g_cfg->core_mnc, VTY_NEWLINE);
for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id) {
struct gbproxy_match *match = &g_cfg->matches[match_id];
if (match->re_str)
vty_out(vty, " match-imsi %s %s%s",
get_value_string(match_ids, match_id),
match->re_str, VTY_NEWLINE);
}
if (g_cfg->core_apn != NULL) {
if (g_cfg->core_apn_size > 0) {
char str[500] = {0};
vty_out(vty, " core-access-point-name %s%s",
osmo_apn_to_str(str, g_cfg->core_apn,
g_cfg->core_apn_size),
VTY_NEWLINE);
} else {
vty_out(vty, " core-access-point-name none%s",
VTY_NEWLINE);
}
}
if (g_cfg->route_to_sgsn2)
vty_out(vty, " secondary-sgsn nsei %u%s", g_cfg->nsip_sgsn2_nsei,
VTY_NEWLINE);
if (g_cfg->tlli_max_age > 0)
vty_out(vty, " link-list max-age %d%s",
g_cfg->tlli_max_age, VTY_NEWLINE);
if (g_cfg->tlli_max_len > 0)
vty_out(vty, " link-list max-length %d%s",
g_cfg->tlli_max_len, VTY_NEWLINE);
vty_out(vty, " link-list keep-mode %s%s",
get_value_string(keep_modes, g_cfg->keep_link_infos),
VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy,
cfg_gbproxy_cmd,
"gbproxy",
"Configure the Gb proxy")
{
vty->node = GBPROXY_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_nsip_sgsn_nsei,
cfg_nsip_sgsn_nsei_cmd,
"sgsn nsei <0-65534>",
"SGSN information\n"
"NSEI to be used in the connection with the SGSN\n"
"The NSEI\n")
{
unsigned int nsei = atoi(argv[0]);
if (g_cfg->route_to_sgsn2 && g_cfg->nsip_sgsn2_nsei == nsei) {
vty_out(vty, "SGSN NSEI %d conflicts with secondary SGSN NSEI%s",
nsei, VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->nsip_sgsn_nsei = nsei;
return CMD_SUCCESS;
}
#define GBPROXY_CORE_MNC_STR "Use this network code for the core network\n"
DEFUN(cfg_gbproxy_core_mnc,
cfg_gbproxy_core_mnc_cmd,
"core-mobile-network-code <1-999>",
GBPROXY_CORE_MNC_STR "NCC value\n")
{
g_cfg->core_mnc = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_core_mnc,
cfg_gbproxy_no_core_mnc_cmd,
"no core-mobile-network-code",
NO_STR GBPROXY_CORE_MNC_STR)
{
g_cfg->core_mnc = 0;
return CMD_SUCCESS;
}
#define GBPROXY_CORE_MCC_STR "Use this country code for the core network\n"
DEFUN(cfg_gbproxy_core_mcc,
cfg_gbproxy_core_mcc_cmd,
"core-mobile-country-code <1-999>",
GBPROXY_CORE_MCC_STR "MCC value\n")
{
g_cfg->core_mcc = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_core_mcc,
cfg_gbproxy_no_core_mcc_cmd,
"no core-mobile-country-code",
NO_STR GBPROXY_CORE_MCC_STR)
{
g_cfg->core_mcc = 0;
return CMD_SUCCESS;
}
#define GBPROXY_MATCH_IMSI_STR "Restrict actions to certain IMSIs\n"
DEFUN(cfg_gbproxy_match_imsi,
cfg_gbproxy_match_imsi_cmd,
"match-imsi (patching|routing) .REGEXP",
GBPROXY_MATCH_IMSI_STR
"Patch MS related information elements on match only\n"
"Route to the secondary SGSN on match only\n"
"Regular expression for the IMSI match\n")
{
const char *filter = argv[1];
const char *err_msg = NULL;
struct gbproxy_match *match;
enum gbproxy_match_id match_id = get_string_value(match_ids, argv[0]);
OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
match_id < GBPROX_MATCH_LAST);
match = &g_cfg->matches[match_id];
if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
vty_out(vty, "Match expression invalid: %s%s",
err_msg, VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->acquire_imsi = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_match_imsi,
cfg_gbproxy_no_match_imsi_cmd,
"no match-imsi",
NO_STR GBPROXY_MATCH_IMSI_STR)
{
enum gbproxy_match_id match_id;
for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id)
gbproxy_clear_patch_filter(&g_cfg->matches[match_id]);
g_cfg->acquire_imsi = 0;
return CMD_SUCCESS;
}
#define GBPROXY_CORE_APN_STR "Use this access point name (APN) for the backbone\n"
#define GBPROXY_CORE_APN_ARG_STR "Replace APN by this string\n" "Remove APN\n"
static int set_core_apn(struct vty *vty, const char *apn)
{
int apn_len;
if (!apn) {
talloc_free(g_cfg->core_apn);
g_cfg->core_apn = NULL;
g_cfg->core_apn_size = 0;
return CMD_SUCCESS;
}
apn_len = strlen(apn);
if (apn_len >= 100) {
vty_out(vty, "APN string too long (max 99 chars)%s",
VTY_NEWLINE);
return CMD_WARNING;
}
if (apn_len == 0) {
talloc_free(g_cfg->core_apn);
/* TODO: replace NULL */
g_cfg->core_apn = talloc_zero_size(NULL, 2);
g_cfg->core_apn_size = 0;
} else {
/* TODO: replace NULL */
g_cfg->core_apn =
talloc_realloc_size(NULL, g_cfg->core_apn, apn_len + 1);
g_cfg->core_apn_size =
gprs_str_to_apn(g_cfg->core_apn, apn_len + 1, apn);
}
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_core_apn,
cfg_gbproxy_core_apn_cmd,
"core-access-point-name (APN|none)",
GBPROXY_CORE_APN_STR GBPROXY_CORE_APN_ARG_STR)
{
if (strcmp(argv[0], "none") == 0)
return set_core_apn(vty, "");
else
return set_core_apn(vty, argv[0]);
}
DEFUN(cfg_gbproxy_no_core_apn,
cfg_gbproxy_no_core_apn_cmd,
"no core-access-point-name",
NO_STR GBPROXY_CORE_APN_STR)
{
return set_core_apn(vty, NULL);
}
/* TODO: Remove the patch-ptmsi command, since P-TMSI patching is enabled
* automatically when needed. This command is only left for manual testing
* (e.g. doing P-TMSI patching without using a secondary SGSN)
*/
#define GBPROXY_PATCH_PTMSI_STR "Patch P-TMSI/TLLI\n"
DEFUN(cfg_gbproxy_patch_ptmsi,
cfg_gbproxy_patch_ptmsi_cmd,
"patch-ptmsi",
GBPROXY_PATCH_PTMSI_STR)
{
g_cfg->patch_ptmsi = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_patch_ptmsi,
cfg_gbproxy_no_patch_ptmsi_cmd,
"no patch-ptmsi",
NO_STR GBPROXY_PATCH_PTMSI_STR)
{
g_cfg->patch_ptmsi = 0;
return CMD_SUCCESS;
}
/* TODO: Remove the acquire-imsi command, since that feature is enabled
* automatically when IMSI matching is enabled. This command is only left for
* manual testing (e.g. doing IMSI acquisition without IMSI based patching)
*/
#define GBPROXY_ACQUIRE_IMSI_STR "Acquire the IMSI before establishing a LLC connection (Experimental)\n"
DEFUN(cfg_gbproxy_acquire_imsi,
cfg_gbproxy_acquire_imsi_cmd,
"acquire-imsi",
GBPROXY_ACQUIRE_IMSI_STR)
{
g_cfg->acquire_imsi = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_acquire_imsi,
cfg_gbproxy_no_acquire_imsi_cmd,
"no acquire-imsi",
NO_STR GBPROXY_ACQUIRE_IMSI_STR)
{
g_cfg->acquire_imsi = 0;
return CMD_SUCCESS;
}
#define GBPROXY_SECOND_SGSN_STR "Route matching LLC connections to a second SGSN (Experimental)\n"
DEFUN(cfg_gbproxy_secondary_sgsn,
cfg_gbproxy_secondary_sgsn_cmd,
"secondary-sgsn nsei <0-65534>",
GBPROXY_SECOND_SGSN_STR
"NSEI to be used in the connection with the SGSN\n"
"The NSEI\n")
{
unsigned int nsei = atoi(argv[0]);
if (g_cfg->nsip_sgsn_nsei == nsei) {
vty_out(vty, "Secondary SGSN NSEI %d conflicts with primary SGSN NSEI%s",
nsei, VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->route_to_sgsn2 = 1;
g_cfg->nsip_sgsn2_nsei = nsei;
g_cfg->patch_ptmsi = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_secondary_sgsn,
cfg_gbproxy_no_secondary_sgsn_cmd,
"no secondary-sgsn",
NO_STR GBPROXY_SECOND_SGSN_STR)
{
g_cfg->route_to_sgsn2 = 0;
g_cfg->nsip_sgsn2_nsei = 0xFFFF;
g_cfg->patch_ptmsi = 0;
return CMD_SUCCESS;
}
#define GBPROXY_LINK_LIST_STR "Set TLLI list parameters\n"
#define GBPROXY_MAX_AGE_STR "Limit maximum age\n"
DEFUN(cfg_gbproxy_link_list_max_age,
cfg_gbproxy_link_list_max_age_cmd,
"link-list max-age <1-999999>",
GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR
"Maximum age in seconds\n")
{
g_cfg->tlli_max_age = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_link_list_no_max_age,
cfg_gbproxy_link_list_no_max_age_cmd,
"no link-list max-age",
NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR)
{
g_cfg->tlli_max_age = 0;
return CMD_SUCCESS;
}
#define GBPROXY_MAX_LEN_STR "Limit list length\n"
DEFUN(cfg_gbproxy_link_list_max_len,
cfg_gbproxy_link_list_max_len_cmd,
"link-list max-length <1-99999>",
GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR
"Maximum number of logical links in the list\n")
{
g_cfg->tlli_max_len = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_link_list_no_max_len,
cfg_gbproxy_link_list_no_max_len_cmd,
"no link-list max-length",
NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR)
{
g_cfg->tlli_max_len = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_link_list_keep_mode,
cfg_gbproxy_link_list_keep_mode_cmd,
"link-list keep-mode (never|re-attach|identified|always)",
GBPROXY_LINK_LIST_STR "How to keep entries for detached logical links\n"
"Discard entry immediately after detachment\n"
"Keep entry if a re-attachment has be requested\n"
"Keep entry if it associated with an IMSI\n"
"Don't discard entries after detachment\n")
{
int val = get_string_value(keep_modes, argv[0]);
OSMO_ASSERT(val >= GBPROX_KEEP_NEVER && val <= GBPROX_KEEP_ALWAYS);
g_cfg->keep_link_infos = val;
return CMD_SUCCESS;
}
DEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy [stats]",
SHOW_STR "Display information about the Gb proxy\n" "Show statistics\n")
{
struct gbproxy_peer *peer;
int show_stats = argc >= 1;
if (show_stats)
vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
gbprox_vty_print_peer(vty, peer);
if (show_stats)
vty_out_rate_ctr_group(vty, " ", peer->ctrg);
}
return CMD_SUCCESS;
}
DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
SHOW_STR "Display information about the Gb proxy\n" "Show logical links\n")
{
struct gbproxy_peer *peer;
char mi_buf[200];
time_t now;
struct timespec ts = {0,};
clock_gettime(CLOCK_MONOTONIC, &ts);
now = ts.tv_sec;
llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &peer->patch_state;
gbprox_vty_print_peer(vty, peer);
llist_for_each_entry(link_info, &state->logical_links, list) {
time_t age = now - link_info->timestamp;
int stored_msgs = 0;
struct llist_head *iter;
llist_for_each(iter, &link_info->stored_msgs)
stored_msgs++;
if (link_info->imsi > 0) {
snprintf(mi_buf, sizeof(mi_buf), "(invalid)");
gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
link_info->imsi,
link_info->imsi_len);
} else {
snprintf(mi_buf, sizeof(mi_buf), "(none)");
}
vty_out(vty, " TLLI %08x, IMSI %s, AGE %d",
link_info->tlli.current, mi_buf, (int)age);
if (stored_msgs)
vty_out(vty, ", STORED %d", stored_msgs);
if (g_cfg->route_to_sgsn2)
vty_out(vty, ", SGSN NSEI %d",
link_info->sgsn_nsei);
if (link_info->is_deregistered)
vty_out(vty, ", DE-REGISTERED");
vty_out(vty, "%s", VTY_NEWLINE);
}
}
return CMD_SUCCESS;
}
DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
"delete-gbproxy-peer <0-65534> bvci <2-65534>",
"Delete a GBProxy peer by NSEI and optionally BVCI\n"
"NSEI number\n"
"Only delete peer with a matching BVCI\n"
"BVCI number\n")
{
const uint16_t nsei = atoi(argv[0]);
const uint16_t bvci = atoi(argv[1]);
int counter;
counter = gbproxy_cleanup_peers(g_cfg, nsei, bvci);
if (counter == 0) {
vty_out(vty, "BVC not found%s", VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
"delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
"Delete a GBProxy peer by NSEI and optionally BVCI\n"
"NSEI number\n"
"Only delete BSSGP connections (BVC)\n"
"Only delete dynamic NS connections (NS-VC)\n"
"Delete BVC and dynamic NS connections\n"
"Show what would be deleted instead of actually deleting\n"
)
{
const uint16_t nsei = atoi(argv[0]);
const char *mode = argv[1];
int dry_run = argc > 2;
int delete_bvc = 0;
int delete_nsvc = 0;
int counter;
if (strcmp(mode, "only-bvc") == 0)
delete_bvc = 1;
else if (strcmp(mode, "only-nsvc") == 0)
delete_nsvc = 1;
else
delete_bvc = delete_nsvc = 1;
if (delete_bvc) {
if (!dry_run)
counter = gbproxy_cleanup_peers(g_cfg, nsei, 0);
else {
struct gbproxy_peer *peer;
counter = 0;
llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
if (peer->nsei != nsei)
continue;
vty_out(vty, "BVC: ");
gbprox_vty_print_peer(vty, peer);
counter += 1;
}
}
vty_out(vty, "%sDeleted %d BVC%s",
dry_run ? "Not " : "", counter, VTY_NEWLINE);
}
if (delete_nsvc) {
struct gprs_ns_inst *nsi = g_cfg->nsi;
struct gprs_nsvc *nsvc, *nsvc2;
counter = 0;
llist_for_each_entry_safe(nsvc, nsvc2, &nsi->gprs_nsvcs, list) {
if (nsvc->nsei != nsei)
continue;
if (nsvc->persistent)
continue;
if (!dry_run)
gprs_nsvc_delete(nsvc);
else
vty_out(vty, "NS-VC: NSEI %5u, NS-VCI %5u, "
"remote %s%s",
nsvc->nsei, nsvc->nsvci,
gprs_ns_ll_str(nsvc), VTY_NEWLINE);
counter += 1;
}
vty_out(vty, "%sDeleted %d NS-VC%s",
dry_run ? "Not " : "", counter, VTY_NEWLINE);
}
return CMD_SUCCESS;
}
#define GBPROXY_DELETE_LINK_STR \
"Delete a GBProxy logical link entry by NSEI and identification\nNSEI number\n"
DEFUN(delete_gb_link_by_id, delete_gb_link_by_id_cmd,
"delete-gbproxy-link <0-65534> (tlli|imsi|sgsn-nsei) IDENT",
GBPROXY_DELETE_LINK_STR
"Delete entries with a matching TLLI (hex)\n"
"Delete entries with a matching IMSI\n"
"Delete entries with a matching SGSN NSEI\n"
"Identification to match\n")
{
const uint16_t nsei = atoi(argv[0]);
enum {MATCH_TLLI = 't', MATCH_IMSI = 'i', MATCH_SGSN = 's'} match;
uint32_t ident = 0;
const char *imsi = NULL;
struct gbproxy_peer *peer = 0;
struct gbproxy_link_info *link_info, *nxt;
struct gbproxy_patch_state *state;
char mi_buf[200];
int found = 0;
match = argv[1][0];
switch (match) {
case MATCH_TLLI: ident = strtoll(argv[2], NULL, 16); break;
case MATCH_IMSI: imsi = argv[2]; break;
case MATCH_SGSN: ident = strtoll(argv[2], NULL, 0); break;
};
peer = gbproxy_peer_by_nsei(g_cfg, nsei);
if (!peer) {
vty_out(vty, "Didn't find peer with NSEI %d%s",
nsei, VTY_NEWLINE);
return CMD_WARNING;
}
state = &peer->patch_state;
llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) {
switch (match) {
case MATCH_TLLI:
if (link_info->tlli.current != ident)
continue;
break;
case MATCH_SGSN:
if (link_info->sgsn_nsei != ident)
continue;
break;
case MATCH_IMSI:
if (!link_info->imsi)
continue;
mi_buf[0] = '\0';
gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
link_info->imsi,
link_info->imsi_len);
if (strcmp(mi_buf, imsi) != 0)
continue;
break;
}
vty_out(vty, "Deleting link with TLLI %08x%s", link_info->tlli.current,
VTY_NEWLINE);
gbproxy_delete_link_info(peer, link_info);
found += 1;
}
if (!found && argc >= 2) {
vty_out(vty, "Didn't find link entry with %s %s%s",
argv[1], argv[2], VTY_NEWLINE);
}
return CMD_SUCCESS;
}
DEFUN(delete_gb_link, delete_gb_link_cmd,
"delete-gbproxy-link <0-65534> (stale|de-registered)",
GBPROXY_DELETE_LINK_STR
"Delete stale entries\n"
"Delete de-registered entries\n")
{
const uint16_t nsei = atoi(argv[0]);
enum {MATCH_STALE = 's', MATCH_DEREGISTERED = 'd'} match;
struct gbproxy_peer *peer = 0;
struct gbproxy_link_info *link_info, *nxt;
struct gbproxy_patch_state *state;
time_t now;
struct timespec ts = {0,};
int found = 0;
match = argv[1][0];
peer = gbproxy_peer_by_nsei(g_cfg, nsei);
if (!peer) {
vty_out(vty, "Didn't find peer with NSEI %d%s",
nsei, VTY_NEWLINE);
return CMD_WARNING;
}
state = &peer->patch_state;
clock_gettime(CLOCK_MONOTONIC, &ts);
now = ts.tv_sec;
if (match == MATCH_STALE) {
found = gbproxy_remove_stale_link_infos(peer, now);
if (found)
vty_out(vty, "Deleted %d stale logical link%s%s",
found, found == 1 ? "" : "s", VTY_NEWLINE);
} else {
llist_for_each_entry_safe(link_info, nxt,
&state->logical_links, list) {
if (!link_info->is_deregistered)
continue;
gbproxy_delete_link_info(peer, link_info);
found += 1;
}
}
if (found)
vty_out(vty, "Deleted %d %s logical link%s%s",
found, argv[1], found == 1 ? "" : "s", VTY_NEWLINE);
return CMD_SUCCESS;
}
/*
* legacy commands to provide an upgrade path from "broken" releases
* or pre-releases
*/
DEFUN_DEPRECATED(cfg_gbproxy_broken_apn_match,
cfg_gbproxy_broken_apn_match_cmd,
"core-access-point-name none match-imsi .REGEXP",
GBPROXY_CORE_APN_STR GBPROXY_MATCH_IMSI_STR "Remove APN\n"
"Patch MS related information elements on match only\n"
"Route to the secondary SGSN on match only\n"
"Regular expression for the IMSI match\n")
{
const char *filter = argv[0];
const char *err_msg = NULL;
struct gbproxy_match *match;
enum gbproxy_match_id match_id = get_string_value(match_ids, "patching");
/* apply APN none */
set_core_apn(vty, "");
/* do the matching... with copy and paste */
OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
match_id < GBPROX_MATCH_LAST);
match = &g_cfg->matches[match_id];
if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
vty_out(vty, "Match expression invalid: %s%s",
err_msg, VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->acquire_imsi = 1;
return CMD_SUCCESS;
}
#define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n"
#define GBPROXY_MAX_LEN_STR "Limit list length\n"
DEFUN_DEPRECATED(cfg_gbproxy_depr_tlli_list_max_len,
cfg_gbproxy_depr_tlli_list_max_len_cmd,
"tlli-list max-length <1-99999>",
GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR
"Maximum number of TLLIs in the list\n")
{
g_cfg->tlli_max_len = atoi(argv[0]);
return CMD_SUCCESS;
}
int gbproxy_vty_init(void)
{
install_element_ve(&show_gbproxy_cmd);
install_element_ve(&show_gbproxy_links_cmd);
install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
install_element(ENABLE_NODE, &delete_gb_link_by_id_cmd);
install_element(ENABLE_NODE, &delete_gb_link_cmd);
install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
install_node(&gbproxy_node, config_write_gbproxy);
vty_install_default(GBPROXY_NODE);
install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_mcc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_mnc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_match_imsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_secondary_sgsn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_patch_ptmsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_acquire_imsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_age_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_len_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_keep_mode_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mnc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_match_imsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_secondary_sgsn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_patch_ptmsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_acquire_imsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_age_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_len_cmd);
/* broken or deprecated to allow an upgrade path */
install_element(GBPROXY_NODE, &cfg_gbproxy_broken_apn_match_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_depr_tlli_list_max_len_cmd);
return 0;
}
int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
{
int rc;
g_cfg = cfg;
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
return rc;
}
return 0;
}

View File

@ -1,636 +0,0 @@
/* GPRS Gb message parser */
/* (C) 2014 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/gsm/gsm48.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <openbsc/gprs_gb_parse.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/debug.h>
#include <osmocom/gprs/gprs_bssgp.h>
static int gprs_gb_parse_gmm_attach_req(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
size_t value_len;
parse_ctx->llc_msg_name = "ATTACH_REQ";
/* Skip MS network capability */
if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 ||
value_len < 1 || value_len > 8)
/* invalid */
return 0;
/* Skip Attach type */
/* Skip Ciphering key sequence number */
/* Skip DRX parameter */
osmo_shift_v_fixed(&data, &data_len, 3, NULL);
/* Get Mobile identity */
if (osmo_shift_lv(&data, &data_len, &value, &value_len) <= 0 ||
value_len < 5 || value_len > 8)
/* invalid */
return 0;
if (gprs_is_mi_tmsi(value, value_len)) {
parse_ctx->ptmsi_enc = value + 1;
} else if (gprs_is_mi_imsi(value, value_len)) {
parse_ctx->imsi = value;
parse_ctx->imsi_len = value_len;
}
if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
return 0;
parse_ctx->old_raid_enc = value;
return 1;
}
static int gprs_gb_parse_gmm_attach_ack(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
size_t value_len;
parse_ctx->llc_msg_name = "ATTACH_ACK";
/* Skip Attach result */
/* Skip Force to standby */
/* Skip Periodic RA update timer */
/* Skip Radio priority for SMS */
/* Skip Spare half octet */
osmo_shift_v_fixed(&data, &data_len, 3, NULL);
if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
return 0;
parse_ctx->raid_enc = value;
/* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */
osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL);
/* Skip Negotiated READY timer value (GPRS timer, opt, TV, length 2) */
osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_TIMER_READY, 1, NULL);
/* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */
if (osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI,
&value, &value_len) > 0 &&
gprs_is_mi_tmsi(value, value_len))
parse_ctx->new_ptmsi_enc = value + 1;
return 1;
}
static int gprs_gb_parse_gmm_attach_rej(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
parse_ctx->llc_msg_name = "ATTACH_REJ";
/* GMM cause */
if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0)
return 0;
parse_ctx->invalidate_tlli = 1;
return 1;
}
static int gprs_gb_parse_gmm_detach_req(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
size_t value_len;
int detach_type;
int power_off;
parse_ctx->llc_msg_name = "DETACH_REQ";
/* Skip spare half octet */
/* Get Detach type */
if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0)
/* invalid */
return 0;
detach_type = *value & 0x07;
power_off = *value & 0x08 ? 1 : 0;
if (parse_ctx->to_bss) {
/* Network originated */
if (detach_type == GPRS_DET_T_MT_REATT_REQ)
parse_ctx->await_reattach = 1;
} else {
/* Mobile originated */
if (power_off)
parse_ctx->invalidate_tlli = 1;
/* Get P-TMSI (Mobile identity), see GSM 24.008, 9.4.5.2 */
if (osmo_match_shift_tlv(&data, &data_len,
GSM48_IE_GMM_ALLOC_PTMSI, &value, &value_len) > 0)
{
if (gprs_is_mi_tmsi(value, value_len))
parse_ctx->ptmsi_enc = value + 1;
}
}
return 1;
}
static int gprs_gb_parse_gmm_ra_upd_req(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
parse_ctx->llc_msg_name = "RA_UPD_REQ";
/* Skip Update type */
/* Skip GPRS ciphering key sequence number */
osmo_shift_v_fixed(&data, &data_len, 1, NULL);
if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
return 0;
parse_ctx->old_raid_enc = value;
return 1;
}
static int gprs_gb_parse_gmm_ra_upd_rej(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
uint8_t cause;
int force_standby;
parse_ctx->llc_msg_name = "RA_UPD_REJ";
/* GMM cause */
if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0)
return 0;
cause = value[0];
/* Force to standby, 1/2 */
/* spare bits, 1/2 */
if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0)
return 0;
force_standby = (value[0] & 0x07) == 0x01;
if (cause == GMM_CAUSE_IMPL_DETACHED && !force_standby)
parse_ctx->await_reattach = 1;
parse_ctx->invalidate_tlli = 1;
return 1;
}
static int gprs_gb_parse_gmm_ra_upd_ack(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
size_t value_len;
parse_ctx->llc_msg_name = "RA_UPD_ACK";
/* Skip Force to standby */
/* Skip Update result */
/* Skip Periodic RA update timer */
osmo_shift_v_fixed(&data, &data_len, 2, NULL);
if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
return 0;
parse_ctx->raid_enc = value;
/* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */
osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL);
/* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */
if (osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI,
&value, &value_len) > 0 &&
gprs_is_mi_tmsi(value, value_len))
parse_ctx->new_ptmsi_enc = value + 1;
return 1;
}
static int gprs_gb_parse_gmm_ptmsi_reall_cmd(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
size_t value_len;
parse_ctx->llc_msg_name = "PTMSI_REALL_CMD";
LOGP(DLLC, LOGL_NOTICE,
"Got P-TMSI Reallocation Command which is not covered by unit tests yet.\n");
/* Allocated P-TMSI */
if (osmo_shift_lv(&data, &data_len, &value, &value_len) > 0 &&
gprs_is_mi_tmsi(value, value_len))
parse_ctx->new_ptmsi_enc = value + 1;
if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
return 0;
parse_ctx->raid_enc = value;
return 1;
}
static int gprs_gb_parse_gmm_id_resp(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
size_t value_len;
parse_ctx->llc_msg_name = "ID_RESP";
/* Mobile identity, Mobile identity 10.5.1.4, M LV 2-10 */
if (osmo_shift_lv(&data, &data_len, &value, &value_len) <= 0 ||
value_len < 1 || value_len > 9)
/* invalid */
return 0;
if (gprs_is_mi_tmsi(value, value_len)) {
parse_ctx->ptmsi_enc = value + 1;
} else if (gprs_is_mi_imsi(value, value_len)) {
parse_ctx->imsi = value;
parse_ctx->imsi_len = value_len;
}
return 1;
}
static int gprs_gb_parse_gsm_act_pdp_req(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
ssize_t old_len;
uint8_t *value;
size_t value_len;
parse_ctx->llc_msg_name = "ACT_PDP_REQ";
/* Skip Requested NSAPI */
/* Skip Requested LLC SAPI */
osmo_shift_v_fixed(&data, &data_len, 2, NULL);
/* Skip Requested QoS (support 04.08 and 24.008) */
if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 ||
value_len < 4 || value_len > 14)
/* invalid */
return 0;
/* Skip Requested PDP address */
if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 ||
value_len < 2 || value_len > 18)
/* invalid */
return 0;
/* Access point name */
old_len = osmo_match_shift_tlv(&data, &data_len,
GSM48_IE_GSM_APN, &value, &value_len);
if (old_len > 0 && value_len >=1 && value_len <= 100) {
parse_ctx->apn_ie = data - old_len;
parse_ctx->apn_ie_len = old_len;
}
return 1;
}
int gprs_gb_parse_dtap(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
struct gsm48_hdr *g48h;
uint8_t pdisc;
uint8_t msg_type;
if (osmo_shift_v_fixed(&data, &data_len, sizeof(*g48h), (uint8_t **)&g48h) <= 0)
return 0;
parse_ctx->g48_hdr = g48h;
pdisc = gsm48_hdr_pdisc(g48h);
if (pdisc != GSM48_PDISC_MM_GPRS && pdisc != GSM48_PDISC_SM_GPRS)
return 1;
msg_type = gsm48_hdr_msg_type(g48h);
switch (msg_type) {
case GSM48_MT_GMM_ATTACH_REQ:
return gprs_gb_parse_gmm_attach_req(data, data_len, parse_ctx);
case GSM48_MT_GMM_ATTACH_REJ:
return gprs_gb_parse_gmm_attach_rej(data, data_len, parse_ctx);
case GSM48_MT_GMM_ATTACH_ACK:
return gprs_gb_parse_gmm_attach_ack(data, data_len, parse_ctx);
case GSM48_MT_GMM_RA_UPD_REQ:
return gprs_gb_parse_gmm_ra_upd_req(data, data_len, parse_ctx);
case GSM48_MT_GMM_RA_UPD_REJ:
return gprs_gb_parse_gmm_ra_upd_rej(data, data_len, parse_ctx);
case GSM48_MT_GMM_RA_UPD_ACK:
return gprs_gb_parse_gmm_ra_upd_ack(data, data_len, parse_ctx);
case GSM48_MT_GMM_PTMSI_REALL_CMD:
return gprs_gb_parse_gmm_ptmsi_reall_cmd(data, data_len, parse_ctx);
case GSM48_MT_GSM_ACT_PDP_REQ:
return gprs_gb_parse_gsm_act_pdp_req(data, data_len, parse_ctx);
case GSM48_MT_GMM_ID_RESP:
return gprs_gb_parse_gmm_id_resp(data, data_len, parse_ctx);
case GSM48_MT_GMM_DETACH_REQ:
return gprs_gb_parse_gmm_detach_req(data, data_len, parse_ctx);
case GSM48_MT_GMM_DETACH_ACK:
parse_ctx->llc_msg_name = "DETACH_ACK";
parse_ctx->invalidate_tlli = 1;
break;
default:
LOGP(DLLC, LOGL_NOTICE,
"Unhandled GSM 04.08 message type %s for protocol discriminator %s.\n",
get_value_string(gprs_msgt_gmm_names, msg_type), get_value_string(gsm48_pdisc_names, pdisc));
break;
};
return 1;
}
int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len,
struct gprs_gb_parse_context *parse_ctx)
{
struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed;
int rc;
int fcs;
/* parse LLC */
rc = gprs_llc_hdr_parse(ghp, llc, llc_len);
gprs_llc_hdr_dump(ghp, NULL);
if (rc != 0) {
LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n");
return 0;
}
fcs = gprs_llc_fcs(llc, ghp->crc_length);
LOGP(DLLC, LOGL_DEBUG, "Got LLC message, CRC: %06x (computed %06x)\n",
ghp->fcs, fcs);
if (!ghp->data)
return 0;
if (ghp->sapi != GPRS_SAPI_GMM)
return 1;
if (ghp->cmd != GPRS_LLC_UI)
return 1;
if (ghp->is_encrypted) {
parse_ctx->need_decryption = 1;
return 0;
}
return gprs_gb_parse_dtap(ghp->data, ghp->data_len, parse_ctx);
}
int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len,
struct gprs_gb_parse_context *parse_ctx)
{
struct bssgp_normal_hdr *bgph;
struct bssgp_ud_hdr *budh = NULL;
struct tlv_parsed *tp = &parse_ctx->bssgp_tp;
uint8_t pdu_type;
uint8_t *data;
size_t data_len;
int rc;
if (bssgp_len < sizeof(struct bssgp_normal_hdr))
return 0;
bgph = (struct bssgp_normal_hdr *)bssgp;
pdu_type = bgph->pdu_type;
if (pdu_type == BSSGP_PDUT_UL_UNITDATA ||
pdu_type == BSSGP_PDUT_DL_UNITDATA) {
if (bssgp_len < sizeof(struct bssgp_ud_hdr))
return 0;
budh = (struct bssgp_ud_hdr *)bssgp;
bgph = NULL;
data = budh->data;
data_len = bssgp_len - sizeof(*budh);
} else {
data = bgph->data;
data_len = bssgp_len - sizeof(*bgph);
}
parse_ctx->pdu_type = pdu_type;
parse_ctx->bud_hdr = budh;
parse_ctx->bgp_hdr = bgph;
parse_ctx->bssgp_data = data;
parse_ctx->bssgp_data_len = data_len;
if (bssgp_tlv_parse(tp, data, data_len) < 0)
return 0;
if (budh)
parse_ctx->tlli_enc = (uint8_t *)&budh->tlli;
if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA))
parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA);
if (TLVP_PRESENT(tp, BSSGP_IE_CELL_ID))
parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_CELL_ID);
if (TLVP_PRESENT(tp, BSSGP_IE_IMSI)) {
parse_ctx->imsi = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_IMSI);
parse_ctx->imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI);
}
if (TLVP_PRESENT(tp, BSSGP_IE_TLLI)) {
if (parse_ctx->tlli_enc)
/* This is TLLI old, don't confuse it with TLLI current */
parse_ctx->old_tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI);
else
parse_ctx->tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI);
}
if (TLVP_PRESENT(tp, BSSGP_IE_TMSI) && pdu_type == BSSGP_PDUT_PAGING_PS)
parse_ctx->bssgp_ptmsi_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TMSI);
if (TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) {
uint8_t *llc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
size_t llc_len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU);
rc = gprs_gb_parse_llc(llc, llc_len, parse_ctx);
if (!rc)
return 0;
parse_ctx->llc = llc;
parse_ctx->llc_len = llc_len;
}
if (parse_ctx->tlli_enc) {
uint32_t tmp_tlli;
memcpy(&tmp_tlli, parse_ctx->tlli_enc, sizeof(tmp_tlli));
parse_ctx->tlli = ntohl(tmp_tlli);
}
if (parse_ctx->bssgp_raid_enc && parse_ctx->old_raid_enc &&
memcmp(parse_ctx->bssgp_raid_enc, parse_ctx->old_raid_enc, 6) != 0)
parse_ctx->old_raid_is_foreign = 1;
return 1;
}
void gprs_gb_log_parse_context(int log_level,
struct gprs_gb_parse_context *parse_ctx,
const char *default_msg_name)
{
const char *msg_name;
const char *sep = "";
if (!parse_ctx->tlli_enc &&
!parse_ctx->ptmsi_enc &&
!parse_ctx->new_ptmsi_enc &&
!parse_ctx->bssgp_ptmsi_enc &&
!parse_ctx->imsi)
return;
msg_name = gprs_gb_message_name(parse_ctx, default_msg_name);
if (parse_ctx->llc_msg_name)
msg_name = parse_ctx->llc_msg_name;
LOGP(DGPRS, log_level, "%s: Got", msg_name);
if (parse_ctx->tlli_enc) {
LOGPC(DGPRS, log_level, "%s TLLI %08x", sep, parse_ctx->tlli);
sep = ",";
}
if (parse_ctx->old_tlli_enc) {
LOGPC(DGPRS, log_level, "%s old TLLI %02x%02x%02x%02x", sep,
parse_ctx->old_tlli_enc[0],
parse_ctx->old_tlli_enc[1],
parse_ctx->old_tlli_enc[2],
parse_ctx->old_tlli_enc[3]);
sep = ",";
}
if (parse_ctx->bssgp_raid_enc) {
struct gprs_ra_id raid;
gsm48_parse_ra(&raid, parse_ctx->bssgp_raid_enc);
LOGPC(DGPRS, log_level, "%s BSSGP RAID %u-%u-%u-%u", sep,
raid.mcc, raid.mnc, raid.lac, raid.rac);
sep = ",";
}
if (parse_ctx->raid_enc) {
struct gprs_ra_id raid;
gsm48_parse_ra(&raid, parse_ctx->raid_enc);
LOGPC(DGPRS, log_level, "%s RAID %u-%u-%u-%u", sep,
raid.mcc, raid.mnc, raid.lac, raid.rac);
sep = ",";
}
if (parse_ctx->old_raid_enc) {
struct gprs_ra_id raid;
gsm48_parse_ra(&raid, parse_ctx->old_raid_enc);
LOGPC(DGPRS, log_level, "%s old RAID %u-%u-%u-%u", sep,
raid.mcc, raid.mnc, raid.lac, raid.rac);
sep = ",";
}
if (parse_ctx->bssgp_ptmsi_enc) {
uint32_t ptmsi = GSM_RESERVED_TMSI;
gprs_parse_tmsi(parse_ctx->bssgp_ptmsi_enc, &ptmsi);
LOGPC(DGPRS, log_level, "%s BSSGP PTMSI %08x", sep, ptmsi);
sep = ",";
}
if (parse_ctx->ptmsi_enc) {
uint32_t ptmsi = GSM_RESERVED_TMSI;
gprs_parse_tmsi(parse_ctx->ptmsi_enc, &ptmsi);
LOGPC(DGPRS, log_level, "%s PTMSI %08x", sep, ptmsi);
sep = ",";
}
if (parse_ctx->new_ptmsi_enc) {
uint32_t new_ptmsi = GSM_RESERVED_TMSI;
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
LOGPC(DGPRS, log_level, "%s new PTMSI %08x", sep, new_ptmsi);
sep = ",";
}
if (parse_ctx->imsi) {
char mi_buf[200];
mi_buf[0] = '\0';
gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
parse_ctx->imsi, parse_ctx->imsi_len);
LOGPC(DGPRS, log_level, "%s IMSI %s",
sep, mi_buf);
sep = ",";
}
if (parse_ctx->invalidate_tlli) {
LOGPC(DGPRS, log_level, "%s invalidate", sep);
sep = ",";
}
if (parse_ctx->await_reattach) {
LOGPC(DGPRS, log_level, "%s re-attach", sep);
sep = ",";
}
LOGPC(DGPRS, log_level, "\n");
}
const char *gprs_gb_message_name(const struct gprs_gb_parse_context *parse_ctx,
const char *default_msg_name)
{
if (parse_ctx->llc_msg_name)
return parse_ctx->llc_msg_name;
if (parse_ctx->g48_hdr)
return "GMM";
if (parse_ctx->llc)
return "LLC";
if (parse_ctx->bud_hdr)
return "BSSGP-UNITDATA";
if (parse_ctx->bgp_hdr)
return "BSSGP";
return "unknown";
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,251 +0,0 @@
/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */
/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <errno.h>
#include <stdint.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <openbsc/gsm_data.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/crc24.h>
static const struct value_string llc_cmd_strs[] = {
{ GPRS_LLC_NULL, "NULL" },
{ GPRS_LLC_RR, "RR" },
{ GPRS_LLC_ACK, "ACK" },
{ GPRS_LLC_RNR, "RNR" },
{ GPRS_LLC_SACK, "SACK" },
{ GPRS_LLC_DM, "DM" },
{ GPRS_LLC_DISC, "DISC" },
{ GPRS_LLC_UA, "UA" },
{ GPRS_LLC_SABM, "SABM" },
{ GPRS_LLC_FRMR, "FRMR" },
{ GPRS_LLC_XID, "XID" },
{ GPRS_LLC_UI, "UI" },
{ 0, NULL }
};
#define LLC_ALLOC_SIZE 16384
#define UI_HDR_LEN 3
#define N202 4
#define CRC24_LENGTH 3
int gprs_llc_fcs(uint8_t *data, unsigned int len)
{
uint32_t fcs_calc;
fcs_calc = crc24_calc(INIT_CRC24, data, len);
fcs_calc = ~fcs_calc;
fcs_calc &= 0xffffff;
return fcs_calc;
}
void gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph, struct gprs_llc_lle *lle)
{
const char *gea;
uint32_t iov_ui = 0;
if (lle) {
gea = get_value_string(gprs_cipher_names, lle->llme->algo);
iov_ui = lle->llme->iov_ui;
} else
gea = "GEA?";
DEBUGP(DLLC, "LLC SAPI=%u %c %c %c %s IOV-UI=0x%06x FCS=0x%06x ",
gph->sapi, gph->is_cmd ? 'C' : 'R', gph->ack_req ? 'A' : ' ',
gph->is_encrypted ? 'E' : 'U',
gea, iov_ui, gph->fcs);
if (gph->cmd)
DEBUGPC(DLLC, "CMD=%s ", get_value_string(llc_cmd_strs, gph->cmd));
if (gph->data)
DEBUGPC(DLLC, "DATA ");
DEBUGPC(DLLC, "\n");
}
/* parse a GPRS LLC header, also check for invalid frames */
int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp,
uint8_t *llc_hdr, int len)
{
uint8_t *ctrl = llc_hdr+1;
if (len <= CRC24_LENGTH)
return -EIO;
ghp->crc_length = len - CRC24_LENGTH;
ghp->ack_req = 0;
/* Section 5.5: FCS */
ghp->fcs = *(llc_hdr + len - 3);
ghp->fcs |= *(llc_hdr + len - 2) << 8;
ghp->fcs |= *(llc_hdr + len - 1) << 16;
/* Section 6.2.1: invalid PD field */
if (llc_hdr[0] & 0x80)
return -EIO;
/* This only works for the MS->SGSN direction */
if (llc_hdr[0] & 0x40)
ghp->is_cmd = 0;
else
ghp->is_cmd = 1;
ghp->sapi = llc_hdr[0] & 0xf;
/* Section 6.2.3: check for reserved SAPI */
switch (ghp->sapi) {
case 0:
case 4:
case 6:
case 0xa:
case 0xc:
case 0xd:
case 0xf:
return -EINVAL;
}
if ((ctrl[0] & 0x80) == 0) {
/* I (Information transfer + Supervisory) format */
uint8_t k;
ghp->data = ctrl + 3;
if (ctrl[0] & 0x40)
ghp->ack_req = 1;
ghp->seq_tx = (ctrl[0] & 0x1f) << 4;
ghp->seq_tx |= (ctrl[1] >> 4);
ghp->seq_rx = (ctrl[1] & 0x7) << 6;
ghp->seq_rx |= (ctrl[2] >> 2);
switch (ctrl[2] & 0x03) {
case 0:
ghp->cmd = GPRS_LLC_RR;
break;
case 1:
ghp->cmd = GPRS_LLC_ACK;
break;
case 2:
ghp->cmd = GPRS_LLC_RNR;
break;
case 3:
ghp->cmd = GPRS_LLC_SACK;
k = ctrl[3] & 0x1f;
ghp->data += 1 + k;
break;
}
ghp->data_len = (llc_hdr + len - 3) - ghp->data;
} else if ((ctrl[0] & 0xc0) == 0x80) {
/* S (Supervisory) format */
ghp->data = NULL;
ghp->data_len = 0;
if (ctrl[0] & 0x20)
ghp->ack_req = 1;
ghp->seq_rx = (ctrl[0] & 0x7) << 6;
ghp->seq_rx |= (ctrl[1] >> 2);
switch (ctrl[1] & 0x03) {
case 0:
ghp->cmd = GPRS_LLC_RR;
break;
case 1:
ghp->cmd = GPRS_LLC_ACK;
break;
case 2:
ghp->cmd = GPRS_LLC_RNR;
break;
case 3:
ghp->cmd = GPRS_LLC_SACK;
break;
}
} else if ((ctrl[0] & 0xe0) == 0xc0) {
/* UI (Unconfirmed Inforamtion) format */
ghp->cmd = GPRS_LLC_UI;
ghp->data = ctrl + 2;
ghp->data_len = (llc_hdr + len - 3) - ghp->data;
ghp->seq_tx = (ctrl[0] & 0x7) << 6;
ghp->seq_tx |= (ctrl[1] >> 2);
if (ctrl[1] & 0x02) {
ghp->is_encrypted = 1;
/* FIXME: encryption */
}
if (ctrl[1] & 0x01) {
/* FCS over hdr + all inf fields */
} else {
/* FCS over hdr + N202 octets (4) */
if (ghp->crc_length > UI_HDR_LEN + N202)
ghp->crc_length = UI_HDR_LEN + N202;
}
} else {
/* U (Unnumbered) format: 1 1 1 P/F M4 M3 M2 M1 */
ghp->data = NULL;
ghp->data_len = 0;
switch (ctrl[0] & 0xf) {
case GPRS_LLC_U_NULL_CMD:
ghp->cmd = GPRS_LLC_NULL;
break;
case GPRS_LLC_U_DM_RESP:
ghp->cmd = GPRS_LLC_DM;
break;
case GPRS_LLC_U_DISC_CMD:
ghp->cmd = GPRS_LLC_DISC;
break;
case GPRS_LLC_U_UA_RESP:
ghp->cmd = GPRS_LLC_UA;
break;
case GPRS_LLC_U_SABM_CMD:
ghp->cmd = GPRS_LLC_SABM;
break;
case GPRS_LLC_U_FRMR_RESP:
ghp->cmd = GPRS_LLC_FRMR;
break;
case GPRS_LLC_U_XID:
ghp->cmd = GPRS_LLC_XID;
ghp->data = ctrl + 1;
ghp->data_len = (llc_hdr + len - 3) - ghp->data;
break;
default:
return -EIO;
}
}
/* FIXME: parse sack frame */
if (ghp->cmd == GPRS_LLC_SACK) {
LOGP(DLLC, LOGL_NOTICE, "Unsupported SACK frame\n");
return -EIO;
}
return 0;
}

View File

@ -1,116 +0,0 @@
/* VTY interface for our GPRS LLC implementation */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <time.h>
#include <arpa/inet.h>
#include <openbsc/gsm_data.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <openbsc/debug.h>
#include <openbsc/signal.h>
#include <openbsc/gprs_llc.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
struct value_string gprs_llc_state_strs[] = {
{ GPRS_LLES_UNASSIGNED, "TLLI Unassigned" },
{ GPRS_LLES_ASSIGNED_ADM, "TLLI Assigned" },
{ GPRS_LLES_LOCAL_EST, "Local Establishment" },
{ GPRS_LLES_REMOTE_EST, "Remote Establishment" },
{ GPRS_LLES_ABM, "Asynchronous Balanced Mode" },
{ GPRS_LLES_LOCAL_REL, "Local Release" },
{ GPRS_LLES_TIMER_REC, "Timer Recovery" },
{ 0, NULL }
};
static void vty_dump_lle(struct vty *vty, struct gprs_llc_lle *lle)
{
struct gprs_llc_params *par = &lle->params;
vty_out(vty, " SAPI %2u State %s VUsend=%u, VUrecv=%u", lle->sapi,
get_value_string(gprs_llc_state_strs, lle->state),
lle->vu_send, lle->vu_recv);
vty_out(vty, " Vsent=%u Vack=%u Vrecv=%u, RetransCtr=%u%s",
lle->v_sent, lle->v_ack, lle->v_recv,
lle->retrans_ctr, VTY_NEWLINE);
vty_out(vty, " T200=%u, N200=%u, N201-U=%u, N201-I=%u, mD=%u, "
"mU=%u, kD=%u, kU=%u%s", par->t200_201, par->n200,
par->n201_u, par->n201_i, par->mD, par->mU, par->kD,
par->kU, VTY_NEWLINE);
}
static uint8_t valid_sapis[] = { 1, 2, 3, 5, 7, 8, 9, 11 };
static void vty_dump_llme(struct vty *vty, struct gprs_llc_llme *llme)
{
unsigned int i;
struct timespec now_tp = {0};
clock_gettime(CLOCK_MONOTONIC, &now_tp);
vty_out(vty, "TLLI %08x (Old TLLI %08x) BVCI=%u NSEI=%u %s: "
"IOV-UI=0x%06x CKSN=%d Age=%d: State %s%s", llme->tlli,
llme->old_tlli, llme->bvci, llme->nsei,
get_value_string(gprs_cipher_names, llme->algo), llme->iov_ui,
llme->cksn, llme->age_timestamp == GPRS_LLME_RESET_AGE ? 0 :
(int)(now_tp.tv_sec - (time_t)llme->age_timestamp),
get_value_string(gprs_llc_state_strs, llme->state), VTY_NEWLINE);
for (i = 0; i < ARRAY_SIZE(valid_sapis); i++) {
struct gprs_llc_lle *lle;
uint8_t sapi = valid_sapis[i];
if (sapi >= ARRAY_SIZE(llme->lle))
continue;
lle = &llme->lle[sapi];
vty_dump_lle(vty, lle);
}
}
DEFUN(show_llc, show_llc_cmd,
"show llc",
SHOW_STR "Display information about the LLC protocol")
{
struct gprs_llc_llme *llme;
vty_out(vty, "State of LLC Entities%s", VTY_NEWLINE);
llist_for_each_entry(llme, &gprs_llc_llmes, list) {
vty_dump_llme(vty, llme);
}
return CMD_SUCCESS;
}
int gprs_llc_vty_init(void)
{
install_element_ve(&show_llc_cmd);
return 0;
}

View File

@ -1,281 +0,0 @@
/* GPRS LLC XID field encoding/decoding as per 3GPP TS 44.064 */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_llc_xid.h>
const struct value_string gprs_llc_xid_type_names[] = {
{ GPRS_LLC_XID_T_VERSION, "VERSION"},
{ GPRS_LLC_XID_T_IOV_UI, "IOV_UI"},
{ GPRS_LLC_XID_T_IOV_I, "IOV_I"},
{ GPRS_LLC_XID_T_T200, "T200"},
{ GPRS_LLC_XID_T_N200, "N200"},
{ GPRS_LLC_XID_T_N201_U, "N201_"},
{ GPRS_LLC_XID_T_N201_I, "N201_I"},
{ GPRS_LLC_XID_T_mD, "mD"},
{ GPRS_LLC_XID_T_mU, "mU"},
{ GPRS_LLC_XID_T_kD, "kD"},
{ GPRS_LLC_XID_T_kU, "kU"},
{ GPRS_LLC_XID_T_L3_PAR, "L3_PAR"},
{ GPRS_LLC_XID_T_RESET, "RESET"},
{ 0, NULL },
};
/* Parse XID parameter field */
static int decode_xid_field(struct gprs_llc_xid_field *xid_field,
const uint8_t *src, uint8_t src_len)
{
uint8_t xl;
uint8_t type;
uint8_t len;
int src_counter = 0;
/* Exit immediately if it is clear that no
* parseable data is present */
if (src_len < 1 || !src)
return -EINVAL;
/* Extract header info */
xl = (*src >> 7) & 1;
type = (*src >> 2) & 0x1F;
/* Extract length field */
len = (*src) & 0x3;
src++;
src_counter++;
if (xl) {
if (src_len < 2)
return -EINVAL;
len = (len << 6) & 0xC0;
len |= ((*src) >> 2) & 0x3F;
src++;
src_counter++;
}
/* Fill out struct */
xid_field->type = type;
xid_field->data_len = len;
if (len > 0) {
if (src_len < src_counter + len)
return -EINVAL;
xid_field->data =
talloc_memdup(xid_field,src,xid_field->data_len);
} else
xid_field->data = NULL;
/* Return consumed length */
return src_counter + len;
}
/* Encode XID parameter field */
static int encode_xid_field(uint8_t *dst, int dst_maxlen,
const struct gprs_llc_xid_field *xid_field)
{
int xl = 0;
/* When the length does not fit into 2 bits,
* we need extended length fields */
if (xid_field->data_len > 3)
xl = 1;
/* Exit immediately if it is clear that no
* encoding result can be stored */
if (dst_maxlen < xid_field->data_len + 1 + xl)
return -EINVAL;
/* There are only 5 bits reserved for the type, exit on exceed */
if (xid_field->type > 31)
return -EINVAL;
/* Encode header */
memset(dst, 0, dst_maxlen);
if (xl)
dst[0] |= 0x80;
dst[0] |= (((xid_field->type) & 0x1F) << 2);
if (xl) {
dst[0] |= (((xid_field->data_len) >> 6) & 0x03);
dst[1] = ((xid_field->data_len) << 2) & 0xFC;
} else
dst[0] |= ((xid_field->data_len) & 0x03);
/* Append payload data */
if (xid_field->data && xid_field->data_len)
memcpy(dst + 1 + xl, xid_field->data, xid_field->data_len);
/* Return generated length */
return xid_field->data_len + 1 + xl;
}
/* Transform a list with XID fields into a XID message (dst) */
int gprs_llc_compile_xid(uint8_t *dst, int dst_maxlen,
const struct llist_head *xid_fields)
{
struct gprs_llc_xid_field *xid_field;
int rc;
int byte_counter = 0;
OSMO_ASSERT(xid_fields);
OSMO_ASSERT(dst);
llist_for_each_entry_reverse(xid_field, xid_fields, list) {
/* Encode XID-Field */
rc = encode_xid_field(dst, dst_maxlen, xid_field);
if (rc < 0)
return -EINVAL;
/* Advance pointer and lower maxlen for the
* next encoding round */
dst += rc;
byte_counter += rc;
dst_maxlen -= rc;
}
/* Return generated length */
return byte_counter;
}
/* Transform a XID message (dst) into a list of XID fields */
struct llist_head *gprs_llc_parse_xid(const void *ctx, const uint8_t *src,
int src_len)
{
struct gprs_llc_xid_field *xid_field;
struct llist_head *xid_fields;
int rc;
int max_loops = src_len;
OSMO_ASSERT(src);
xid_fields = talloc_zero(ctx, struct llist_head);
INIT_LLIST_HEAD(xid_fields);
while (1) {
/* Bail in case decode_xid_field() constantly returns zero */
if (max_loops <= 0) {
talloc_free(xid_fields);
return NULL;
}
/* Decode XID field */
xid_field = talloc_zero(xid_fields, struct gprs_llc_xid_field);
rc = decode_xid_field(xid_field, src, src_len);
/* Immediately stop on error */
if (rc < 0) {
talloc_free(xid_fields);
return NULL;
}
/* Add parsed XID field to list */
llist_add(&xid_field->list, xid_fields);
/* Advance pointer and lower dst_len for the next
* decoding round */
src += rc;
src_len -= rc;
/* We are (scuccessfully) done when no further byes are left */
if (src_len == 0)
return xid_fields;
max_loops--;
}
}
/* Create a duplicate of an XID-Field */
struct gprs_llc_xid_field *gprs_llc_dup_xid_field(const void *ctx, const struct
gprs_llc_xid_field
*xid_field)
{
struct gprs_llc_xid_field *dup;
OSMO_ASSERT(xid_field);
/* Create a copy of the XID field in memory */
dup = talloc_memdup(ctx, xid_field, sizeof(*xid_field));
dup->data = talloc_memdup(ctx, xid_field->data, xid_field->data_len);
/* Unlink duplicate from source list */
INIT_LLIST_HEAD(&dup->list);
return dup;
}
/* Copy an llist with xid fields */
struct llist_head *gprs_llc_copy_xid(const void *ctx,
const struct llist_head *xid_fields)
{
struct gprs_llc_xid_field *xid_field;
struct llist_head *xid_fields_copy;
OSMO_ASSERT(xid_fields);
xid_fields_copy = talloc_zero(ctx, struct llist_head);
INIT_LLIST_HEAD(xid_fields_copy);
/* Create duplicates and add them to the target list */
llist_for_each_entry(xid_field, xid_fields, list) {
llist_add(&gprs_llc_dup_xid_field(ctx, xid_field)->list,
xid_fields_copy);
}
return xid_fields_copy;
}
/* Dump a list with XID fields (Debug) */
void gprs_llc_dump_xid_fields(const struct llist_head *xid_fields,
unsigned int logl)
{
struct gprs_llc_xid_field *xid_field;
OSMO_ASSERT(xid_fields);
llist_for_each_entry(xid_field, xid_fields, list) {
if (xid_field->data_len) {
OSMO_ASSERT(xid_field->data);
LOGP(DLLC, logl,
"XID: type %s, data_len=%d, data=%s\n",
get_value_string(gprs_llc_xid_type_names,
xid_field->type),
xid_field->data_len,
osmo_hexdump_nospc(xid_field->data,
xid_field->data_len));
} else {
LOGP(DLLC, logl,
"XID: type=%d, data_len=%d, data=NULL\n",
xid_field->type, xid_field->data_len);
}
}
}

View File

@ -1,933 +0,0 @@
/* GPRS SGSN functionality */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/backtrace.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/apn.h>
#include <openbsc/gprs_subscriber.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/signal.h>
#include "openbsc/gprs_llc.h"
#include <pdp.h>
#include <time.h>
#include <openssl/rand.h>
#include "../../bscconfig.h"
#if BUILD_IU
#include <osmocom/ranap/iu_client.h>
#endif
#define GPRS_LLME_CHECK_TICK 30
extern struct sgsn_instance *sgsn;
LLIST_HEAD(sgsn_mm_ctxts);
LLIST_HEAD(sgsn_ggsn_ctxts);
LLIST_HEAD(sgsn_apn_ctxts);
LLIST_HEAD(sgsn_pdp_ctxts);
static const struct rate_ctr_desc mmctx_ctr_description[] = {
{ "sign.packets.in", "Signalling Messages ( In)" },
{ "sign.packets.out", "Signalling Messages (Out)" },
{ "udata.packets.in", "User Data Messages ( In)" },
{ "udata.packets.out", "User Data Messages (Out)" },
{ "udata.bytes.in", "User Data Bytes ( In)" },
{ "udata.bytes.out", "User Data Bytes (Out)" },
{ "pdp_ctx_act", "PDP Context Activations " },
{ "suspend", "SUSPEND Count " },
{ "paging.ps", "Paging Packet Switched " },
{ "paging.cs", "Paging Circuit Switched " },
{ "ra_update", "Routing Area Update " },
};
static const struct rate_ctr_group_desc mmctx_ctrg_desc = {
.group_name_prefix = "sgsn.mmctx",
.group_description = "SGSN MM Context Statistics",
.num_ctr = ARRAY_SIZE(mmctx_ctr_description),
.ctr_desc = mmctx_ctr_description,
.class_id = OSMO_STATS_CLASS_SUBSCRIBER,
};
static const struct rate_ctr_desc pdpctx_ctr_description[] = {
{ "udata.packets.in", "User Data Messages ( In)" },
{ "udata.packets.out", "User Data Messages (Out)" },
{ "udata.bytes.in", "User Data Bytes ( In)" },
{ "udata.bytes.out", "User Data Bytes (Out)" },
};
static const struct rate_ctr_group_desc pdpctx_ctrg_desc = {
.group_name_prefix = "sgsn.pdpctx",
.group_description = "SGSN PDP Context Statistics",
.num_ctr = ARRAY_SIZE(pdpctx_ctr_description),
.ctr_desc = pdpctx_ctr_description,
.class_id = OSMO_STATS_CLASS_SUBSCRIBER,
};
static const struct rate_ctr_desc sgsn_ctr_description[] = {
{ "llc.dl_bytes", "Count sent LLC bytes before giving it to the bssgp layer" },
{ "llc.ul_bytes", "Count sucessful received LLC bytes (encrypt & fcs correct)" },
{ "llc.dl_packets", "Count sucessful sent LLC packets before giving it to the bssgp layer" },
{ "llc.ul_packets", "Count sucessful received LLC packets (encrypt & fcs correct)" },
{ "gprs.attach_requested", "Received attach requests" },
{ "gprs.attach_accepted", "Sent attach accepts" },
{ "gprs.attach_rejected", "Sent attach rejects" },
{ "gprs.detach_requested", "Received detach requests" },
{ "gprs.detach_acked", "Sent detach acks" },
{ "gprs.routing_area_requested", "Received routing area requests" },
{ "gprs.routing_area_requested", "Sent routing area acks" },
{ "gprs.routing_area_requested", "Sent routing area rejects" },
{ "pdp.activate_requested", "Received activate requests" },
{ "pdp.activate_rejected", "Sent activate rejects" },
{ "pdp.activate_accepted", "Sent activate accepts" },
{ "pdp.request_activated", "unused" },
{ "pdp.request_activate_rejected", "unused" },
{ "pdp.modify_requested", "unused" },
{ "pdp.modify_accepted", "unused" },
{ "pdp.dl_deactivate_requested", "Sent deactivate requests" },
{ "pdp.dl_deactivate_accepted", "Sent deactivate accepted" },
{ "pdp.ul_deactivate_requested", "Received deactivate requests" },
{ "pdp.ul_deactivate_accepted", "Received deactivate accepts" },
};
static const struct rate_ctr_group_desc sgsn_ctrg_desc = {
"sgsn",
"SGSN Overall Statistics",
OSMO_STATS_CLASS_GLOBAL,
ARRAY_SIZE(sgsn_ctr_description),
sgsn_ctr_description,
};
void sgsn_rate_ctr_init() {
sgsn->rate_ctrs = rate_ctr_group_alloc(tall_bsc_ctx, &sgsn_ctrg_desc, 0);
OSMO_ASSERT(sgsn->rate_ctrs);
}
/* look-up an SGSN MM context based on Iu UE context (struct ue_conn_ctx)*/
struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx)
{
struct sgsn_mm_ctx *ctx;
llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
if (ctx->ran_type == MM_CTX_T_UTRAN_Iu
&& uectx == ctx->iu.ue_ctx)
return ctx;
}
return NULL;
}
/* look-up a SGSN MM context based on TLLI + RAI */
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli,
const struct gprs_ra_id *raid)
{
struct sgsn_mm_ctx *ctx;
llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
if ((tlli == ctx->gb.tlli || tlli == ctx->gb.tlli_new) &&
gprs_ra_id_equals(raid, &ctx->ra))
return ctx;
}
return NULL;
}
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli,
const struct gprs_ra_id *raid)
{
struct sgsn_mm_ctx *ctx;
int tlli_type;
/* TODO: Also check the P_TMSI signature to be safe. That signature
* should be different (at least with a sufficiently high probability)
* after SGSN restarts and for multiple SGSN instances.
*/
tlli_type = gprs_tlli_type(tlli);
if (tlli_type != TLLI_FOREIGN && tlli_type != TLLI_LOCAL)
return NULL;
llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
if ((gprs_tmsi2tlli(ctx->p_tmsi, tlli_type) == tlli ||
gprs_tmsi2tlli(ctx->p_tmsi_old, tlli_type) == tlli) &&
gprs_ra_id_equals(raid, &ctx->ra))
return ctx;
}
return NULL;
}
struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t p_tmsi)
{
struct sgsn_mm_ctx *ctx;
llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
if (p_tmsi == ctx->p_tmsi ||
(ctx->p_tmsi_old && ctx->p_tmsi_old == p_tmsi))
return ctx;
}
return NULL;
}
struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi)
{
struct sgsn_mm_ctx *ctx;
llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
if (!strcmp(imsi, ctx->imsi))
return ctx;
}
return NULL;
}
/* Allocate a new SGSN MM context for GERAN_Gb */
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli,
const struct gprs_ra_id *raid)
{
struct sgsn_mm_ctx *ctx;
ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx);
if (!ctx)
return NULL;
memcpy(&ctx->ra, raid, sizeof(ctx->ra));
ctx->ran_type = MM_CTX_T_GERAN_Gb;
ctx->gb.tlli = tlli;
ctx->gmm_state = GMM_DEREGISTERED;
ctx->pmm_state = MM_IDLE;
ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
ctx->ciph_algo = sgsn->cfg.cipher;
LOGMMCTXP(LOGL_DEBUG, ctx, "Allocated with %s cipher.\n",
get_value_string(gprs_cipher_names, ctx->ciph_algo));
ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli);
if (!ctx->ctrg) {
LOGMMCTXP(LOGL_ERROR, ctx, "Cannot allocate counter group\n");
talloc_free(ctx);
return NULL;
}
INIT_LLIST_HEAD(&ctx->pdp_list);
llist_add(&ctx->list, &sgsn_mm_ctxts);
return ctx;
}
/* Allocate a new SGSN MM context */
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx)
{
#if BUILD_IU
struct sgsn_mm_ctx *ctx;
ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx);
if (!ctx)
return NULL;
ctx->ran_type = MM_CTX_T_UTRAN_Iu;
ctx->iu.ue_ctx = uectx;
ctx->iu.ue_ctx->rab_assign_addr_enc = sgsn->cfg.iu.rab_assign_addr_enc;
ctx->iu.new_key = 1;
ctx->gmm_state = GMM_DEREGISTERED;
ctx->pmm_state = PMM_DETACHED;
ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, 0);
if (!ctx->ctrg) {
LOGMMCTXP(LOGL_ERROR, ctx, "Cannot allocate counter group\n");
talloc_free(ctx);
return NULL;
}
/* Need to get RAID from IU conn */
ctx->ra = ctx->iu.ue_ctx->ra_id;
INIT_LLIST_HEAD(&ctx->pdp_list);
llist_add(&ctx->list, &sgsn_mm_ctxts);
return ctx;
#else
return NULL;
#endif
}
/* this is a hard _free_ function, it doesn't clean up the PDP contexts
* in libgtp! */
static void sgsn_mm_ctx_free(struct sgsn_mm_ctx *mm)
{
struct sgsn_pdp_ctx *pdp, *pdp2;
/* Unlink from global list of MM contexts */
llist_del(&mm->list);
/* Free all PDP contexts */
llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list)
sgsn_pdp_ctx_free(pdp);
rate_ctr_group_free(mm->ctrg);
talloc_free(mm);
}
void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *mm)
{
struct gprs_llc_llme *llme = NULL;
uint32_t tlli = mm->gb.tlli;
struct sgsn_pdp_ctx *pdp, *pdp2;
struct sgsn_signal_data sig_data;
if (mm->ran_type == MM_CTX_T_GERAN_Gb)
llme = mm->gb.llme;
else
OSMO_ASSERT(mm->gb.llme == NULL);
/* Forget about ongoing look-ups */
if (mm->ggsn_lookup) {
LOGMMCTXP(LOGL_NOTICE, mm,
"Cleaning mmctx with on-going query.\n");
mm->ggsn_lookup->mmctx = NULL;
mm->ggsn_lookup = NULL;
}
/* delete all existing PDP contexts for this MS */
llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list) {
LOGMMCTXP(LOGL_NOTICE, mm,
"Dropping PDP context for NSAPI=%u\n", pdp->nsapi);
sgsn_pdp_ctx_terminate(pdp);
}
if (osmo_timer_pending(&mm->timer)) {
LOGMMCTXP(LOGL_INFO, mm, "Cancelling MM timer %u\n", mm->T);
osmo_timer_del(&mm->timer);
}
memset(&sig_data, 0, sizeof(sig_data));
sig_data.mm = mm;
osmo_signal_dispatch(SS_SGSN, S_SGSN_MM_FREE, &sig_data);
/* Detach from subscriber which is possibly freed then */
if (mm->subscr) {
struct gprs_subscr *subscr = gprs_subscr_get(mm->subscr);
gprs_subscr_cleanup(subscr);
gprs_subscr_put(subscr);
}
sgsn_mm_ctx_free(mm);
mm = NULL;
if (llme) {
/* TLLI unassignment, must be called after sgsn_mm_ctx_free */
gprs_llgmm_assign(llme, tlli, 0xffffffff);
}
}
/* look up PDP context by MM context and NSAPI */
struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
uint8_t nsapi)
{
struct sgsn_pdp_ctx *pdp;
llist_for_each_entry(pdp, &mm->pdp_list, list) {
if (pdp->nsapi == nsapi)
return pdp;
}
return NULL;
}
/* look up PDP context by MM context and transaction ID */
struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
uint8_t tid)
{
struct sgsn_pdp_ctx *pdp;
llist_for_each_entry(pdp, &mm->pdp_list, list) {
if (pdp->ti == tid)
return pdp;
}
return NULL;
}
/* you don't want to use this directly, call sgsn_create_pdp_ctx() */
struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
uint8_t nsapi)
{
struct sgsn_pdp_ctx *pdp;
pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi);
if (pdp)
return NULL;
pdp = talloc_zero(tall_bsc_ctx, struct sgsn_pdp_ctx);
if (!pdp)
return NULL;
pdp->mm = mm;
pdp->nsapi = nsapi;
pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi);
if (!pdp->ctrg) {
LOGPDPCTXP(LOGL_ERROR, pdp, "Error allocation counter group\n");
talloc_free(pdp);
return NULL;
}
llist_add(&pdp->list, &mm->pdp_list);
llist_add(&pdp->g_list, &sgsn_pdp_ctxts);
return pdp;
}
/*
* This function will not trigger any GSM DEACT PDP ACK messages, so you
* probably want to call sgsn_delete_pdp_ctx() instead if the connection
* isn't detached already.
*/
void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp)
{
struct sgsn_signal_data sig_data;
OSMO_ASSERT(pdp->mm != NULL);
/* There might still be pending callbacks in libgtp. So the parts of
* this object relevant to GTP need to remain intact in this case. */
LOGPDPCTXP(LOGL_INFO, pdp, "Forcing release of PDP context\n");
if (pdp->mm->ran_type == MM_CTX_T_GERAN_Gb) {
/* Force the deactivation of the SNDCP layer */
sndcp_sm_deactivate_ind(&pdp->mm->gb.llme->lle[pdp->sapi], pdp->nsapi);
}
memset(&sig_data, 0, sizeof(sig_data));
sig_data.pdp = pdp;
osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_TERMINATE, &sig_data);
/* Detach from MM context */
llist_del(&pdp->list);
pdp->mm = NULL;
sgsn_delete_pdp_ctx(pdp);
}
/*
* Don't call this function directly unless you know what you are doing.
* In normal conditions use sgsn_delete_pdp_ctx and in unspecified or
* implementation dependent abnormal ones sgsn_pdp_ctx_terminate.
*/
void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
{
struct sgsn_signal_data sig_data;
memset(&sig_data, 0, sizeof(sig_data));
sig_data.pdp = pdp;
osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_FREE, &sig_data);
rate_ctr_group_free(pdp->ctrg);
if (pdp->mm)
llist_del(&pdp->list);
llist_del(&pdp->g_list);
/* _if_ we still have a library handle, at least set it to NULL
* to avoid any dereferences of the now-deleted PDP context from
* sgsn_libgtp:cb_data_ind() */
if (pdp->lib) {
struct pdp_t *lib = pdp->lib;
LOGPDPCTXP(LOGL_NOTICE, pdp, "freeing PDP context that still "
"has a libgtp handle attached to it, this shouldn't "
"happen!\n");
osmo_generate_backtrace();
lib->priv = NULL;
}
if (pdp->destroy_ggsn)
sgsn_ggsn_ctx_free(pdp->ggsn);
talloc_free(pdp);
}
/* GGSN contexts */
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id)
{
struct sgsn_ggsn_ctx *ggc;
ggc = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_ctx);
if (!ggc)
return NULL;
ggc->id = id;
ggc->gtp_version = 1;
ggc->remote_restart_ctr = -1;
/* if we are called from config file parse, this gsn doesn't exist yet */
ggc->gsn = sgsn->gsn;
llist_add(&ggc->list, &sgsn_ggsn_ctxts);
return ggc;
}
void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc)
{
llist_del(&ggc->list);
talloc_free(ggc);
}
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id)
{
struct sgsn_ggsn_ctx *ggc;
llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
if (id == ggc->id)
return ggc;
}
return NULL;
}
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr)
{
struct sgsn_ggsn_ctx *ggc;
llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr)))
return ggc;
}
return NULL;
}
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id)
{
struct sgsn_ggsn_ctx *ggc;
ggc = sgsn_ggsn_ctx_by_id(id);
if (!ggc)
ggc = sgsn_ggsn_ctx_alloc(id);
return ggc;
}
/* APN contexts */
static struct apn_ctx *sgsn_apn_ctx_alloc(const char *ap_name, const char *imsi_prefix)
{
struct apn_ctx *actx;
actx = talloc_zero(tall_bsc_ctx, struct apn_ctx);
if (!actx)
return NULL;
actx->name = talloc_strdup(actx, ap_name);
actx->imsi_prefix = talloc_strdup(actx, imsi_prefix);
llist_add_tail(&actx->list, &sgsn_apn_ctxts);
return actx;
}
void sgsn_apn_ctx_free(struct apn_ctx *actx)
{
llist_del(&actx->list);
talloc_free(actx);
}
struct apn_ctx *sgsn_apn_ctx_match(const char *name, const char *imsi)
{
struct apn_ctx *actx;
struct apn_ctx *found_actx = NULL;
size_t imsi_prio = 0;
size_t name_prio = 0;
size_t name_req_len = strlen(name);
llist_for_each_entry(actx, &sgsn_apn_ctxts, list) {
size_t name_ref_len, imsi_ref_len;
const char *name_ref_start, *name_match_start;
imsi_ref_len = strlen(actx->imsi_prefix);
if (strncmp(actx->imsi_prefix, imsi, imsi_ref_len) != 0)
continue;
if (imsi_ref_len < imsi_prio)
continue;
/* IMSI matches */
name_ref_start = &actx->name[0];
if (name_ref_start[0] == '*') {
/* Suffix match */
name_ref_start += 1;
name_ref_len = strlen(name_ref_start);
if (name_ref_len > name_req_len)
continue;
} else {
name_ref_len = strlen(name_ref_start);
if (name_ref_len != name_req_len)
continue;
}
name_match_start = name + (name_req_len - name_ref_len);
if (strcasecmp(name_match_start, name_ref_start) != 0)
continue;
/* IMSI and name match */
if (imsi_ref_len == imsi_prio && name_ref_len < name_prio)
/* Lower priority, skip */
continue;
imsi_prio = imsi_ref_len;
name_prio = name_ref_len;
found_actx = actx;
}
return found_actx;
}
struct apn_ctx *sgsn_apn_ctx_by_name(const char *name, const char *imsi_prefix)
{
struct apn_ctx *actx;
llist_for_each_entry(actx, &sgsn_apn_ctxts, list) {
if (strcasecmp(name, actx->name) == 0 &&
strcasecmp(imsi_prefix, actx->imsi_prefix) == 0)
return actx;
}
return NULL;
}
struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix)
{
struct apn_ctx *actx;
actx = sgsn_apn_ctx_by_name(name, imsi_prefix);
if (!actx)
actx = sgsn_apn_ctx_alloc(name, imsi_prefix);
return actx;
}
uint32_t sgsn_alloc_ptmsi(void)
{
struct sgsn_mm_ctx *mm;
uint32_t ptmsi = 0xdeadbeef;
int max_retries = 100;
restart:
if (RAND_bytes((uint8_t *) &ptmsi, sizeof(ptmsi)) != 1)
goto failed;
/* Enforce that the 2 MSB are set without loosing the distance between
* identical values. Since rand() has no duplicate values within a
* period (because the size of the state is the same like the size of
* the random value), this leads to a distance of period/4 when the
* distribution of the 2 MSB is uniform. This approach fails with a
* probability of (3/4)^max_retries, only 1% of the approaches will
* need more than 16 numbers (even distribution assumed).
*
* Alternatively, a freeze list could be used if another PRNG is used
* or when this approach proves to be not sufficient.
*/
if (ptmsi >= 0xC0000000) {
if (!max_retries--)
goto failed;
goto restart;
}
ptmsi |= 0xC0000000;
if (ptmsi == GSM_RESERVED_TMSI) {
if (!max_retries--)
goto failed;
goto restart;
}
llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
if (mm->p_tmsi == ptmsi) {
if (!max_retries--)
goto failed;
goto restart;
}
}
return ptmsi;
failed:
LOGP(DGPRS, LOGL_ERROR, "Failed to allocate a P-TMSI\n");
return GSM_RESERVED_TMSI;
}
static void drop_one_pdp(struct sgsn_pdp_ctx *pdp)
{
if (pdp->mm->gmm_state == GMM_REGISTERED_NORMAL)
gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL);
else {
/* FIXME: GPRS paging in case MS is SUSPENDED */
LOGPDPCTXP(LOGL_NOTICE, pdp, "Hard-dropping PDP ctx due to GGSN "
"recovery\n");
/* FIXME: how to tell this to libgtp? */
sgsn_pdp_ctx_free(pdp);
}
}
/* High-level function to be called in case a GGSN has disappeared or
* otherwise lost state (recovery procedure) */
int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn)
{
struct sgsn_mm_ctx *mm;
int num = 0;
llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
struct sgsn_pdp_ctx *pdp;
llist_for_each_entry(pdp, &mm->pdp_list, list) {
if (pdp->ggsn == ggsn) {
drop_one_pdp(pdp);
num++;
}
}
}
return num;
}
void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx)
{
OSMO_ASSERT(mmctx != NULL);
LOGMMCTXP(LOGL_INFO, mmctx, "Subscriber data update\n");
sgsn_auth_update(mmctx);
}
static void insert_extra(struct tlv_parsed *tp,
struct sgsn_subscriber_data *data,
struct sgsn_subscriber_pdp_data *pdp)
{
tp->lv[OSMO_IE_GSM_SUB_QOS].len = pdp->qos_subscribed_len;
tp->lv[OSMO_IE_GSM_SUB_QOS].val = pdp->qos_subscribed;
/* Prefer PDP charging characteristics of per subscriber one */
if (pdp->has_pdp_charg) {
tp->lv[OSMO_IE_GSM_CHARG_CHAR].len = sizeof(pdp->pdp_charg);
tp->lv[OSMO_IE_GSM_CHARG_CHAR].val = &pdp->pdp_charg[0];
} else if (data->has_pdp_charg) {
tp->lv[OSMO_IE_GSM_CHARG_CHAR].len = sizeof(data->pdp_charg);
tp->lv[OSMO_IE_GSM_CHARG_CHAR].val = &data->pdp_charg[0];
}
}
/**
* The tlv_parsed tp parameter will be modified to insert a
* OSMO_IE_GSM_SUB_QOS in case the data is available in the
* PDP context handling.
*/
struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx,
struct tlv_parsed *tp,
enum gsm48_gsm_cause *gsm_cause,
char *out_apn_str)
{
char req_apn_str[GSM_APN_LENGTH] = {0};
const struct apn_ctx *apn_ctx = NULL;
const char *selected_apn_str = NULL;
struct sgsn_subscriber_pdp_data *pdp;
struct sgsn_ggsn_ctx *ggsn = NULL;
int allow_any_apn = 0;
out_apn_str[0] = '\0';
if (TLVP_PRESENT(tp, GSM48_IE_GSM_APN)) {
if (TLVP_LEN(tp, GSM48_IE_GSM_APN) >= GSM_APN_LENGTH - 1) {
LOGMMCTXP(LOGL_ERROR, mmctx, "APN IE too long\n");
*gsm_cause = GSM_CAUSE_INV_MAND_INFO;
return NULL;
}
osmo_apn_to_str(req_apn_str,
TLVP_VAL(tp, GSM48_IE_GSM_APN),
TLVP_LEN(tp, GSM48_IE_GSM_APN));
if (strcmp(req_apn_str, "*") == 0)
req_apn_str[0] = 0;
}
if (mmctx->subscr == NULL)
allow_any_apn = 1;
if (strlen(req_apn_str) == 0 && !allow_any_apn) {
/* No specific APN requested, check for an APN that is both
* granted and configured */
llist_for_each_entry(pdp, &mmctx->subscr->sgsn_data->pdp_list, list) {
if (strcmp(pdp->apn_str, "*") == 0)
{
allow_any_apn = 1;
selected_apn_str = "";
insert_extra(tp, mmctx->subscr->sgsn_data, pdp);
continue;
}
if (!llist_empty(&sgsn_apn_ctxts)) {
apn_ctx = sgsn_apn_ctx_match(req_apn_str, mmctx->imsi);
/* Not configured */
if (apn_ctx == NULL)
continue;
}
insert_extra(tp, mmctx->subscr->sgsn_data, pdp);
selected_apn_str = pdp->apn_str;
break;
}
} else if (!allow_any_apn) {
/* Check whether the given APN is granted */
llist_for_each_entry(pdp, &mmctx->subscr->sgsn_data->pdp_list, list) {
if (strcmp(pdp->apn_str, "*") == 0) {
insert_extra(tp, mmctx->subscr->sgsn_data, pdp);
selected_apn_str = req_apn_str;
allow_any_apn = 1;
continue;
}
if (strcasecmp(pdp->apn_str, req_apn_str) == 0) {
insert_extra(tp, mmctx->subscr->sgsn_data, pdp);
selected_apn_str = req_apn_str;
break;
}
}
} else if (strlen(req_apn_str) != 0) {
/* Any APN is allowed */
selected_apn_str = req_apn_str;
} else {
/* Prefer the GGSN associated with the wildcard APN */
selected_apn_str = "";
}
if (!allow_any_apn && selected_apn_str == NULL) {
/* Access not granted */
LOGMMCTXP(LOGL_NOTICE, mmctx,
"The requested APN '%s' is not allowed\n",
req_apn_str);
*gsm_cause = GSM_CAUSE_REQ_SERV_OPT_NOTSUB;
return NULL;
}
/* copy the selected apn_str */
if (selected_apn_str)
strcpy(out_apn_str, selected_apn_str);
else
out_apn_str[0] = '\0';
if (apn_ctx == NULL && selected_apn_str)
apn_ctx = sgsn_apn_ctx_match(selected_apn_str, mmctx->imsi);
if (apn_ctx != NULL) {
ggsn = apn_ctx->ggsn;
} else if (llist_empty(&sgsn_apn_ctxts)) {
/* No configuration -> use GGSN 0 */
ggsn = sgsn_ggsn_ctx_by_id(0);
} else if (allow_any_apn &&
(selected_apn_str == NULL || strlen(selected_apn_str) == 0)) {
/* No APN given and no default configuration -> Use GGSN 0 */
ggsn = sgsn_ggsn_ctx_by_id(0);
} else {
/* No matching configuration found */
LOGMMCTXP(LOGL_NOTICE, mmctx,
"The selected APN '%s' has not been configured\n",
selected_apn_str);
*gsm_cause = GSM_CAUSE_MISSING_APN;
return NULL;
}
if (!ggsn) {
LOGMMCTXP(LOGL_NOTICE, mmctx,
"No static GGSN configured. Selected APN '%s'\n",
selected_apn_str);
return NULL;
}
LOGMMCTXP(LOGL_INFO, mmctx,
"Found GGSN %d for APN '%s' (requested '%s')\n",
ggsn->id, selected_apn_str ? selected_apn_str : "---",
req_apn_str);
return ggsn;
}
static void sgsn_llme_cleanup_free(struct gprs_llc_llme *llme)
{
struct sgsn_mm_ctx *mmctx = NULL;
llist_for_each_entry(mmctx, &sgsn_mm_ctxts, list) {
if (llme == mmctx->gb.llme) {
gsm0408_gprs_access_cancelled(mmctx, SGSN_ERROR_CAUSE_NONE);
return;
}
}
/* No MM context found */
LOGP(DGPRS, LOGL_INFO, "Deleting orphaned LLME, TLLI 0x%08x\n",
llme->tlli);
gprs_llgmm_unassign(llme);
}
static void sgsn_llme_check_cb(void *data_)
{
struct gprs_llc_llme *llme, *llme_tmp;
struct timespec now_tp;
time_t now, age;
time_t max_age = gprs_max_time_to_idle();
int rc;
rc = clock_gettime(CLOCK_MONOTONIC, &now_tp);
OSMO_ASSERT(rc >= 0);
now = now_tp.tv_sec;
LOGP(DGPRS, LOGL_DEBUG,
"Checking for inactive LLMEs, time = %u\n", (unsigned)now);
llist_for_each_entry_safe(llme, llme_tmp, &gprs_llc_llmes, list) {
if (llme->age_timestamp == GPRS_LLME_RESET_AGE)
llme->age_timestamp = now;
age = now - llme->age_timestamp;
if (age > max_age || age < 0) {
LOGP(DGPRS, LOGL_INFO,
"Inactivity timeout for TLLI 0x%08x, age %d\n",
llme->tlli, (int)age);
sgsn_llme_cleanup_free(llme);
}
}
osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0);
}
void sgsn_inst_init()
{
osmo_timer_setup(&sgsn->llme_timer, sgsn_llme_check_cb, NULL);
osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,323 +0,0 @@
/* GPRS SNDCP header compression entity management tools */
/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <errno.h>
#include <stdbool.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_sndcp_xid.h>
#include <openbsc/gprs_sndcp_comp.h>
#include <openbsc/gprs_sndcp_pcomp.h>
#include <openbsc/gprs_sndcp_dcomp.h>
/* Create a new compression entity from a XID-Field */
static struct gprs_sndcp_comp *gprs_sndcp_comp_create(const void *ctx,
const struct
gprs_sndcp_comp_field
*comp_field)
{
struct gprs_sndcp_comp *comp_entity;
comp_entity = talloc_zero(ctx, struct gprs_sndcp_comp);
/* Copy relevant information from the SNDCP-XID field */
comp_entity->entity = comp_field->entity;
comp_entity->comp_len = comp_field->comp_len;
memcpy(comp_entity->comp, comp_field->comp, sizeof(comp_entity->comp));
if (comp_field->rfc1144_params) {
comp_entity->nsapi_len = comp_field->rfc1144_params->nsapi_len;
memcpy(comp_entity->nsapi,
comp_field->rfc1144_params->nsapi,
sizeof(comp_entity->nsapi));
} else if (comp_field->rfc2507_params) {
comp_entity->nsapi_len = comp_field->rfc2507_params->nsapi_len;
memcpy(comp_entity->nsapi,
comp_field->rfc2507_params->nsapi,
sizeof(comp_entity->nsapi));
} else if (comp_field->rohc_params) {
comp_entity->nsapi_len = comp_field->rohc_params->nsapi_len;
memcpy(comp_entity->nsapi, comp_field->rohc_params->nsapi,
sizeof(comp_entity->nsapi));
} else if (comp_field->v42bis_params) {
comp_entity->nsapi_len = comp_field->v42bis_params->nsapi_len;
memcpy(comp_entity->nsapi,
comp_field->v42bis_params->nsapi,
sizeof(comp_entity->nsapi));
} else if (comp_field->v44_params) {
comp_entity->nsapi_len = comp_field->v44_params->nsapi_len;
memcpy(comp_entity->nsapi,
comp_field->v44_params->nsapi,
sizeof(comp_entity->nsapi));
} else {
/* The caller is expected to check carefully if the all
* data fields required for compression entity creation
* are present. Otherwise we blow an assertion here */
OSMO_ASSERT(false);
}
comp_entity->algo = comp_field->algo;
/* Check if an NSAPI is selected, if not, it does not make sense
* to create the compression entity, since the caller should
* have checked the presence of the NSAPI, we blow an assertion
* in case of missing NSAPIs */
OSMO_ASSERT(comp_entity->nsapi_len > 0);
/* Determine of which class our compression entity will be
* (Protocol or Data compresson ?) */
comp_entity->compclass = gprs_sndcp_get_compression_class(comp_field);
OSMO_ASSERT(comp_entity->compclass != -1);
/* Create an algorithm specific compression context */
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
if (gprs_sndcp_pcomp_init(ctx, comp_entity, comp_field) != 0) {
talloc_free(comp_entity);
comp_entity = NULL;
}
} else {
if (gprs_sndcp_dcomp_init(ctx, comp_entity, comp_field) != 0) {
talloc_free(comp_entity);
comp_entity = NULL;
}
}
/* Bail on failure */
if (comp_entity == NULL) {
LOGP(DSNDCP, LOGL_ERROR,
"Compression entity creation failed!\n");
return NULL;
}
/* Display info message */
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
LOGP(DSNDCP, LOGL_INFO,
"New header compression entity (%d) created.\n",
comp_entity->entity);
} else {
LOGP(DSNDCP, LOGL_INFO,
"New data compression entity (%d) created.\n",
comp_entity->entity);
}
return comp_entity;
}
/* Allocate a compression enitiy list */
struct llist_head *gprs_sndcp_comp_alloc(const void *ctx)
{
struct llist_head *lh;
lh = talloc_zero(ctx, struct llist_head);
INIT_LLIST_HEAD(lh);
return lh;
}
/* Free a compression entitiy list */
void gprs_sndcp_comp_free(struct llist_head *comp_entities)
{
struct gprs_sndcp_comp *comp_entity;
/* We expect the caller to take care of allocating a
* compression entity list properly. Attempting to
* free a non existing list clearly points out
* a malfunction. */
OSMO_ASSERT(comp_entities);
llist_for_each_entry(comp_entity, comp_entities, list) {
/* Free compression entity */
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
LOGP(DSNDCP, LOGL_INFO,
"Deleting header compression entity %d ...\n",
comp_entity->entity);
gprs_sndcp_pcomp_term(comp_entity);
} else {
LOGP(DSNDCP, LOGL_INFO,
"Deleting data compression entity %d ...\n",
comp_entity->entity);
gprs_sndcp_dcomp_term(comp_entity);
}
}
talloc_free(comp_entities);
}
/* Delete a compression entity */
void gprs_sndcp_comp_delete(struct llist_head *comp_entities,
unsigned int entity)
{
struct gprs_sndcp_comp *comp_entity;
struct gprs_sndcp_comp *comp_entity_to_delete = NULL;
OSMO_ASSERT(comp_entities);
llist_for_each_entry(comp_entity, comp_entities, list) {
if (comp_entity->entity == entity) {
comp_entity_to_delete = comp_entity;
break;
}
}
if (!comp_entity_to_delete)
return;
if (comp_entity_to_delete->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) {
LOGP(DSNDCP, LOGL_INFO,
"Deleting header compression entity %d ...\n",
comp_entity_to_delete->entity);
gprs_sndcp_pcomp_term(comp_entity_to_delete);
} else {
LOGP(DSNDCP, LOGL_INFO,
"Deleting data compression entity %d ...\n",
comp_entity_to_delete->entity);
}
/* Delete compression entity */
llist_del(&comp_entity_to_delete->list);
talloc_free(comp_entity_to_delete);
}
/* Create and Add a new compression entity
* (returns a pointer to the compression entity that has just been created) */
struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx,
struct llist_head *comp_entities,
const struct gprs_sndcp_comp_field
*comp_field)
{
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(comp_entities);
OSMO_ASSERT(comp_field);
/* Just to be sure, if the entity is already in
* the list it will be deleted now */
gprs_sndcp_comp_delete(comp_entities, comp_field->entity);
/* Create and add a new entity to the list */
comp_entity = gprs_sndcp_comp_create(ctx, comp_field);
if (!comp_entity)
return NULL;
llist_add(&comp_entity->list, comp_entities);
return comp_entity;
}
/* Find which compression entity handles the specified pcomp/dcomp */
struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head
*comp_entities, uint8_t comp)
{
struct gprs_sndcp_comp *comp_entity;
int i;
OSMO_ASSERT(comp_entities);
llist_for_each_entry(comp_entity, comp_entities, list) {
for (i = 0; i < comp_entity->comp_len; i++) {
if (comp_entity->comp[i] == comp)
return comp_entity;
}
}
LOGP(DSNDCP, LOGL_ERROR,
"Could not find a matching compression entity for given pcomp/dcomp value %d.\n",
comp);
return NULL;
}
/* Find which compression entity handles the specified nsapi */
struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head
*comp_entities, uint8_t nsapi)
{
struct gprs_sndcp_comp *comp_entity;
int i;
OSMO_ASSERT(comp_entities);
llist_for_each_entry(comp_entity, comp_entities, list) {
for (i = 0; i < comp_entity->nsapi_len; i++) {
if (comp_entity->nsapi[i] == nsapi)
return comp_entity;
}
}
return NULL;
}
/* Find a comp_index for a given pcomp/dcomp value */
uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity,
uint8_t comp)
{
/* Note: This function returns a normalized version of the comp value,
* which matches up with the position of the comp field. Since comp=0
* is reserved for "no compression", the index value starts counting
* at one. The return value is the PCOMPn/DCOMPn value one can find
* in the Specification (see e.g. 3GPP TS 44.065, 6.5.3.2, Table 7) */
int i;
OSMO_ASSERT(comp_entity);
/* A pcomp/dcomp value of zero is reserved for "no comproession",
* So we just bail and return zero in this case */
if (comp == 0)
return 0;
/* Look in the pcomp/dcomp list for the index */
for (i = 0; i < comp_entity->comp_len; i++) {
if (comp_entity->comp[i] == comp)
return i + 1;
}
LOGP(DSNDCP, LOGL_ERROR,
"Could not find a matching comp_index for given pcomp/dcomp value %d\n",
comp);
return 0;
}
/* Find a pcomp/dcomp value for a given comp_index */
uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity,
uint8_t comp_index)
{
OSMO_ASSERT(comp_entity);
/* A comp_index of zero translates to zero right away. */
if (comp_index == 0)
return 0;
if (comp_index > comp_entity->comp_len) {
LOGP(DSNDCP, LOGL_ERROR,
"Could not find a matching pcomp/dcomp value for given comp_index value %d.\n",
comp_index);
return 0;
}
/* Look in the pcomp/dcomp list for the comp_index, see
* note in gprs_sndcp_comp_get_idx() */
return comp_entity->comp[comp_index - 1];
}

View File

@ -1,358 +0,0 @@
/* GPRS SNDCP data compression handler */
/* (C) 2016 by Sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <errno.h>
#include <stdbool.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/tlv.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_sndcp_xid.h>
#include <openbsc/v42bis.h>
#include <openbsc/v42bis_private.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_sndcp_comp.h>
#include <openbsc/gprs_sndcp_dcomp.h>
/* A struct to capture the output data of compressor and decompressor */
struct v42bis_output_buffer {
uint8_t *buf;
uint8_t *buf_pointer;
int len;
};
/* Handler to capture the output data from the compressor */
void tx_v42bis_frame_handler(void *user_data, const uint8_t *pkt, int len)
{
struct v42bis_output_buffer *output_buffer =
(struct v42bis_output_buffer *)user_data;
memcpy(output_buffer->buf_pointer, pkt, len);
output_buffer->buf_pointer += len;
output_buffer->len += len;
return;
}
/* Handler to capture the output data from the decompressor */
void rx_v42bis_data_handler(void *user_data, const uint8_t *buf, int len)
{
struct v42bis_output_buffer *output_buffer =
(struct v42bis_output_buffer *)user_data;
memcpy(output_buffer->buf_pointer, buf, len);
output_buffer->buf_pointer += len;
output_buffer->len += len;
return;
}
/* Initalize data compression */
int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
const struct gprs_sndcp_comp_field *comp_field)
{
/* Note: This function is automatically called from
* gprs_sndcp_comp.c when a new data compression
* entity is created by gprs_sndcp.c */
OSMO_ASSERT(comp_entity);
OSMO_ASSERT(comp_field);
if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION
&& comp_entity->algo == V42BIS) {
OSMO_ASSERT(comp_field->v42bis_params);
comp_entity->state =
v42bis_init(ctx, NULL, comp_field->v42bis_params->p0,
comp_field->v42bis_params->p1,
comp_field->v42bis_params->p2,
&tx_v42bis_frame_handler, NULL,
V42BIS_MAX_OUTPUT_LENGTH,
&rx_v42bis_data_handler, NULL,
V42BIS_MAX_OUTPUT_LENGTH);
LOGP(DSNDCP, LOGL_INFO,
"V.42bis data compression initalized.\n");
return 0;
}
/* Just in case someone tries to initalize an unknown or unsupported
* data compresson. Since everything is checked during the SNDCP
* negotiation process, this should never happen! */
OSMO_ASSERT(false);
}
/* Terminate data compression */
void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity)
{
/* Note: This function is automatically called from
* gprs_sndcp_comp.c when a data compression
* entity is deleted by gprs_sndcp.c */
OSMO_ASSERT(comp_entity);
if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION
&& comp_entity->algo == V42BIS) {
if (comp_entity->state) {
v42bis_free((v42bis_state_t *) comp_entity->state);
comp_entity->state = NULL;
}
LOGP(DSNDCP, LOGL_INFO,
"V.42bis data compression terminated.\n");
return;
}
/* Just in case someone tries to terminate an unknown or unsupported
* data compresson. Since everything is checked during the SNDCP
* negotiation process, this should never happen! */
OSMO_ASSERT(false);
}
/* Perform a full reset of the V.42bis compression state */
static void v42bis_reset(v42bis_state_t *comp)
{
/* This function performs a complete reset of the V.42bis compression
* state by reinitalizing the state withe the previously negotiated
* parameters. */
int p0, p1, p2;
p0 = comp->decompress.v42bis_parm_p0 | comp->compress.v42bis_parm_p0;
p1 = comp->decompress.v42bis_parm_n2;
p2 = comp->decompress.v42bis_parm_n7;
DEBUGP(DSNDCP, "Resetting compression state: %p, p0=%d, p1=%d, p2=%d\n",
comp, p0, p1, p2);
v42bis_init(NULL, comp, p0, p1, p2, &tx_v42bis_frame_handler, NULL,
V42BIS_MAX_OUTPUT_LENGTH, &rx_v42bis_data_handler, NULL,
V42BIS_MAX_OUTPUT_LENGTH);
}
/* Compress a packet using V.42bis data compression */
static int v42bis_compress_unitdata(uint8_t *pcomp_index, uint8_t *data,
unsigned int len, v42bis_state_t *comp)
{
/* Note: This implementation may only be used to compress SN_UNITDATA
* packets, since it resets the compression state for each NPDU. */
uint8_t *data_o;
int rc;
int skip = 0;
struct v42bis_output_buffer compressed_data;
/* Don't bother with short packets */
if (len < MIN_COMPR_PAYLOAD)
skip = 1;
/* Skip if compression is not enabled for TX direction */
if (!comp->compress.v42bis_parm_p0)
skip = 1;
/* Skip compression */
if (skip) {
*pcomp_index = 0;
return len;
}
/* Reset V.42bis compression state */
v42bis_reset(comp);
/* Run compressor */
data_o = talloc_zero_size(comp, len * MAX_DATADECOMPR_FAC);
compressed_data.buf = data_o;
compressed_data.buf_pointer = data_o;
compressed_data.len = 0;
comp->compress.user_data = (&compressed_data);
rc = v42bis_compress(comp, data, len);
if (rc < 0) {
LOGP(DSNDCP, LOGL_ERROR,
"Data compression failed, skipping...\n");
skip = 1;
}
rc = v42bis_compress_flush(comp);
if (rc < 0) {
LOGP(DSNDCP, LOGL_ERROR,
"Data compression failed, skipping...\n");
skip = 1;
}
/* The compressor might yield negative compression gain, in
* this case, we just decide to send the packat as normal,
* uncompressed payload => skip compresssion */
if (compressed_data.len >= len) {
LOGP(DSNDCP, LOGL_ERROR,
"Data compression ineffective, skipping...\n");
skip = 1;
}
/* Skip compression */
if (skip) {
*pcomp_index = 0;
talloc_free(data_o);
return len;
}
*pcomp_index = 1;
memcpy(data, data_o, compressed_data.len);
talloc_free(data_o);
return compressed_data.len;
}
/* Expand a packet using V.42bis data compression */
static int v42bis_expand_unitdata(uint8_t *data, unsigned int len,
uint8_t pcomp_index, v42bis_state_t *comp)
{
/* Note: This implementation may only be used to compress SN_UNITDATA
* packets, since it resets the compression state for each NPDU. */
int rc;
struct v42bis_output_buffer uncompressed_data;
uint8_t *data_i;
/* Skip when the packet is marked as uncompressed */
if (pcomp_index == 0) {
return len;
}
/* Reset V.42bis compression state */
v42bis_reset(comp);
/* Decompress packet */
data_i = talloc_zero_size(comp, len);
memcpy(data_i, data, len);
uncompressed_data.buf = data;
uncompressed_data.buf_pointer = data;
uncompressed_data.len = 0;
comp->decompress.user_data = (&uncompressed_data);
rc = v42bis_decompress(comp, data_i, len);
talloc_free(data_i);
if (rc < 0)
return -EINVAL;
rc = v42bis_decompress_flush(comp);
if (rc < 0)
return -EINVAL;
return uncompressed_data.len;
}
/* Expand packet */
int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
const struct llist_head *comp_entities)
{
int rc;
uint8_t pcomp_index = 0;
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(data);
OSMO_ASSERT(comp_entities);
LOGP(DSNDCP, LOGL_DEBUG,
"Data compression entity list: comp_entities=%p\n", comp_entities);
LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", pcomp);
/* Skip on pcomp=0 */
if (pcomp == 0) {
return len;
}
/* Find out which compression entity handles the data */
comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp);
/* Skip compression if no suitable compression entity can be found */
if (!comp_entity) {
return len;
}
/* Note: Only data compression entities may appear in
* data compression context */
OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
/* Note: Currently V42BIS is the only compression method we
* support, so the only allowed algorithm is V42BIS */
OSMO_ASSERT(comp_entity->algo == V42BIS);
/* Find pcomp_index */
pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
/* Run decompression algo */
rc = v42bis_expand_unitdata(data, len, pcomp_index, comp_entity->state);
LOGP(DSNDCP, LOGL_DEBUG,
"Data expansion done, old length=%d, new length=%d, entity=%p\n",
len, rc, comp_entity);
return rc;
}
/* Compress packet */
int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
const struct llist_head *comp_entities,
uint8_t nsapi)
{
int rc;
uint8_t pcomp_index = 0;
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(data);
OSMO_ASSERT(pcomp);
OSMO_ASSERT(comp_entities);
LOGP(DSNDCP, LOGL_DEBUG,
"Data compression entity list: comp_entities=%p\n", comp_entities);
/* Find out which compression entity handles the data */
comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi);
/* Skip compression if no suitable compression entity can be found */
if (!comp_entity) {
*pcomp = 0;
return len;
}
/* Note: Only data compression entities may appear in
* data compression context */
OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION);
/* Note: Currently V42BIS is the only compression method we
* support, so the only allowed algorithm is V42BIS */
OSMO_ASSERT(comp_entity->algo == V42BIS);
/* Run compression algo */
rc = v42bis_compress_unitdata(&pcomp_index, data, len,
comp_entity->state);
/* Find pcomp value */
*pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", *pcomp);
LOGP(DSNDCP, LOGL_DEBUG,
"Data compression done, old length=%d, new length=%d, entity=%p\n",
len, rc, comp_entity);
return rc;
}

View File

@ -1,282 +0,0 @@
/* GPRS SNDCP header compression handler */
/* (C) 2016 by Sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* Author: Philipp Maier
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <errno.h>
#include <stdbool.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/tlv.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_sndcp_xid.h>
#include <openbsc/slhc.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_sndcp_comp.h>
#include <openbsc/gprs_sndcp_pcomp.h>
/* Initalize header compression */
int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity,
const struct gprs_sndcp_comp_field *comp_field)
{
/* Note: This function is automatically called from
* gprs_sndcp_comp.c when a new header compression
* entity is created by gprs_sndcp.c */
OSMO_ASSERT(comp_entity);
OSMO_ASSERT(comp_field);
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION
&& comp_entity->algo == RFC_1144) {
OSMO_ASSERT(comp_field->rfc1144_params);
comp_entity->state =
slhc_init(ctx, comp_field->rfc1144_params->s01 + 1,
comp_field->rfc1144_params->s01 + 1);
LOGP(DSNDCP, LOGL_INFO,
"RFC1144 header compression initalized.\n");
return 0;
}
/* Just in case someone tries to initalize an unknown or unsupported
* header compresson. Since everything is checked during the SNDCP
* negotiation process, this should never happen! */
OSMO_ASSERT(false);
}
/* Terminate header compression */
void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity)
{
/* Note: This function is automatically called from
* gprs_sndcp_comp.c when a header compression
* entity is deleted by gprs_sndcp.c */
OSMO_ASSERT(comp_entity);
if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION
&& comp_entity->algo == RFC_1144) {
if (comp_entity->state) {
slhc_free((struct slcompress *)comp_entity->state);
comp_entity->state = NULL;
}
LOGP(DSNDCP, LOGL_INFO,
"RFC1144 header compression terminated.\n");
return;
}
/* Just in case someone tries to terminate an unknown or unsupported
* data compresson. Since everything is checked during the SNDCP
* negotiation process, this should never happen! */
OSMO_ASSERT(false);
}
/* Compress a packet using Van Jacobson RFC1144 header compression */
static int rfc1144_compress(uint8_t *pcomp_index, uint8_t *data,
unsigned int len, struct slcompress *comp)
{
uint8_t *comp_ptr;
int compr_len;
uint8_t *data_o;
/* Create a working copy of the incoming data */
data_o = talloc_zero_size(comp, len);
memcpy(data_o, data, len);
/* Run compressor */
compr_len = slhc_compress(comp, data, len, data_o, &comp_ptr, 0);
/* Generate pcomp_index */
if (data_o[0] & SL_TYPE_COMPRESSED_TCP) {
*pcomp_index = 2;
data_o[0] &= ~SL_TYPE_COMPRESSED_TCP;
memcpy(data, data_o, compr_len);
} else if ((data_o[0] & SL_TYPE_UNCOMPRESSED_TCP) ==
SL_TYPE_UNCOMPRESSED_TCP) {
*pcomp_index = 1;
data_o[0] &= 0x4F;
memcpy(data, data_o, compr_len);
} else
*pcomp_index = 0;
talloc_free(data_o);
return compr_len;
}
/* Expand a packet using Van Jacobson RFC1144 header compression */
static int rfc1144_expand(uint8_t *data, unsigned int len, uint8_t pcomp_index,
struct slcompress *comp)
{
int data_decompressed_len;
int type;
/* Note: this function should never be called with pcomp_index=0,
* since this condition is already filtered
* out by gprs_sndcp_pcomp_expand() */
/* Determine the data type by the PCOMP index */
switch (pcomp_index) {
case 0:
type = SL_TYPE_IP;
break;
case 1:
type = SL_TYPE_UNCOMPRESSED_TCP;
break;
case 2:
type = SL_TYPE_COMPRESSED_TCP;
break;
default:
LOGP(DSNDCP, LOGL_ERROR,
"rfc1144_expand() Invalid pcomp_index value (%d) detected, assuming no compression!\n",
pcomp_index);
type = SL_TYPE_IP;
break;
}
/* Restore the original version nibble on
* marked uncompressed packets */
if (type == SL_TYPE_UNCOMPRESSED_TCP) {
/* Just in case the phone tags uncompressed tcp-data
* (normally this is handled by pcomp so there is
* no need for tagging the data) */
data[0] &= 0x4F;
data_decompressed_len = slhc_remember(comp, data, len);
return data_decompressed_len;
}
/* Uncompress compressed packets */
else if (type == SL_TYPE_COMPRESSED_TCP) {
data_decompressed_len = slhc_uncompress(comp, data, len);
return data_decompressed_len;
}
/* Regular or unknown packets will not be touched */
return len;
}
/* Expand packet header */
int gprs_sndcp_pcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp,
const struct llist_head *comp_entities)
{
int rc;
uint8_t pcomp_index = 0;
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(data);
OSMO_ASSERT(comp_entities);
LOGP(DSNDCP, LOGL_DEBUG,
"Header compression entity list: comp_entities=%p\n",
comp_entities);
LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", pcomp);
/* Skip on pcomp=0 */
if (pcomp == 0) {
return len;
}
/* Find out which compression entity handles the data */
comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp);
/* Skip compression if no suitable compression entity can be found */
if (!comp_entity) {
return len;
}
/* Note: Only protocol compression entities may appear in
* protocol compression context */
OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
/* Note: Currently RFC1144 is the only compression method we
* support, so the only allowed algorithm is RFC1144 */
OSMO_ASSERT(comp_entity->algo == RFC_1144);
/* Find pcomp_index */
pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp);
/* Run decompression algo */
rc = rfc1144_expand(data, len, pcomp_index, comp_entity->state);
slhc_i_status(comp_entity->state);
slhc_o_status(comp_entity->state);
LOGP(DSNDCP, LOGL_DEBUG,
"Header expansion done, old length=%d, new length=%d, entity=%p\n",
len, rc, comp_entity);
return rc;
}
/* Compress packet header */
int gprs_sndcp_pcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp,
const struct llist_head *comp_entities,
uint8_t nsapi)
{
int rc;
uint8_t pcomp_index = 0;
struct gprs_sndcp_comp *comp_entity;
OSMO_ASSERT(data);
OSMO_ASSERT(pcomp);
OSMO_ASSERT(comp_entities);
LOGP(DSNDCP, LOGL_DEBUG,
"Header compression entity list: comp_entities=%p\n",
comp_entities);
/* Find out which compression entity handles the data */
comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi);
/* Skip compression if no suitable compression entity can be found */
if (!comp_entity) {
*pcomp = 0;
return len;
}
/* Note: Only protocol compression entities may appear in
* protocol compression context */
OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION);
/* Note: Currently RFC1144 is the only compression method we
* support, so the only allowed algorithm is RFC1144 */
OSMO_ASSERT(comp_entity->algo == RFC_1144);
/* Run compression algo */
rc = rfc1144_compress(&pcomp_index, data, len, comp_entity->state);
slhc_i_status(comp_entity->state);
slhc_o_status(comp_entity->state);
/* Find pcomp value */
*pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index);
LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", *pcomp);
LOGP(DSNDCP, LOGL_DEBUG,
"Header compression done, old length=%d, new length=%d, entity=%p\n",
len, rc, comp_entity);
return rc;
}

View File

@ -1,71 +0,0 @@
/* VTY interface for our GPRS SNDCP implementation */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <openbsc/gsm_data.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <openbsc/debug.h>
#include <openbsc/signal.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/gprs_sndcp.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
static void vty_dump_sne(struct vty *vty, struct gprs_sndcp_entity *sne)
{
vty_out(vty, " TLLI %08x SAPI=%u NSAPI=%u:%s",
sne->lle->llme->tlli, sne->lle->sapi, sne->nsapi, VTY_NEWLINE);
vty_out(vty, " Defrag: npdu=%u highest_seg=%u seg_have=0x%08x tot_len=%u%s",
sne->defrag.npdu, sne->defrag.highest_seg, sne->defrag.seg_have,
sne->defrag.tot_len, VTY_NEWLINE);
}
DEFUN(show_sndcp, show_sndcp_cmd,
"show sndcp",
SHOW_STR "Display information about the SNDCP protocol")
{
struct gprs_sndcp_entity *sne;
vty_out(vty, "State of SNDCP Entities%s", VTY_NEWLINE);
llist_for_each_entry(sne, &gprs_sndcp_entities, list)
vty_dump_sne(vty, sne);
return CMD_SUCCESS;
}
int gprs_sndcp_vty_init(void)
{
install_element_ve(&show_sndcp_cmd);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,937 +0,0 @@
/* MS subscriber data handling */
/* (C) 2014 by sysmocom s.f.m.c. GmbH
* (C) 2015 by Holger Hans Peter Freyther
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gsm/apn.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <openbsc/gprs_subscriber.h>
#include <openbsc/gsup_client.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/debug.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <limits.h>
#define SGSN_SUBSCR_MAX_RETRIES 3
#define SGSN_SUBSCR_RETRY_INTERVAL 10
#define LOGGSUPP(level, gsup, fmt, args...) \
LOGP(DGPRS, level, "GSUP(%s) " fmt, \
(gsup)->imsi, \
## args)
extern void *tall_bsc_ctx;
LLIST_HEAD(_gprs_subscribers);
struct llist_head * const gprs_subscribers = &_gprs_subscribers;
static int gsup_read_cb(struct gsup_client *gsupc, struct msgb *msg);
/* TODO: Some functions are specific to the SGSN, but this file is more general
* (it has gprs_* name). Either move these functions elsewhere, split them and
* move a part, or replace the gprs_ prefix by sgsn_. The applies to
* gprs_subscr_init, gsup_read_cb, and gprs_subscr_tx_gsup_message.
*/
int gprs_subscr_init(struct sgsn_instance *sgi)
{
const char *addr_str;
if (!sgi->cfg.gsup_server_addr.sin_addr.s_addr)
return 0;
addr_str = inet_ntoa(sgi->cfg.gsup_server_addr.sin_addr);
sgi->gsup_client = gsup_client_create(
"SGSN",
addr_str, sgi->cfg.gsup_server_port,
&gsup_read_cb,
&sgi->cfg.oap);
if (!sgi->gsup_client)
return -1;
return 1;
}
static int gsup_read_cb(struct gsup_client *gsupc, struct msgb *msg)
{
int rc;
rc = gprs_subscr_rx_gsup_message(msg);
msgb_free(msg);
if (rc < 0)
return -1;
return rc;
}
int gprs_subscr_purge(struct gprs_subscr *subscr);
static struct sgsn_subscriber_data *sgsn_subscriber_data_alloc(void *ctx)
{
struct sgsn_subscriber_data *sdata;
int idx;
sdata = talloc_zero(ctx, struct sgsn_subscriber_data);
sdata->error_cause = SGSN_ERROR_CAUSE_NONE;
for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++)
sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL;
INIT_LLIST_HEAD(&sdata->pdp_list);
return sdata;
}
struct sgsn_subscriber_pdp_data* sgsn_subscriber_pdp_data_alloc(
struct sgsn_subscriber_data *sdata)
{
struct sgsn_subscriber_pdp_data* pdata;
pdata = talloc_zero(sdata, struct sgsn_subscriber_pdp_data);
llist_add_tail(&pdata->list, &sdata->pdp_list);
return pdata;
}
struct gprs_subscr *gprs_subscr_get_by_imsi(const char *imsi)
{
struct gprs_subscr *gsub;
if (!imsi || !*imsi)
return NULL;
llist_for_each_entry(gsub, gprs_subscribers, entry) {
if (!strcmp(gsub->imsi, imsi))
return gprs_subscr_get(gsub);
}
return NULL;
}
static struct gprs_subscr *gprs_subscr_alloc(void)
{
struct gprs_subscr *gsub;
gsub = talloc_zero(tall_bsc_ctx, struct gprs_subscr);
if (!gsub)
return NULL;
llist_add_tail(&gsub->entry, gprs_subscribers);
gsub->use_count = 1;
gsub->tmsi = GSM_RESERVED_TMSI;
return gsub;
}
struct gprs_subscr *gprs_subscr_get_or_create(const char *imsi)
{
struct gprs_subscr *gsub;
gsub = gprs_subscr_get_by_imsi(imsi);
if (!gsub) {
gsub = gprs_subscr_alloc();
if (!gsub)
return NULL;
osmo_strlcpy(gsub->imsi, imsi, sizeof(gsub->imsi));
}
if (!gsub->sgsn_data)
gsub->sgsn_data = sgsn_subscriber_data_alloc(gsub);
return gsub;
}
void gprs_subscr_cleanup(struct gprs_subscr *subscr)
{
if (subscr->sgsn_data->mm) {
gprs_subscr_put(subscr->sgsn_data->mm->subscr);
subscr->sgsn_data->mm->subscr = NULL;
subscr->sgsn_data->mm = NULL;
}
if (subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE) {
gprs_subscr_purge(subscr);
subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE;
}
}
void gprs_subscr_cancel(struct gprs_subscr *subscr)
{
subscr->authorized = 0;
subscr->flags |= GPRS_SUBSCRIBER_CANCELLED;
subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE;
gprs_subscr_update(subscr);
gprs_subscr_cleanup(subscr);
}
static int gprs_subscr_tx_gsup_message(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
struct msgb *msg = gsup_client_msgb_alloc();
if (strlen(gsup_msg->imsi) == 0 && subscr)
osmo_strlcpy(gsup_msg->imsi, subscr->imsi,
sizeof(gsup_msg->imsi));
gsup_msg->cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
osmo_gsup_encode(msg, gsup_msg);
LOGGSUBSCRP(LOGL_INFO, subscr,
"Sending GSUP, will send: %s\n", msgb_hexdump(msg));
if (!sgsn->gsup_client) {
msgb_free(msg);
return -ENOTSUP;
}
return gsup_client_send(sgsn->gsup_client, msg);
}
static int gprs_subscr_tx_gsup_error_reply(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_orig,
enum gsm48_gmm_cause cause)
{
struct osmo_gsup_message gsup_reply = {0};
osmo_strlcpy(gsup_reply.imsi, gsup_orig->imsi,
sizeof(gsup_reply.imsi));
gsup_reply.cause = cause;
gsup_reply.message_type =
OSMO_GSUP_TO_MSGT_ERROR(gsup_orig->message_type);
return gprs_subscr_tx_gsup_message(subscr, &gsup_reply);
}
static int gprs_subscr_handle_gsup_auth_res(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
unsigned idx;
struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
LOGGSUBSCRP(LOGL_INFO, subscr,
"Got SendAuthenticationInfoResult, num_auth_vectors = %zu\n",
gsup_msg->num_auth_vectors);
if (gsup_msg->num_auth_vectors > 0) {
memset(sdata->auth_triplets, 0, sizeof(sdata->auth_triplets));
for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++)
sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL;
}
for (idx = 0; idx < gsup_msg->num_auth_vectors; idx++) {
size_t key_seq = idx;
LOGGSUBSCRP(LOGL_DEBUG, subscr,
"Adding auth tuple, cksn = %zu\n", key_seq);
if (key_seq >= ARRAY_SIZE(sdata->auth_triplets)) {
LOGGSUBSCRP(LOGL_NOTICE, subscr,
"Skipping auth triplet with invalid cksn %zu\n",
key_seq);
continue;
}
sdata->auth_triplets[key_seq].vec = gsup_msg->auth_vectors[idx];
sdata->auth_triplets[key_seq].key_seq = key_seq;
}
sdata->auth_triplets_updated = 1;
sdata->error_cause = SGSN_ERROR_CAUSE_NONE;
gprs_subscr_update_auth_info(subscr);
return 0;
}
static int gprs_subscr_pdp_data_clear(struct gprs_subscr *subscr)
{
struct sgsn_subscriber_pdp_data *pdp, *pdp2;
int count = 0;
llist_for_each_entry_safe(pdp, pdp2, &subscr->sgsn_data->pdp_list, list) {
llist_del(&pdp->list);
talloc_free(pdp);
count += 1;
}
return count;
}
static struct sgsn_subscriber_pdp_data *gprs_subscr_pdp_data_get_by_id(
struct gprs_subscr *subscr, unsigned context_id)
{
struct sgsn_subscriber_pdp_data *pdp;
llist_for_each_entry(pdp, &subscr->sgsn_data->pdp_list, list) {
if (pdp->context_id == context_id)
return pdp;
}
return NULL;
}
static void gprs_subscr_gsup_insert_data(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
unsigned idx;
int rc;
if (gsup_msg->msisdn_enc) {
if (gsup_msg->msisdn_enc_len > sizeof(sdata->msisdn)) {
LOGP(DGPRS, LOGL_ERROR, "MSISDN too long (%zu)\n",
gsup_msg->msisdn_enc_len);
sdata->msisdn_len = 0;
} else {
memcpy(sdata->msisdn, gsup_msg->msisdn_enc,
gsup_msg->msisdn_enc_len);
sdata->msisdn_len = gsup_msg->msisdn_enc_len;
}
}
if (gsup_msg->hlr_enc) {
if (gsup_msg->hlr_enc_len > sizeof(sdata->hlr)) {
LOGP(DGPRS, LOGL_ERROR, "HLR-Number too long (%zu)\n",
gsup_msg->hlr_enc_len);
sdata->hlr_len = 0;
} else {
memcpy(sdata->hlr, gsup_msg->hlr_enc,
gsup_msg->hlr_enc_len);
sdata->hlr_len = gsup_msg->hlr_enc_len;
}
}
if (gsup_msg->pdp_charg_enc && gsup_msg->pdp_charg_enc_len >= sizeof(sdata->pdp_charg)) {
memcpy(&sdata->pdp_charg, gsup_msg->pdp_charg_enc, sizeof(sdata->pdp_charg));
sdata->has_pdp_charg = 1;
} else {
sdata->has_pdp_charg = 0;
}
if (gsup_msg->pdp_info_compl) {
rc = gprs_subscr_pdp_data_clear(subscr);
if (rc > 0)
LOGP(DGPRS, LOGL_INFO, "Cleared existing PDP info\n");
}
for (idx = 0; idx < gsup_msg->num_pdp_infos; idx++) {
struct osmo_gsup_pdp_info *pdp_info = &gsup_msg->pdp_infos[idx];
size_t ctx_id = pdp_info->context_id;
struct sgsn_subscriber_pdp_data *pdp_data;
if (pdp_info->apn_enc_len >= sizeof(pdp_data->apn_str)-1) {
LOGGSUBSCRP(LOGL_ERROR, subscr,
"APN too long, context id = %zu, APN = %s\n",
ctx_id, osmo_hexdump(pdp_info->apn_enc,
pdp_info->apn_enc_len));
continue;
}
if (pdp_info->qos_enc_len > sizeof(pdp_data->qos_subscribed)) {
LOGGSUBSCRP(LOGL_ERROR, subscr,
"QoS info too long (%zu)\n",
pdp_info->qos_enc_len);
continue;
}
LOGGSUBSCRP(LOGL_INFO, subscr,
"Will set PDP info, context id = %zu, APN = %s\n",
ctx_id, osmo_hexdump(pdp_info->apn_enc, pdp_info->apn_enc_len));
/* Set PDP info [ctx_id] */
pdp_data = gprs_subscr_pdp_data_get_by_id(subscr, ctx_id);
if (!pdp_data) {
pdp_data = sgsn_subscriber_pdp_data_alloc(subscr->sgsn_data);
pdp_data->context_id = ctx_id;
}
OSMO_ASSERT(pdp_data != NULL);
pdp_data->pdp_type = pdp_info->pdp_type;
osmo_apn_to_str(pdp_data->apn_str,
pdp_info->apn_enc, pdp_info->apn_enc_len);
memcpy(pdp_data->qos_subscribed, pdp_info->qos_enc, pdp_info->qos_enc_len);
pdp_data->qos_subscribed_len = pdp_info->qos_enc_len;
if (pdp_info->pdp_charg_enc && pdp_info->pdp_charg_enc_len >= sizeof(pdp_data->pdp_charg)) {
memcpy(&pdp_data->pdp_charg, pdp_info->pdp_charg_enc, sizeof(pdp_data->pdp_charg));
pdp_data->has_pdp_charg = 1;
} else {
pdp_data->has_pdp_charg = 0;
}
}
}
static int gprs_subscr_handle_gsup_upd_loc_res(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
/* contrary to MAP, we allow piggy-backing subscriber data onto
* the UPDATE LOCATION RESULT, and don't mandate the use of a
* separate nested INSERT SUBSCRIBER DATA transaction */
gprs_subscr_gsup_insert_data(subscr, gsup_msg);
subscr->authorized = 1;
subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
subscr->flags |= GPRS_SUBSCRIBER_ENABLE_PURGE;
gprs_subscr_update(subscr);
return 0;
}
static int gprs_subscr_handle_gsup_dsd_req(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
struct osmo_gsup_message gsup_reply = {0};
if (gsup_msg->cn_domain != OSMO_GSUP_CN_DOMAIN_PS) {
LOGGSUBSCRP(LOGL_ERROR, subscr,
"Rx GSUP message %s not supported for CS\n",
osmo_gsup_message_type_name(gsup_msg->message_type));
gsup_reply.cause = GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL;
gsup_reply.message_type = OSMO_GSUP_MSGT_DELETE_DATA_ERROR;
} else {
gsm0408_gprs_access_cancelled(subscr->sgsn_data->mm,
GMM_CAUSE_GPRS_NOTALLOWED);
gsup_reply.message_type = OSMO_GSUP_MSGT_DELETE_DATA_RESULT;
}
return gprs_subscr_tx_gsup_message(subscr, &gsup_reply);
}
static int gprs_subscr_handle_gsup_isd_req(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
struct osmo_gsup_message gsup_reply = {0};
gprs_subscr_gsup_insert_data(subscr, gsup_msg);
subscr->authorized = 1;
subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
subscr->flags |= GPRS_SUBSCRIBER_ENABLE_PURGE;
gprs_subscr_update(subscr);
gsup_reply.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT;
return gprs_subscr_tx_gsup_message(subscr, &gsup_reply);
}
static int check_cause(int cause)
{
switch (cause) {
case GMM_CAUSE_IMSI_UNKNOWN ... GMM_CAUSE_ILLEGAL_ME:
case GMM_CAUSE_GPRS_NOTALLOWED ... GMM_CAUSE_NO_GPRS_PLMN:
return EACCES;
case GMM_CAUSE_MSC_TEMP_NOTREACH ... GMM_CAUSE_CONGESTION:
return EHOSTUNREACH;
case GMM_CAUSE_SEM_INCORR_MSG ... GMM_CAUSE_PROTO_ERR_UNSPEC:
default:
return EINVAL;
}
}
static int gprs_subscr_handle_gsup_auth_err(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
unsigned idx;
struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
int cause_err;
cause_err = check_cause(gsup_msg->cause);
LOGGSUBSCRP(LOGL_DEBUG, subscr,
"Send authentication info has failed with cause %d, "
"handled as: %s\n",
gsup_msg->cause, strerror(cause_err));
switch (cause_err) {
case EACCES:
LOGGSUBSCRP(LOGL_NOTICE, subscr,
"GPRS send auth info req failed, access denied, "
"GMM cause = '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
/* Clear auth tuples */
memset(sdata->auth_triplets, 0, sizeof(sdata->auth_triplets));
for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++)
sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL;
subscr->authorized = 0;
sdata->error_cause = gsup_msg->cause;
gprs_subscr_update_auth_info(subscr);
break;
case EHOSTUNREACH:
LOGGSUBSCRP(LOGL_NOTICE, subscr,
"GPRS send auth info req failed, GMM cause = '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
sdata->error_cause = gsup_msg->cause;
gprs_subscr_update_auth_info(subscr);
break;
default:
case EINVAL:
LOGGSUBSCRP(LOGL_ERROR, subscr,
"GSUP protocol remote error, GMM cause = '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
break;
}
return -gsup_msg->cause;
}
static int gprs_subscr_handle_gsup_upd_loc_err(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
int cause_err;
cause_err = check_cause(gsup_msg->cause);
LOGGSUBSCRP(LOGL_DEBUG, subscr,
"Update location has failed with cause %d, handled as: %s\n",
gsup_msg->cause, strerror(cause_err));
switch (cause_err) {
case EACCES:
LOGGSUBSCRP(LOGL_NOTICE, subscr,
"GPRS update location failed, access denied, "
"GMM cause = '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
subscr->authorized = 0;
subscr->sgsn_data->error_cause = gsup_msg->cause;
gprs_subscr_update_auth_info(subscr);
break;
case EHOSTUNREACH:
LOGGSUBSCRP(LOGL_NOTICE, subscr,
"GPRS update location failed, GMM cause = '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
subscr->sgsn_data->error_cause = gsup_msg->cause;
gprs_subscr_update_auth_info(subscr);
break;
default:
case EINVAL:
LOGGSUBSCRP(LOGL_ERROR, subscr,
"GSUP protocol remote error, GMM cause = '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
break;
}
return -gsup_msg->cause;
}
static int gprs_subscr_handle_gsup_purge_no_subscr(
struct osmo_gsup_message *gsup_msg)
{
if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) {
LOGGSUPP(LOGL_NOTICE, gsup_msg,
"Purge MS has failed with cause '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
return -gsup_msg->cause;
}
LOGGSUPP(LOGL_INFO, gsup_msg, "Completing purge MS\n");
return 0;
}
static int gprs_subscr_handle_gsup_purge_res(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
LOGGSUBSCRP(LOGL_INFO, subscr, "Completing purge MS\n");
/* Force silent cancellation */
subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
gprs_subscr_cancel(subscr);
return 0;
}
static int gprs_subscr_handle_gsup_purge_err(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
LOGGSUBSCRP(LOGL_NOTICE, subscr,
"Purge MS has failed with cause '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
/* In GSM 09.02, 19.1.4.4, the text and the SDL diagram imply that
* the subscriber data is not removed if the request has failed. On the
* other hand, keeping the subscriber data in either error case
* (subscriber unknown, syntactical message error, connection error)
* doesn't seem to give any advantage, since the data will be restored
* on the next Attach Request anyway.
* This approach ensures, that the subscriber record will not stick if
* an error happens.
*/
/* TODO: Check whether this behaviour is acceptable and either just
* remove this TODO-notice or change the implementation to not delete
* the subscriber data (eventually resetting the ENABLE_PURGE flag and
* restarting the expiry timer based on the cause).
*
* Subscriber Unknown: cancel subscr
* Temporary network problems: do nothing (handled by timer based retry)
* Message problems (syntax, nyi, ...): cancel subscr (retry won't help)
*/
gprs_subscr_handle_gsup_purge_res(subscr, gsup_msg);
return -gsup_msg->cause;
}
static int gprs_subscr_handle_loc_cancel_req(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
struct osmo_gsup_message gsup_reply = {0};
int is_update_procedure = !gsup_msg->cancel_type ||
gsup_msg->cancel_type == OSMO_GSUP_CANCEL_TYPE_UPDATE;
LOGGSUBSCRP(LOGL_INFO, subscr, "Cancelling MS subscriber (%s)\n",
is_update_procedure ?
"update procedure" : "subscription withdraw");
gsup_reply.message_type = OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT;
gprs_subscr_tx_gsup_message(subscr, &gsup_reply);
if (is_update_procedure)
subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
else
/* Since a withdraw cause is not specified, just abort the
* current attachment. The following re-attachment should then
* be rejected with a proper cause value.
*/
subscr->sgsn_data->error_cause = GMM_CAUSE_IMPL_DETACHED;
gprs_subscr_cancel(subscr);
return 0;
}
static int gprs_subscr_handle_unknown_imsi(struct osmo_gsup_message *gsup_msg)
{
if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg->message_type)) {
gprs_subscr_tx_gsup_error_reply(NULL, gsup_msg,
GMM_CAUSE_IMSI_UNKNOWN);
LOGP(DGPRS, LOGL_NOTICE,
"Unknown IMSI %s, discarding GSUP request "
"of type 0x%02x\n",
gsup_msg->imsi, gsup_msg->message_type);
} else if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) {
LOGP(DGPRS, LOGL_NOTICE,
"Unknown IMSI %s, discarding GSUP error "
"of type 0x%02x, cause '%s' (%d)\n",
gsup_msg->imsi, gsup_msg->message_type,
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
} else {
LOGP(DGPRS, LOGL_NOTICE,
"Unknown IMSI %s, discarding GSUP response "
"of type 0x%02x\n",
gsup_msg->imsi, gsup_msg->message_type);
}
return -GMM_CAUSE_IMSI_UNKNOWN;
}
int gprs_subscr_rx_gsup_message(struct msgb *msg)
{
uint8_t *data = msgb_l2(msg);
size_t data_len = msgb_l2len(msg);
int rc = 0;
struct osmo_gsup_message gsup_msg = {0};
struct gprs_subscr *subscr;
rc = osmo_gsup_decode(data, data_len, &gsup_msg);
if (rc < 0) {
LOGP(DGPRS, LOGL_ERROR,
"decoding GSUP message fails with error '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, -rc), -rc);
return rc;
}
if (!gsup_msg.imsi[0]) {
LOGP(DGPRS, LOGL_ERROR, "Missing IMSI in GSUP message\n");
if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg.message_type))
gprs_subscr_tx_gsup_error_reply(NULL, &gsup_msg,
GMM_CAUSE_INV_MAND_INFO);
return -GMM_CAUSE_INV_MAND_INFO;
}
if (!gsup_msg.cause && OSMO_GSUP_IS_MSGT_ERROR(gsup_msg.message_type))
gsup_msg.cause = GMM_CAUSE_NET_FAIL;
subscr = gprs_subscr_get_by_imsi(gsup_msg.imsi);
if (!subscr) {
switch (gsup_msg.message_type) {
case OSMO_GSUP_MSGT_PURGE_MS_RESULT:
case OSMO_GSUP_MSGT_PURGE_MS_ERROR:
return gprs_subscr_handle_gsup_purge_no_subscr(&gsup_msg);
default:
return gprs_subscr_handle_unknown_imsi(&gsup_msg);
}
}
LOGGSUBSCRP(LOGL_INFO, subscr,
"Received GSUP message %s\n",
osmo_gsup_message_type_name(gsup_msg.message_type));
switch (gsup_msg.message_type) {
case OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST:
rc = gprs_subscr_handle_loc_cancel_req(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT:
rc = gprs_subscr_handle_gsup_auth_res(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR:
rc = gprs_subscr_handle_gsup_auth_err(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT:
rc = gprs_subscr_handle_gsup_upd_loc_res(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR:
rc = gprs_subscr_handle_gsup_upd_loc_err(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_PURGE_MS_ERROR:
rc = gprs_subscr_handle_gsup_purge_err(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_PURGE_MS_RESULT:
rc = gprs_subscr_handle_gsup_purge_res(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST:
rc = gprs_subscr_handle_gsup_isd_req(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_DELETE_DATA_REQUEST:
rc = gprs_subscr_handle_gsup_dsd_req(subscr, &gsup_msg);
break;
default:
LOGGSUBSCRP(LOGL_ERROR, subscr,
"Rx GSUP message %s not valid at SGSN\n",
osmo_gsup_message_type_name(gsup_msg.message_type));
if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg.message_type))
gprs_subscr_tx_gsup_error_reply(
subscr, &gsup_msg, GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL);
rc = -GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL;
break;
};
gprs_subscr_put(subscr);
return rc;
}
int gprs_subscr_purge(struct gprs_subscr *subscr)
{
struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
struct osmo_gsup_message gsup_msg = {0};
LOGGSUBSCRP(LOGL_INFO, subscr, "purging MS subscriber\n");
gsup_msg.message_type = OSMO_GSUP_MSGT_PURGE_MS_REQUEST;
/* Provide the HLR number in case it is known */
gsup_msg.hlr_enc_len = sdata->hlr_len;
gsup_msg.hlr_enc = sdata->hlr;
return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
}
static int gprs_subscr_query_auth_info(struct gprs_subscr *subscr,
const uint8_t *auts,
const uint8_t *auts_rand)
{
struct osmo_gsup_message gsup_msg = {0};
/* Make sure we have a complete resync or clearly no resync. */
OSMO_ASSERT((auts != NULL) == (auts_rand != NULL));
LOGGSUBSCRP(LOGL_INFO, subscr, "requesting auth info%s\n",
auts ? " with AUTS (UMTS Resynch)" : "");
gsup_msg.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST;
gsup_msg.auts = auts;
gsup_msg.rand = auts_rand;
return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
}
int gprs_subscr_location_update(struct gprs_subscr *subscr)
{
struct osmo_gsup_message gsup_msg = {0};
LOGGSUBSCRP(LOGL_INFO, subscr,
"subscriber data is not available\n");
gsup_msg.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST;
return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
}
void gprs_subscr_update(struct gprs_subscr *subscr)
{
LOGGSUBSCRP(LOGL_DEBUG, subscr, "Updating subscriber data\n");
subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING;
subscr->flags &= ~GPRS_SUBSCRIBER_FIRST_CONTACT;
if (subscr->sgsn_data->mm)
sgsn_update_subscriber_data(subscr->sgsn_data->mm);
}
void gprs_subscr_update_auth_info(struct gprs_subscr *subscr)
{
LOGGSUBSCRP(LOGL_DEBUG, subscr,
"Updating subscriber authentication info\n");
subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING;
subscr->flags &= ~GPRS_SUBSCRIBER_FIRST_CONTACT;
if (subscr->sgsn_data->mm)
sgsn_update_subscriber_data(subscr->sgsn_data->mm);
}
struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx(struct sgsn_mm_ctx *mmctx)
{
struct gprs_subscr *subscr = NULL;
if (mmctx->subscr)
return gprs_subscr_get(mmctx->subscr);
if (mmctx->imsi[0])
subscr = gprs_subscr_get_by_imsi(mmctx->imsi);
if (!subscr) {
subscr = gprs_subscr_get_or_create(mmctx->imsi);
subscr->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT;
subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE;
}
osmo_strlcpy(subscr->imei, mmctx->imei, sizeof(subscr->imei));
if (subscr->lac != mmctx->ra.lac)
subscr->lac = mmctx->ra.lac;
subscr->sgsn_data->mm = mmctx;
mmctx->subscr = gprs_subscr_get(subscr);
return subscr;
}
int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx)
{
struct gprs_subscr *subscr = NULL;
int rc;
LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber data update\n");
subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
subscr->flags |= GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING;
rc = gprs_subscr_location_update(subscr);
gprs_subscr_put(subscr);
return rc;
}
/*! \brief Send Update Auth Info request via GSUP, with or without resync.
* \param[in] mmctx MM context to request authentication tuples for.
* \param[in] auts 14 octet AUTS token for UMTS resync, or NULL.
* \param[in] auts_rand 16 octet Random token for UMTS resync, or NULL.
* In case of normal Authentication Info request, both \a auts and \a auts_rand
* must be NULL. For resync, both must be non-NULL.
*/
int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx,
const uint8_t *auts,
const uint8_t *auts_rand)
{
struct gprs_subscr *subscr = NULL;
int rc;
LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber authentication info\n");
subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
subscr->flags |= GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING;
rc = gprs_subscr_query_auth_info(subscr, auts, auts_rand);
gprs_subscr_put(subscr);
return rc;
}
static void gprs_subscr_free(struct gprs_subscr *gsub)
{
llist_del(&gsub->entry);
talloc_free(gsub);
}
struct gprs_subscr *_gprs_subscr_get(struct gprs_subscr *gsub,
const char *file, int line)
{
OSMO_ASSERT(gsub->use_count < INT_MAX);
gsub->use_count++;
LOGPSRC(DREF, LOGL_DEBUG, file, line,
"subscr %s usage increases to: %d\n",
gsub->imsi, gsub->use_count);
return gsub;
}
struct gprs_subscr *_gprs_subscr_put(struct gprs_subscr *gsub,
const char *file, int line)
{
gsub->use_count--;
LOGPSRC(DREF, gsub->use_count >= 0? LOGL_DEBUG : LOGL_ERROR,
file, line,
"subscr %s usage decreases to: %d%s\n",
gsub->imsi, gsub->use_count,
gsub->keep_in_ram? ", keep-in-ram flag is set" : "");
if (gsub->use_count > 0)
return gsub;
if (gsub->keep_in_ram)
return gsub;
gprs_subscr_free(gsub);
return NULL;
}

View File

@ -1,246 +0,0 @@
/* GPRS utility functions */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010-2014 by On-Waves
* (C) 2013 by Holger Hans Peter Freyther
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <openbsc/gprs_utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm48.h>
#include <string.h>
/* FIXME: this needs to go to libosmocore/msgb.c */
struct msgb *gprs_msgb_copy(const struct msgb *msg, const char *name)
{
struct libgb_msgb_cb *old_cb, *new_cb;
struct msgb *new_msg;
new_msg = msgb_alloc(msg->data_len, name);
if (!new_msg)
return NULL;
/* copy data */
memcpy(new_msg->_data, msg->_data, new_msg->data_len);
/* copy header */
new_msg->len = msg->len;
new_msg->data += msg->data - msg->_data;
new_msg->head += msg->head - msg->_data;
new_msg->tail += msg->tail - msg->_data;
if (msg->l1h)
new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data);
if (msg->l2h)
new_msg->l2h = new_msg->_data + (msg->l2h - msg->_data);
if (msg->l3h)
new_msg->l3h = new_msg->_data + (msg->l3h - msg->_data);
if (msg->l4h)
new_msg->l4h = new_msg->_data + (msg->l4h - msg->_data);
/* copy GB specific data */
old_cb = LIBGB_MSGB_CB(msg);
new_cb = LIBGB_MSGB_CB(new_msg);
if (old_cb->bssgph)
new_cb->bssgph = new_msg->_data + (old_cb->bssgph - msg->_data);
if (old_cb->llch)
new_cb->llch = new_msg->_data + (old_cb->llch - msg->_data);
/* bssgp_cell_id is a pointer into the old msgb, so we need to make
* it a pointer into the new msgb */
if (old_cb->bssgp_cell_id)
new_cb->bssgp_cell_id = new_msg->_data +
(old_cb->bssgp_cell_id - msg->_data);
new_cb->nsei = old_cb->nsei;
new_cb->bvci = old_cb->bvci;
new_cb->tlli = old_cb->tlli;
return new_msg;
}
/* TODO: Move this to libosmocore/msgb.c */
int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area,
size_t old_size, size_t new_size)
{
int rc;
uint8_t *rest = area + old_size;
int rest_len = msg->len - old_size - (area - msg->data);
int delta_size = (int)new_size - (int)old_size;
if (delta_size == 0)
return 0;
if (delta_size > 0) {
rc = msgb_trim(msg, msg->len + delta_size);
if (rc < 0)
return rc;
}
memmove(area + new_size, area + old_size, rest_len);
if (msg->l1h >= rest)
msg->l1h += delta_size;
if (msg->l2h >= rest)
msg->l2h += delta_size;
if (msg->l3h >= rest)
msg->l3h += delta_size;
if (msg->l4h >= rest)
msg->l4h += delta_size;
if (delta_size < 0)
msgb_trim(msg, msg->len + delta_size);
return 0;
}
int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str)
{
uint8_t *last_len_field;
int len;
/* Can we even write the length field to the output? */
if (max_len == 0)
return -1;
/* Remember where we need to put the length once we know it */
last_len_field = apn_enc;
len = 1;
apn_enc += 1;
while (str[0]) {
if (len >= max_len)
return -1;
if (str[0] == '.') {
*last_len_field = (apn_enc - last_len_field) - 1;
last_len_field = apn_enc;
} else {
*apn_enc = str[0];
}
apn_enc += 1;
str += 1;
len += 1;
}
*last_len_field = (apn_enc - last_len_field) - 1;
return len;
}
/* GSM 04.08, 10.5.7.3 GPRS Timer */
int gprs_tmr_to_secs(uint8_t tmr)
{
switch (tmr & GPRS_TMR_UNIT_MASK) {
case GPRS_TMR_2SECONDS:
return 2 * (tmr & GPRS_TMR_FACT_MASK);
default:
case GPRS_TMR_MINUTE:
return 60 * (tmr & GPRS_TMR_FACT_MASK);
case GPRS_TMR_6MINUTE:
return 360 * (tmr & GPRS_TMR_FACT_MASK);
case GPRS_TMR_DEACTIVATED:
return -1;
}
}
/* This functions returns a tmr value such that
* - f is monotonic
* - f(s) <= s
* - f(s) == s if a tmr exists with s = gprs_tmr_to_secs(tmr)
* - the best possible resolution is used
* where
* f(s) = gprs_tmr_to_secs(gprs_secs_to_tmr_floor(s))
*/
uint8_t gprs_secs_to_tmr_floor(int secs)
{
if (secs < 0)
return GPRS_TMR_DEACTIVATED;
if (secs < 2 * 32)
return GPRS_TMR_2SECONDS | (secs / 2);
if (secs < 60 * 2)
/* Ensure monotonicity */
return GPRS_TMR_2SECONDS | GPRS_TMR_FACT_MASK;
if (secs < 60 * 32)
return GPRS_TMR_MINUTE | (secs / 60);
if (secs < 360 * 6)
/* Ensure monotonicity */
return GPRS_TMR_MINUTE | GPRS_TMR_FACT_MASK;
if (secs < 360 * 32)
return GPRS_TMR_6MINUTE | (secs / 360);
return GPRS_TMR_6MINUTE | GPRS_TMR_FACT_MASK;
}
/* GSM 04.08, 10.5.1.4 */
int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len)
{
if (value_len != GSM48_TMSI_LEN)
return 0;
if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_TMSI)
return 0;
return 1;
}
/* GSM 04.08, 10.5.1.4 */
int gprs_is_mi_imsi(const uint8_t *value, size_t value_len)
{
if (value_len == 0)
return 0;
if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI)
return 0;
return 1;
}
int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi)
{
uint32_t tmsi_be;
if (!gprs_is_mi_tmsi(value, value_len))
return 0;
memcpy(&tmsi_be, value + 1, sizeof(tmsi_be));
*tmsi = ntohl(tmsi_be);
return 1;
}
void gprs_parse_tmsi(const uint8_t *value, uint32_t *tmsi)
{
uint32_t tmsi_be;
memcpy(&tmsi_be, value, sizeof(tmsi_be));
*tmsi = ntohl(tmsi_be);
}
int gprs_ra_id_equals(const struct gprs_ra_id *id1,
const struct gprs_ra_id *id2)
{
return (id1->mcc == id2->mcc && id1->mnc == id2->mnc &&
id1->lac == id2->lac && id1->rac == id2->rac);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,220 +0,0 @@
/* GTP Hub Implementation */
/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* gtphub_ares.c.
*
* This file is kept separate so that these functions can be wrapped for
* gtphub_test.c. When a function and its callers are in the same compilational
* unit, the wrappability may be optimized away.
*
* Author: Neels Hofmeyr
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <unistd.h>
#include <openbsc/gtphub.h>
#include <openbsc/debug.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/apn.h>
/* TODO split GRX ares from sgsn into a separate struct and allow use without
* globals. */
#include <openbsc/sgsn.h>
extern struct sgsn_instance *sgsn;
struct sgsn_instance sgsn_inst = { 0 };
struct sgsn_instance *sgsn = &sgsn_inst;
extern void *osmo_gtphub_ctx;
int gtphub_ares_init(struct gtphub *hub)
{
return sgsn_ares_init(sgsn);
}
struct ggsn_lookup {
struct llist_head entry;
struct expiring_item expiry_entry;
struct gtphub *hub;
char imsi_str[GSM23003_IMSI_MAX_DIGITS+1];
char apn_ni_str[GSM_APN_LENGTH];
char apn_oi_str[GSM_APN_LENGTH];
int have_3dig_mnc;
};
static int start_ares_query(struct ggsn_lookup *lookup);
static void ggsn_lookup_cb(void *arg, int status, int timeouts,
struct hostent *hostent)
{
struct ggsn_lookup *lookup = arg;
LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_cb(%p / %p)", lookup,
&lookup->expiry_entry);
if (status != ARES_SUCCESS) {
LOGP(DGTPHUB, LOGL_ERROR, "DNS query failed.\n");
/* Need to try with three digits now */
if (!lookup->have_3dig_mnc) {
lookup->have_3dig_mnc = 1;
if (start_ares_query(lookup) == 0)
return;
}
LOGP(DGTPHUB, LOGL_ERROR, "Failed to resolve GGSN. (%p)\n",
lookup);
goto remove_from_queue;
}
struct gsn_addr resolved_addr;
if (hostent->h_length > sizeof(resolved_addr.buf)) {
LOGP(DGTPHUB, LOGL_ERROR, "Addr size too large: %d > %d\n",
(int)hostent->h_length, (int)sizeof(resolved_addr.buf));
goto remove_from_queue;
}
/* Get the first addr from the list */
char *addr0 = hostent->h_addr_list[0];
if (!addr0) {
LOGP(DGTPHUB, LOGL_ERROR, "No host address.\n");
goto remove_from_queue;
}
memcpy(resolved_addr.buf, addr0, hostent->h_length);
resolved_addr.len = hostent->h_length;
LOGP(DGTPHUB, LOGL_NOTICE, "resolved addr %s\n",
osmo_hexdump((unsigned char*)&resolved_addr,
sizeof(resolved_addr)));
gtphub_resolved_ggsn(lookup->hub, lookup->apn_oi_str, &resolved_addr,
gtphub_now());
remove_from_queue:
LOGP(DGTPHUB, LOGL_ERROR, "Removing GGSN lookup. (%p / %p)\n", lookup,
&lookup->expiry_entry);
expiring_item_del(&lookup->expiry_entry);
}
static void make_addr_str(struct ggsn_lookup *lookup)
{
char *apn_oi_str;
apn_oi_str = osmo_apn_qualify_from_imsi(lookup->imsi_str,
lookup->apn_ni_str,
lookup->have_3dig_mnc);
osmo_strlcpy(lookup->apn_oi_str, apn_oi_str,
sizeof(lookup->apn_oi_str));
}
static int start_ares_query(struct ggsn_lookup *lookup)
{
LOGP(DGTPHUB, LOGL_DEBUG, "Going to query %s (%p / %p)\n",
lookup->apn_oi_str, lookup, &lookup->expiry_entry);
int rc = sgsn_ares_query(sgsn, lookup->apn_oi_str, ggsn_lookup_cb,
lookup);
if (rc != 0)
LOGP(DGTPHUB, LOGL_ERROR, "Failed to start ares query.\n");
return rc;
}
static void ggsn_lookup_del_cb(struct expiring_item *expi)
{
struct ggsn_lookup *lookup;
lookup = container_of(expi, struct ggsn_lookup, expiry_entry);
LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_del_cb(%p / %p)\n", lookup,
expi);
lookup->expiry_entry.del_cb = 0;
expiring_item_del(expi);
llist_del(&lookup->entry);
talloc_free(lookup);
}
struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub,
const char *imsi_str,
const char *apn_ni_str)
{
OSMO_ASSERT(imsi_str);
OSMO_ASSERT(apn_ni_str);
struct ggsn_lookup *lookup = talloc_zero(osmo_gtphub_ctx,
struct ggsn_lookup);
OSMO_ASSERT(lookup);
LOGP(DGTPHUB, LOGL_DEBUG, "Request to resolve IMSI"
" '%s' with APN-NI '%s' (%p / %p)\n",
imsi_str, apn_ni_str, lookup, &lookup->expiry_entry);
expiring_item_init(&lookup->expiry_entry);
lookup->hub = hub;
osmo_strlcpy(lookup->imsi_str, imsi_str, sizeof(lookup->imsi_str));
osmo_strlcpy(lookup->apn_ni_str, apn_ni_str,
sizeof(lookup->apn_ni_str));
make_addr_str(lookup);
struct ggsn_lookup *active;
llist_for_each_entry(active, &hub->ggsn_lookups, entry) {
if (strncmp(active->apn_oi_str, lookup->apn_oi_str,
sizeof(lookup->apn_oi_str)) == 0) {
LOGP(DGTPHUB, LOGL_DEBUG,
"Query already pending for %s\n",
lookup->apn_oi_str);
/* A query already pending. Just tip our hat. */
return NULL;
}
}
struct gtphub_resolved_ggsn *resolved;
llist_for_each_entry(resolved, &hub->resolved_ggsns, entry) {
if (strncmp(resolved->apn_oi_str, lookup->apn_oi_str,
sizeof(lookup->apn_oi_str)) == 0) {
LOGP(DGTPHUB, LOGL_DEBUG,
"GGSN resolved from cache: %s -> %s\n",
lookup->apn_oi_str,
gtphub_port_str(resolved->peer));
return resolved->peer;
}
}
/* Kick off a resolution, but so far return nothing. The hope is that
* the peer will resend the request (a couple of times), and by then
* the GGSN will be resolved. */
LOGP(DGTPHUB, LOGL_DEBUG,
"Sending out DNS query for %s..."
" (Returning failure, hoping for a retry once resolution"
" has concluded)\n",
lookup->apn_oi_str);
llist_add(&lookup->entry, &hub->ggsn_lookups);
lookup->expiry_entry.del_cb = ggsn_lookup_del_cb;
expiry_add(&hub->expire_quickly, &lookup->expiry_entry, gtphub_now());
start_ares_query(lookup);
return NULL;
}

View File

@ -1,359 +0,0 @@
/* GTP Hub main program */
/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>
#include <sys/stat.h>
#define _GNU_SOURCE
#include <getopt.h>
#include <osmocom/core/signal.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/ports.h>
#include <openbsc/debug.h>
#include <openbsc/gtphub.h>
#include <openbsc/vty.h>
#include "../../bscconfig.h"
extern void *osmo_gtphub_ctx;
const char *gtphub_copyright =
"Copyright (C) 2015 sysmocom s.f.m.c GmbH <info@sysmocom.de>\r\n"
"License AGPLv3+: GNU AGPL version 2 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
static struct log_info_cat gtphub_categories[] = {
[DGTPHUB] = {
.name = "DGTPHUB",
.description = "GTP Hub",
.color = "\033[1;33m",
.enabled = 1,
.loglevel = LOGL_INFO,
},
};
int gtphub_log_filter_fn(const struct log_context *ctx,
struct log_target *tar)
{
return 0;
}
static const struct log_info gtphub_log_info = {
.filter_fn = gtphub_log_filter_fn,
.cat = gtphub_categories,
.num_cat = ARRAY_SIZE(gtphub_categories),
};
void log_cfg(struct gtphub_cfg *cfg)
{
int side_idx, plane_idx;
for_each_side_and_plane(side_idx, plane_idx) {
struct gtphub_cfg_addr *a;
a = &cfg->to_gsns[side_idx][plane_idx].bind;
LOGP(DGTPHUB, LOGL_NOTICE,
"to-%ss bind, %s: %s port %d\n",
gtphub_side_idx_names[side_idx],
gtphub_plane_idx_names[plane_idx],
a->addr_str, a->port);
}
}
static void signal_handler(int signal)
{
fprintf(stdout, "signal %d received\n", signal);
switch (signal) {
case SIGINT:
case SIGTERM:
osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
sleep(1);
exit(0);
break;
case SIGABRT:
/* in case of abort, we want to obtain a talloc report
* and then return to the caller, who will abort the process */
case SIGUSR1:
case SIGUSR2:
talloc_report_full(osmo_gtphub_ctx, stderr);
break;
default:
break;
}
}
extern int bsc_vty_go_parent(struct vty *vty);
static struct vty_app_info vty_info = {
.name = "OsmoGTPhub",
.version = PACKAGE_VERSION,
.go_parent_cb = bsc_vty_go_parent,
.is_config_node = bsc_vty_is_config_node,
};
struct cmdline_cfg {
const char *config_file;
const char *restart_counter_file;
int daemonize;
};
static uint8_t next_restart_count(const char *path)
{
int umask_was = umask(022);
uint8_t counter = 0;
FILE *f = fopen(path, "r");
if (f) {
int rc = fscanf(f, "%hhu", &counter);
if (rc != 1)
goto failed_to_read;
char c;
while (fread(&c, 1, 1, f) > 0) {
switch (c) {
case ' ':
case '\t':
case '\n':
case '\r':
break;
default:
goto failed_to_read;
}
}
fclose(f);
}
counter ++;
f = fopen(path, "w");
if (!f)
goto failed_to_write;
if (fprintf(f, "%" PRIu8 "\n", counter) < 2)
goto failed_to_write;
if (fclose(f)) {
f = NULL;
goto failed_to_write;
}
umask(umask_was);
LOGP(DGTPHUB, LOGL_NOTICE, "Restarted with counter %hhu\n", counter);
return counter;
failed_to_read:
fclose(f);
umask(umask_was);
LOGP(DGTPHUB, LOGL_FATAL, "Restart counter file cannot be parsed:"
" %s\n", path);
exit(1);
failed_to_write:
if (f)
fclose(f);
umask(umask_was);
LOGP(DGTPHUB, LOGL_FATAL, "Restart counter file cannot be written:"
" %s\n", path);
exit(1);
}
static void print_help(struct cmdline_cfg *ccfg)
{
printf("gtphub commandline options\n");
printf(" -h,--help This text.\n");
printf(" -D,--daemonize Fork the process into a background daemon.\n");
printf(" -d,--debug <cat> Enable Debugging for this category.\n");
printf(" Pass '-d list' to get a category listing.\n");
printf(" -s,--disable-color\n");
printf(" -c,--config-file <path> The config file to use [%s].\n",
ccfg->config_file);
printf(" -e,--log-level <nr> Set a global log level.\n");
printf(" -r,--restart-file <path> File for counting restarts [%s].\n",
ccfg->restart_counter_file);
}
static void list_categories(void)
{
printf("Avaliable debug categories:\n");
int i;
for (i = 0; i < gtphub_log_info.num_cat; ++i) {
if (!gtphub_log_info.cat[i].name)
continue;
printf("%s\n", gtphub_log_info.cat[i].name);
}
}
static void handle_options(struct cmdline_cfg *ccfg, int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"debug", 1, 0, 'd'},
{"daemonize", 0, 0, 'D'},
{"config-file", 1, 0, 'c'},
{"disable-color", 0, 0, 's'},
{"timestamp", 0, 0, 'T'},
{"log-level", 1, 0, 'e'},
{"restart-file", 1, 0, 'r'},
{NULL, 0, 0, 0}
};
c = getopt_long(argc, argv, "hd:Dc:sTe:r:",
long_options, &option_index);
if (c == -1) {
if (optind < argc) {
LOGP(DGTPHUB, LOGL_FATAL,
"Excess commandline arguments ('%s').\n",
argv[optind]);
exit(2);
}
break;
}
switch (c) {
case 'h':
//print_usage();
print_help(ccfg);
exit(0);
case 's':
log_set_use_color(osmo_stderr_target, 0);
break;
case 'd':
if (strcmp("list", optarg) == 0) {
list_categories();
exit(0);
} else
log_parse_category_mask(osmo_stderr_target, optarg);
break;
case 'D':
ccfg->daemonize = 1;
break;
case 'c':
ccfg->config_file = optarg;
break;
case 'T':
log_set_print_timestamp(osmo_stderr_target, 1);
break;
case 'e':
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
case 'r':
ccfg->restart_counter_file = optarg;
break;
default:
LOGP(DGTPHUB, LOGL_FATAL, "Invalid command line argument, abort.\n");
exit(1);
break;
}
}
}
int main(int argc, char **argv)
{
int rc;
struct cmdline_cfg _ccfg;
struct cmdline_cfg *ccfg = &_ccfg;
memset(ccfg, '\0', sizeof(*ccfg));
ccfg->config_file = "./gtphub.conf";
ccfg->restart_counter_file = "./gtphub_restart_count";
struct gtphub_cfg _cfg;
struct gtphub_cfg *cfg = &_cfg;
memset(cfg, '\0', sizeof(*cfg));
struct gtphub _hub;
struct gtphub *hub = &_hub;
osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub");
msgb_talloc_ctx_init(osmo_gtphub_ctx, 0);
signal(SIGINT, &signal_handler);
signal(SIGTERM, &signal_handler);
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
osmo_init_logging(&gtphub_log_info);
vty_info.copyright = gtphub_copyright;
vty_init(&vty_info);
logging_vty_add_cmds(NULL);
gtphub_vty_init(hub, cfg);
rate_ctr_init(osmo_gtphub_ctx);
handle_options(ccfg, argc, argv);
rc = gtphub_cfg_read(cfg, ccfg->config_file);
if (rc < 0) {
LOGP(DGTPHUB, LOGL_FATAL, "Cannot parse config file '%s'\n",
ccfg->config_file);
exit(2);
}
/* start telnet after reading config for vty_get_bind_addr() */
rc = telnet_init_dynif(osmo_gtphub_ctx, 0, vty_get_bind_addr(),
OSMO_VTY_PORT_GTPHUB);
if (rc < 0)
exit(1);
if (gtphub_start(hub, cfg,
next_restart_count(ccfg->restart_counter_file))
!= 0)
return -1;
log_cfg(cfg);
if (ccfg->daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
LOGP(DGTPHUB, LOGL_FATAL, "Error during daemonize");
exit(1);
}
}
while (1) {
rc = osmo_select_main(0);
if (rc < 0)
exit(3);
}
/* not reached */
exit(0);
}

View File

@ -1,60 +0,0 @@
/* GTP Hub Implementation */
/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* gtphub_sock.c.
*
* This file is kept separate so that these functions can be wrapped for
* gtphub_test.c. When a function and its callers are in the same compilational
* unit, the wrappability may be optimized away.
*
* Author: Neels Hofmeyr
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <openbsc/gtphub.h>
#include <openbsc/debug.h>
/* Convenience makro, note: only within this C file. */
#define LOG(level, fmt, args...) \
LOGP(DGTPHUB, level, fmt, ##args)
int gtphub_write(const struct osmo_fd *to,
const struct osmo_sockaddr *to_addr,
const uint8_t *buf, size_t buf_len)
{
errno = 0;
ssize_t sent = sendto(to->fd, buf, buf_len, 0,
(struct sockaddr*)&to_addr->a, to_addr->l);
LOG(LOGL_DEBUG, "to %s\n", osmo_sockaddr_to_str(to_addr));
if (sent == -1) {
LOG(LOGL_ERROR, "error: %s\n", strerror(errno));
return -EINVAL;
}
if (sent != buf_len)
LOG(LOGL_ERROR, "sent(%d) != data_len(%d)\n",
(int)sent, (int)buf_len);
else
LOG(LOGL_DEBUG, "Sent %d: %s%s\n",
(int)sent,
osmo_hexdump(buf, sent > 1000? 1000 : sent),
sent > 1000 ? "..." : "");
return 0;
}

View File

@ -1,613 +0,0 @@
/* (C) 2015 by sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* Author: Neels Hofmeyr
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string.h>
#include <inttypes.h>
#include <ares.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osmocom/core/talloc.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/misc.h>
#include <openbsc/vty.h>
#include <openbsc/gtphub.h>
/* TODO split GRX ares from sgsn into a separate struct and allow use without
* globals. */
#include <openbsc/sgsn.h>
extern struct sgsn_instance *sgsn;
static struct gtphub *g_hub = 0;
static struct gtphub_cfg *g_cfg = 0;
static struct cmd_node gtphub_node = {
GTPHUB_NODE,
"%s(config-gtphub)# ",
1,
};
#define GTPH_DEFAULT_CONTROL_PORT 2123
#define GTPH_DEFAULT_USER_PORT 2152
static void write_addrs(struct vty *vty, const char *name,
struct gtphub_cfg_addr *c, struct gtphub_cfg_addr *u)
{
if ((c->port == GTPH_DEFAULT_CONTROL_PORT)
&& (u->port == GTPH_DEFAULT_USER_PORT)
&& (strcmp(c->addr_str, u->addr_str) == 0)) {
/* Default port numbers and same IP address: write "short"
* variant. */
vty_out(vty, " %s %s%s",
name,
c->addr_str,
VTY_NEWLINE);
return;
}
vty_out(vty, " %s ctrl %s %d user %s %d%s",
name,
c->addr_str, (int)c->port,
u->addr_str, (int)u->port,
VTY_NEWLINE);
struct ares_addr_node *server;
for (server = sgsn->ares_servers; server; server = server->next)
vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE);
}
static int config_write_gtphub(struct vty *vty)
{
vty_out(vty, "gtphub%s", VTY_NEWLINE);
write_addrs(vty, "bind-to-sgsns",
&g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind,
&g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind);
write_addrs(vty, "bind-to-ggsns",
&g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind,
&g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind);
if (g_cfg->sgsn_use_sender) {
vty_out(vty, "sgsn-use-sender%s", VTY_NEWLINE);
}
if (g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str) {
write_addrs(vty, "sgsn-proxy",
&g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL],
&g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER]);
}
if (g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str) {
write_addrs(vty, "ggsn-proxy",
&g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL],
&g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER]);
}
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub, cfg_gtphub_cmd,
"gtphub",
"Configure the GTP hub\n")
{
vty->node = GTPHUB_NODE;
return CMD_SUCCESS;
}
#define BIND_ARGS "ctrl ADDR <0-65535> user ADDR <0-65535>"
#define BIND_DOCS \
"Set GTP-C bind\n" \
"GTP-C local IP address (v4 or v6)\n" \
"GTP-C local port\n" \
"Set GTP-U bind\n" \
"GTP-U local IP address (v4 or v6)\n" \
"GTP-U local port\n"
DEFUN(cfg_gtphub_bind_to_sgsns_short, cfg_gtphub_bind_to_sgsns_short_cmd,
"bind-to-sgsns ADDR",
"GTP Hub Parameters\n"
"Set the local bind address to listen for SGSNs, for both GTP-C and GTP-U\n"
"Local IP address (v4 or v6)\n"
)
{
int i;
for_each_plane(i)
g_cfg->to_gsns[GTPH_SIDE_SGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT;
g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT;
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub_bind_to_ggsns_short, cfg_gtphub_bind_to_ggsns_short_cmd,
"bind-to-ggsns ADDR",
"GTP Hub Parameters\n"
"Set the local bind address to listen for GGSNs, for both GTP-C and GTP-U\n"
"Local IP address (v4 or v6)\n"
)
{
int i;
for_each_plane(i)
g_cfg->to_gsns[GTPH_SIDE_GGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT;
g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT;
return CMD_SUCCESS;
}
static int handle_binds(struct gtphub_cfg_bind *b, const char **argv)
{
b[GTPH_PLANE_CTRL].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
b[GTPH_PLANE_CTRL].bind.port = atoi(argv[1]);
b[GTPH_PLANE_USER].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
b[GTPH_PLANE_USER].bind.port = atoi(argv[3]);
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub_bind_to_sgsns, cfg_gtphub_bind_to_sgsns_cmd,
"bind-to-sgsns " BIND_ARGS,
"GTP Hub Parameters\n"
"Set the local bind addresses and ports to listen for SGSNs\n"
BIND_DOCS
)
{
return handle_binds(g_cfg->to_gsns[GTPH_SIDE_SGSN], argv);
}
DEFUN(cfg_gtphub_bind_to_ggsns, cfg_gtphub_bind_to_ggsns_cmd,
"bind-to-ggsns " BIND_ARGS,
"GTP Hub Parameters\n"
"Set the local bind addresses and ports to listen for GGSNs\n"
BIND_DOCS
)
{
return handle_binds(g_cfg->to_gsns[GTPH_SIDE_GGSN], argv);
}
DEFUN(cfg_gtphub_ggsn_proxy_short, cfg_gtphub_ggsn_proxy_short_cmd,
"ggsn-proxy ADDR",
"GTP Hub Parameters\n"
"Redirect all GGSN bound traffic to default ports on this address (another gtphub)\n"
"Remote IP address (v4 or v6)\n"
)
{
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT;
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT;
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub_ggsn_proxy, cfg_gtphub_ggsn_proxy_cmd,
"ggsn-proxy " BIND_ARGS,
"GTP Hub Parameters\n"
"Redirect all GGSN bound traffic to these addresses and ports (another gtphub)\n"
BIND_DOCS
)
{
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]);
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = atoi(argv[3]);
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub_sgsn_proxy_short, cfg_gtphub_sgsn_proxy_short_cmd,
"sgsn-proxy ADDR",
"GTP Hub Parameters\n"
"Redirect all SGSN bound traffic to default ports on this address (another gtphub)\n"
"Remote IP address (v4 or v6)\n"
)
{
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT;
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT;
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub_sgsn_proxy, cfg_gtphub_sgsn_proxy_cmd,
"sgsn-proxy " BIND_ARGS,
"GTP Hub Parameters\n"
"Redirect all SGSN bound traffic to these addresses and ports (another gtphub)\n"
BIND_DOCS
)
{
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]);
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = atoi(argv[3]);
return CMD_SUCCESS;
}
#define SGSN_USE_SENDER_STR \
"Ignore SGSN's Address IEs, use sender address and port (useful over NAT)\n"
DEFUN(cfg_gtphub_sgsn_use_sender,
cfg_gtphub_sgsn_use_sender_cmd,
"sgsn-use-sender",
SGSN_USE_SENDER_STR)
{
g_cfg->sgsn_use_sender = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub_no_sgsn_use_sender,
cfg_gtphub_no_sgsn_use_sender_cmd,
"no sgsn-use-sender",
NO_STR SGSN_USE_SENDER_STR)
{
g_cfg->sgsn_use_sender = 0;
return CMD_SUCCESS;
}
/* Copied from sgsn_vty.h */
DEFUN(cfg_grx_ggsn, cfg_grx_ggsn_cmd,
"grx-dns-add A.B.C.D",
"Add DNS server\nIPv4 address\n")
{
struct ares_addr_node *node = talloc_zero(tall_bsc_ctx, struct ares_addr_node);
node->family = AF_INET;
inet_aton(argv[0], &node->addr.addr4);
node->next = sgsn->ares_servers;
sgsn->ares_servers = node;
return CMD_SUCCESS;
}
static void show_bind_stats_all(struct vty *vty)
{
int plane_idx;
for_each_plane(plane_idx) {
vty_out(vty, "- %s Plane:%s",
gtphub_plane_idx_names[plane_idx], VTY_NEWLINE);
int side_idx;
for_each_side(side_idx) {
struct gtphub_bind *b = &g_hub->to_gsns[side_idx][plane_idx];
vty_out(vty, " - local addr to/from %ss: %s port %d%s",
gtphub_side_idx_names[side_idx],
gsn_addr_to_str(&b->local_addr), (int)b->local_port,
VTY_NEWLINE);
vty_out_rate_ctr_group(vty, " ", b->counters_io);
}
}
}
static void show_tunnel_stats(struct vty *vty, struct gtphub_tunnel *tun)
{
int plane_idx;
for_each_plane(plane_idx) {
vty_out(vty, "- %s Plane:%s",
gtphub_plane_idx_names[plane_idx], VTY_NEWLINE);
int side_idx;
for_each_side(side_idx) {
struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx];
vty_out(vty, " - to/from %s:%s",
gtphub_side_idx_names[side_idx],
VTY_NEWLINE);
vty_out_rate_ctr_group(vty, " ", te->counters_io);
}
}
}
static void show_peer_summary(struct vty *vty, const char *prefix,
int side_idx, int plane_idx,
struct gtphub_peer *p, int with_io_stats)
{
struct gtphub_peer_addr *pa;
int p2l = strlen(prefix) + 4 + 1;
char prefix2[p2l];
memset(prefix2, ' ', p2l - 1);
prefix2[p2l - 1] = '\0';
if (with_io_stats) {
llist_for_each_entry(pa, &p->addresses, entry) {
vty_out(vty, "%s- %s %s %s%s", prefix,
gtphub_side_idx_names[side_idx],
gtphub_plane_idx_names[plane_idx],
gsn_addr_to_str(&pa->addr),
VTY_NEWLINE);
struct gtphub_peer_port *pp;
llist_for_each_entry(pp, &pa->ports, entry) {
vty_out(vty, "%s Port %" PRIu16 "%s", prefix, pp->port, VTY_NEWLINE);
vty_out_rate_ctr_group(vty, prefix2, pp->counters_io);
}
}
} else {
llist_for_each_entry(pa, &p->addresses, entry) {
vty_out(vty, "%s- %s %s %s", prefix,
gtphub_side_idx_names[side_idx],
gtphub_plane_idx_names[plane_idx],
gsn_addr_to_str(&pa->addr));
struct gtphub_peer_port *pp;
llist_for_each_entry(pp, &pa->ports, entry) {
vty_out(vty, ":%" PRIu16, pp->port);
}
vty_out(vty, VTY_NEWLINE);
}
}
}
static void show_peers_summary(struct vty *vty)
{
int side_idx;
int plane_idx;
int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}};
for_each_side(side_idx) {
for_each_plane(plane_idx) {
struct gtphub_peer *p;
llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) {
count[side_idx][plane_idx] ++;
}
}
}
vty_out(vty, "Peers Count:%s", VTY_NEWLINE);
for_each_side_and_plane(side_idx, plane_idx) {
vty_out(vty, " %s %s peers: %d%s",
gtphub_side_idx_names[side_idx],
gtphub_plane_idx_names[plane_idx],
count[side_idx][plane_idx],
VTY_NEWLINE);
}
}
static void show_peers_all(struct vty *vty, int with_io_stats)
{
int side_idx;
int plane_idx;
int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}};
vty_out(vty, "All Peers%s%s",
with_io_stats? " with I/O stats" : "",
VTY_NEWLINE);
for_each_side(side_idx) {
vty_out(vty, "- %s%s", gtphub_side_idx_names[side_idx], VTY_NEWLINE);
for_each_plane(plane_idx) {
struct gtphub_peer *p;
llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) {
count[side_idx][plane_idx] ++;
show_peer_summary(vty, " ", side_idx, plane_idx, p, with_io_stats);
}
}
}
for_each_side_and_plane(side_idx, plane_idx) {
vty_out(vty, "%s %s peers: %d%s",
gtphub_side_idx_names[side_idx],
gtphub_plane_idx_names[plane_idx],
count[side_idx][plane_idx],
VTY_NEWLINE);
}
}
static void show_tunnels_summary(struct vty *vty)
{
time_t now = gtphub_now();
const int w = 36;
int max_expiry = g_hub->expire_slowly.expiry_in_seconds;
float seconds_per_step = ((float)max_expiry) / w;
/* Print TEI mapping expiry in an ASCII histogram, like:
TEI map summary
Legend: '_'=0 '.'<=1% ':'<=2% '|'<=10% '#'>10% (10.0 m/step)
CTRL: 30 mappings, valid for 360m[# :. | . : . ]1m
USER: 30 mappings, valid for 360m[# :. | . : . ]1m
4 TEI mappings in total, last expiry in 359.4 min
*/
vty_out(vty,
"Tunnels summary%s"
" Legend: ' '=0 '.'<=1%% ':'<=2%% '|'<=10%% '#'>10%% (%.1f m/step)%s",
VTY_NEWLINE,
seconds_per_step / 60.,
VTY_NEWLINE);
int last_expiry = 0;
unsigned int count = 0;
int histogram[w];
memset(histogram, 0, sizeof(histogram));
struct gtphub_tunnel *t;
llist_for_each_entry(t, &g_hub->tunnels, entry) {
count ++;
int expiry = t->expiry_entry.expiry - now;
last_expiry = (last_expiry > expiry) ? last_expiry : expiry;
int hi = ((float)expiry) / seconds_per_step;
if (hi < 0)
hi = 0;
if (hi > (w - 1))
hi = w - 1;
histogram[hi] ++;
}
vty_out(vty,
" %u tunnels, valid for %dm[",
count, max_expiry / 60);
int i;
for (i = w - 1; i >= 0; i--) {
char c;
int val = histogram[i];
int percent = 100. * val / count;
if (!val)
c = ' ';
else if (percent <= 1)
c = '.';
else if (percent <= 2)
c = ':';
else if (percent <= 10)
c = '|';
else c = '#';
vty_out(vty, "%c", c);
}
vty_out(vty, "]1m%s", VTY_NEWLINE);
vty_out(vty, " last expiry in %.1f min%s",
((float)last_expiry) / 60.,
VTY_NEWLINE);
}
static void show_tunnels_all(struct vty *vty, int with_io_stats)
{
time_t now = gtphub_now();
vty_out(vty, "All tunnels%s:%s"
"Legend: TEI=<hex>: SGSN <-> GGSN (expiry in minutes), with each:%s"
" <IP-Ctrl>[/<IP-User>] (TEI C=<TEI-Ctrl-hex> U=<TEI-User-hex>)%s",
with_io_stats? "with I/O stats" : "",
VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
unsigned int count = 0;
unsigned int incomplete = 0;
struct gtphub_tunnel *tun;
llist_for_each_entry(tun, &g_hub->tunnels, entry) {
vty_out(vty,
"%s (expiry in %dm)%s",
gtphub_tunnel_str(tun),
(int)((tun->expiry_entry.expiry - now) / 60),
VTY_NEWLINE);
count ++;
if (!gtphub_tunnel_complete(tun))
incomplete ++;
if (with_io_stats)
show_tunnel_stats(vty, tun);
}
vty_out(vty, "Total: %u tunnels (of which %u incomplete)%s",
count, incomplete, VTY_NEWLINE);
}
#define SHOW_GTPHUB_STRS SHOW_STR "Show info on running GTP hub\n"
#define SHOW_GTPHUB_PEERS_STRS SHOW_GTPHUB_STRS "Active peers\n"
#define SHOW_GTPHUB_TUNS_STRS SHOW_GTPHUB_STRS "Active tunnels\n"
DEFUN(show_gtphub_peers_summary, show_gtphub_peers_summary_cmd, "show gtphub peers summary",
SHOW_GTPHUB_PEERS_STRS "Summary of all peers\n")
{
show_peers_summary(vty);
return CMD_SUCCESS;
}
DEFUN(show_gtphub_peers_list, show_gtphub_peers_list_cmd, "show gtphub peers list",
SHOW_GTPHUB_PEERS_STRS "List all peers\n")
{
show_peers_all(vty, 0);
return CMD_SUCCESS;
}
DEFUN(show_gtphub_peers_stats, show_gtphub_peers_stats_cmd, "show gtphub peers stats",
SHOW_GTPHUB_PEERS_STRS "List all peers with I/O stats\n")
{
show_peers_all(vty, 1);
return CMD_SUCCESS;
}
DEFUN(show_gtphub_tunnels_summary, show_gtphub_tunnels_summary_cmd, "show gtphub tunnels summary",
SHOW_GTPHUB_TUNS_STRS "Summary of all tunnels\n")
{
show_tunnels_summary(vty);
return CMD_SUCCESS;
}
DEFUN(show_gtphub_tunnels_list, show_gtphub_tunnels_list_cmd, "show gtphub tunnels list",
SHOW_GTPHUB_TUNS_STRS "List all tunnels\n")
{
show_tunnels_all(vty, 0);
return CMD_SUCCESS;
}
DEFUN(show_gtphub_tunnels_stats, show_gtphub_tunnels_stats_cmd, "show gtphub tunnels stats",
SHOW_GTPHUB_TUNS_STRS "List all tunnels with I/O stats\n")
{
show_tunnels_all(vty, 1);
return CMD_SUCCESS;
}
DEFUN(show_gtphub, show_gtphub_cmd, "show gtphub all",
SHOW_GTPHUB_STRS "Summarize everything about the GTP hub\n")
{
show_bind_stats_all(vty);
show_peers_summary(vty);
show_tunnels_summary(vty);
return CMD_SUCCESS;
}
int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg)
{
g_hub = global_hub;
g_cfg = global_cfg;
install_element_ve(&show_gtphub_cmd);
install_element_ve(&show_gtphub_peers_summary_cmd);
install_element_ve(&show_gtphub_peers_list_cmd);
install_element_ve(&show_gtphub_peers_stats_cmd);
install_element_ve(&show_gtphub_tunnels_summary_cmd);
install_element_ve(&show_gtphub_tunnels_list_cmd);
install_element_ve(&show_gtphub_tunnels_stats_cmd);
install_element(CONFIG_NODE, &cfg_gtphub_cmd);
install_node(&gtphub_node, config_write_gtphub);
vty_install_default(GTPHUB_NODE);
install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_short_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_short_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_short_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_short_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_use_sender_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_no_sgsn_use_sender_cmd);
install_element(GTPHUB_NODE, &cfg_grx_ggsn_cmd);
return 0;
}
int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file)
{
int rc;
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
return rc;
}
return 0;
}

View File

@ -1,23 +0,0 @@
!
! Osmocom SGSN (0.9.0.474-0ede2) configuration saved from vty
!!
!
line vty
no login
!
sgsn
gtp local-ip 192.168.100.11
ggsn 0 remote-ip 192.168.100.239
ggsn 0 gtp-version 1
ns
timer tns-block 3
timer tns-block-retries 3
timer tns-reset 3
timer tns-reset-retries 3
timer tns-test 30
timer tns-alive 3
timer tns-alive-retries 10
encapsulation udp local-ip 192.168.100.11
encapsulation udp local-port 23000
encapsulation framerelay-gre enabled 0
bssgp

View File

@ -1,173 +0,0 @@
/* C-ARES DNS resolver integration */
/*
* (C) 2015 by Holger Hans Peter Freyther
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <openbsc/sgsn.h>
#include <openbsc/debug.h>
#include <netdb.h>
struct cares_event_fd {
struct llist_head head;
struct osmo_fd fd;
};
struct cares_cb_data {
ares_host_callback cb;
void *data;
};
static void osmo_ares_reschedule(struct sgsn_instance *sgsn);
static void ares_cb(void *_arg, int status, int timeouts, struct hostent *hostent)
{
struct cares_cb_data *arg = _arg;
arg->cb(arg->data, status, timeouts, hostent);
osmo_ares_reschedule(sgsn);
talloc_free(arg);
}
static int ares_osmo_fd_cb(struct osmo_fd *fd, unsigned int what)
{
LOGP(DGPRS, LOGL_DEBUG, "C-ares fd(%d) ready(%d)\n", fd->fd, what);
ares_process_fd(sgsn->ares_channel,
(what & BSC_FD_READ) ? fd->fd : ARES_SOCKET_BAD,
(what & BSC_FD_WRITE) ? fd->fd : ARES_SOCKET_BAD);
osmo_ares_reschedule(sgsn);
return 0;
}
static void ares_timeout_cb(void *data)
{
struct sgsn_instance *sgsn = data;
LOGP(DGPRS, LOGL_DEBUG, "C-ares triggering timeout\n");
ares_process_fd(sgsn->ares_channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
osmo_ares_reschedule(sgsn);
}
static void osmo_ares_reschedule(struct sgsn_instance *sgsn)
{
struct timeval *timeout, tv;
osmo_timer_del(&sgsn->ares_timer);
timeout = ares_timeout(sgsn->ares_channel, NULL, &tv);
if (timeout) {
LOGP(DGPRS, LOGL_DEBUG, "C-ares scheduling timeout %llu.%llu\n",
(unsigned long long) tv.tv_sec,
(unsigned long long) tv.tv_usec);
osmo_timer_setup(&sgsn->ares_timer, ares_timeout_cb, sgsn);
osmo_timer_schedule(&sgsn->ares_timer, tv.tv_sec, tv.tv_usec);
}
}
static void setup_ares_osmo_fd(void *data, int fd, int read, int write)
{
struct cares_event_fd *ufd, *tmp;
/* delete the entry */
if (read == 0 && write == 0) {
llist_for_each_entry_safe(ufd, tmp, &sgsn->ares_fds, head) {
if (ufd->fd.fd != fd)
continue;
LOGP(DGPRS, LOGL_DEBUG,
"Removing C-ares watched fd (%d)\n", fd);
osmo_fd_unregister(&ufd->fd);
llist_del(&ufd->head);
talloc_free(ufd);
return;
}
}
/* Search for the fd or create a new one */
llist_for_each_entry(ufd, &sgsn->ares_fds, head) {
if (ufd->fd.fd != fd)
continue;
LOGP(DGPRS, LOGL_DEBUG, "Updating C-ares fd (%d)\n", fd);
goto update_fd;
}
LOGP(DGPRS, LOGL_DEBUG, "Registering C-ares fd (%d)\n", fd);
ufd = talloc_zero(tall_bsc_ctx, struct cares_event_fd);
ufd->fd.fd = fd;
ufd->fd.cb = ares_osmo_fd_cb;
ufd->fd.data = data;
if (osmo_fd_register(&ufd->fd) != 0)
LOGP(DGPRS, LOGL_ERROR, "Failed to register C-ares fd (%d)\n", fd);
llist_add(&ufd->head, &sgsn->ares_fds);
update_fd:
if (read)
ufd->fd.when |= BSC_FD_READ;
else
ufd->fd.when &= ~BSC_FD_READ;
if (write)
ufd->fd.when |= BSC_FD_WRITE;
else
ufd->fd.when &= ~BSC_FD_WRITE;
osmo_ares_reschedule(sgsn);
}
int sgsn_ares_query(struct sgsn_instance *sgsn, const char *name,
ares_host_callback cb, void *data)
{
struct cares_cb_data *cb_data;
cb_data = talloc_zero(tall_bsc_ctx, struct cares_cb_data);
cb_data->cb = cb;
cb_data->data = data;
ares_gethostbyname(sgsn->ares_channel, name, AF_INET, ares_cb, cb_data);
osmo_ares_reschedule(sgsn);
return 0;
}
int sgsn_ares_init(struct sgsn_instance *sgsn)
{
struct ares_options options;
int optmask;
int rc;
INIT_LLIST_HEAD(&sgsn->ares_fds);
memset(&options, 0, sizeof(options));
options.sock_state_cb = setup_ares_osmo_fd;
options.sock_state_cb_data = sgsn;
optmask = ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB | ARES_OPT_DOMAINS;
if (sgsn->ares_servers)
optmask |= ARES_OPT_SERVERS;
ares_library_init(ARES_LIB_INIT_ALL);
rc = ares_init_options(&sgsn->ares_channel, &options, optmask);
if (rc != ARES_SUCCESS)
return rc;
if (sgsn->ares_servers)
rc = ares_set_servers(sgsn->ares_channel, sgsn->ares_servers);
return rc;
}
osmo_static_assert(ARES_SUCCESS == 0, ares_success_zero);

View File

@ -1,312 +0,0 @@
/* MS authorization and subscriber data handling */
/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/core/utils.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/gprs_subscriber.h>
#include <openbsc/debug.h>
const struct value_string auth_state_names[] = {
{ SGSN_AUTH_ACCEPTED, "accepted"},
{ SGSN_AUTH_REJECTED, "rejected"},
{ SGSN_AUTH_UNKNOWN, "unknown"},
{ SGSN_AUTH_AUTHENTICATE, "authenticate" },
{ SGSN_AUTH_UMTS_RESYNC, "UMTS-resync" },
{ 0, NULL }
};
const struct value_string *sgsn_auth_state_names = auth_state_names;
void sgsn_auth_init(void)
{
INIT_LLIST_HEAD(&sgsn->cfg.imsi_acl);
}
/* temporary IMSI ACL hack */
struct imsi_acl_entry *sgsn_acl_lookup(const char *imsi, struct sgsn_config *cfg)
{
struct imsi_acl_entry *acl;
llist_for_each_entry(acl, &cfg->imsi_acl, list) {
if (!strcmp(imsi, acl->imsi))
return acl;
}
return NULL;
}
int sgsn_acl_add(const char *imsi, struct sgsn_config *cfg)
{
struct imsi_acl_entry *acl;
if (sgsn_acl_lookup(imsi, cfg))
return -EEXIST;
acl = talloc_zero(NULL, struct imsi_acl_entry);
if (!acl)
return -ENOMEM;
osmo_strlcpy(acl->imsi, imsi, sizeof(acl->imsi));
llist_add(&acl->list, &cfg->imsi_acl);
return 0;
}
int sgsn_acl_del(const char *imsi, struct sgsn_config *cfg)
{
struct imsi_acl_entry *acl;
acl = sgsn_acl_lookup(imsi, cfg);
if (!acl)
return -ENODEV;
llist_del(&acl->list);
talloc_free(acl);
return 0;
}
enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mmctx)
{
char mccmnc[16];
int check_net = 0;
int check_acl = 0;
OSMO_ASSERT(mmctx);
switch (sgsn->cfg.auth_policy) {
case SGSN_AUTH_POLICY_OPEN:
return SGSN_AUTH_ACCEPTED;
case SGSN_AUTH_POLICY_CLOSED:
check_net = 1;
check_acl = 1;
break;
case SGSN_AUTH_POLICY_ACL_ONLY:
check_acl = 1;
break;
case SGSN_AUTH_POLICY_REMOTE:
if (!mmctx->subscr)
return mmctx->auth_state;
if (mmctx->subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING_MASK)
return mmctx->auth_state;
if (sgsn->cfg.require_authentication &&
(!mmctx->is_authenticated ||
mmctx->subscr->sgsn_data->auth_triplets_updated))
return SGSN_AUTH_AUTHENTICATE;
if (mmctx->subscr->authorized)
return SGSN_AUTH_ACCEPTED;
return SGSN_AUTH_REJECTED;
}
if (!strlen(mmctx->imsi)) {
LOGMMCTXP(LOGL_NOTICE, mmctx,
"Missing IMSI, authorization state not known\n");
return SGSN_AUTH_UNKNOWN;
}
if (check_net) {
/* We simply assume that the IMSI exists, as long as it is part
* of 'our' network */
snprintf(mccmnc, sizeof(mccmnc), "%03d%02d",
mmctx->ra.mcc, mmctx->ra.mnc);
if (strncmp(mccmnc, mmctx->imsi, 5) == 0)
return SGSN_AUTH_ACCEPTED;
}
if (check_acl && sgsn_acl_lookup(mmctx->imsi, &sgsn->cfg))
return SGSN_AUTH_ACCEPTED;
return SGSN_AUTH_REJECTED;
}
/*
* This function is directly called by e.g. the GMM layer. It returns either
* after calling sgsn_auth_update directly or after triggering an asynchronous
* procedure which will call sgsn_auth_update later on.
*/
int sgsn_auth_request(struct sgsn_mm_ctx *mmctx)
{
struct gprs_subscr *subscr;
struct gsm_auth_tuple *at;
int need_update_location;
int rc;
LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting authorization\n");
if (sgsn->cfg.auth_policy != SGSN_AUTH_POLICY_REMOTE) {
sgsn_auth_update(mmctx);
return 0;
}
need_update_location = sgsn->cfg.require_update_location &&
(mmctx->subscr == NULL ||
mmctx->pending_req == GSM48_MT_GMM_ATTACH_REQ);
/* This has the side effect of registering the subscr with the mmctx */
subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
gprs_subscr_put(subscr);
OSMO_ASSERT(mmctx->subscr != NULL);
if (sgsn->cfg.require_authentication && !mmctx->is_authenticated) {
/* Find next tuple */
at = sgsn_auth_get_tuple(mmctx, mmctx->auth_triplet.key_seq);
if (!at) {
/* No valid tuple found, request fresh ones */
mmctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
LOGMMCTXP(LOGL_INFO, mmctx,
"Requesting authentication tuples\n");
rc = gprs_subscr_request_auth_info(mmctx, NULL, NULL);
if (rc >= 0)
return 0;
return rc;
}
mmctx->auth_triplet = *at;
} else if (need_update_location) {
LOGMMCTXP(LOGL_INFO, mmctx,
"Missing information, requesting subscriber data\n");
rc = gprs_subscr_request_update_location(mmctx);
if (rc >= 0)
return 0;
return rc;
}
sgsn_auth_update(mmctx);
return 0;
}
void sgsn_auth_update(struct sgsn_mm_ctx *mmctx)
{
enum sgsn_auth_state auth_state;
struct gprs_subscr *subscr = mmctx->subscr;
struct gsm_auth_tuple *at;
int gmm_cause;
auth_state = sgsn_auth_state(mmctx);
LOGMMCTXP(LOGL_DEBUG, mmctx, "Updating authorization (%s -> %s)\n",
get_value_string(sgsn_auth_state_names, mmctx->auth_state),
get_value_string(sgsn_auth_state_names, auth_state));
if (auth_state == SGSN_AUTH_UNKNOWN && subscr &&
!(subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING_MASK)) {
/* Reject requests if gprs_subscr_request_update_location fails */
LOGMMCTXP(LOGL_ERROR, mmctx,
"Missing information, authorization not possible\n");
auth_state = SGSN_AUTH_REJECTED;
}
if (auth_state == SGSN_AUTH_AUTHENTICATE &&
mmctx->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) {
/* The current tuple is not valid, but we are possibly called
* because new auth tuples have been received */
at = sgsn_auth_get_tuple(mmctx, mmctx->auth_triplet.key_seq);
if (!at) {
LOGMMCTXP(LOGL_ERROR, mmctx,
"Missing auth tuples, authorization not possible\n");
auth_state = SGSN_AUTH_REJECTED;
} else {
mmctx->auth_triplet = *at;
}
}
if (mmctx->auth_state == auth_state)
return;
LOGMMCTXP(LOGL_INFO, mmctx, "Got authorization update: state %s -> %s\n",
get_value_string(sgsn_auth_state_names, mmctx->auth_state),
get_value_string(sgsn_auth_state_names, auth_state));
mmctx->auth_state = auth_state;
switch (auth_state) {
case SGSN_AUTH_AUTHENTICATE:
if (subscr)
subscr->sgsn_data->auth_triplets_updated = 0;
gsm0408_gprs_authenticate(mmctx);
break;
case SGSN_AUTH_ACCEPTED:
gsm0408_gprs_access_granted(mmctx);
break;
case SGSN_AUTH_REJECTED:
gmm_cause =
subscr ? subscr->sgsn_data->error_cause :
SGSN_ERROR_CAUSE_NONE;
if (subscr && (subscr->flags & GPRS_SUBSCRIBER_CANCELLED) != 0)
gsm0408_gprs_access_cancelled(mmctx, gmm_cause);
else
gsm0408_gprs_access_denied(mmctx, gmm_cause);
break;
default:
break;
}
}
struct gsm_auth_tuple *sgsn_auth_get_tuple(struct sgsn_mm_ctx *mmctx,
unsigned key_seq)
{
unsigned count;
unsigned idx;
struct gsm_auth_tuple *at = NULL;
struct sgsn_subscriber_data *sdata;
if (!mmctx->subscr)
return NULL;
if (key_seq == GSM_KEY_SEQ_INVAL)
/* Start with 0 after increment module array size */
idx = ARRAY_SIZE(sdata->auth_triplets) - 1;
else
idx = key_seq;
sdata = mmctx->subscr->sgsn_data;
/* Find next tuple */
for (count = ARRAY_SIZE(sdata->auth_triplets); count > 0; count--) {
idx = (idx + 1) % ARRAY_SIZE(sdata->auth_triplets);
if (sdata->auth_triplets[idx].key_seq == GSM_KEY_SEQ_INVAL)
continue;
if (sdata->auth_triplets[idx].use_count == 0) {
at = &sdata->auth_triplets[idx];
at->use_count = 1;
return at;
}
}
return NULL;
}

View File

@ -1,259 +0,0 @@
/* GPRS SGSN CDR dumper */
/* (C) 2015 by Holger Hans Peter Freyther
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <openbsc/sgsn.h>
#include <openbsc/signal.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/debug.h>
#include <osmocom/gsm/apn.h>
#include <openbsc/vty.h>
#include <gtp.h>
#include <pdp.h>
#include <arpa/inet.h>
#include <time.h>
#include <stdio.h>
#include <inttypes.h>
/* TODO...avoid going through a global */
extern struct sgsn_instance *sgsn;
/**
* The CDR module will generate an entry like:
*
* IMSI, # Subscriber IMSI
* IMEI, # Subscriber IMEI
* MSISDN, # Subscriber MISDN
* Charging_Timestamp, # Event start Time
* Charging_UTC, # Time zone of event start time
* Duration, # Session DURATION
* Cell_Id, # CELL_ID
* Location_Area, # LAC
* GGSN_ADDR, # GGSN_ADDR
* SGSN_ADDR, # SGSN_ADDR
* APNI, # APNI
* PDP_ADDR, # PDP_ADDR
* VOL_IN, # VOL_IN in Bytes
* VOL_OUT, # VOL_OUT in Bytes
* CAUSE_FOR_TERM, # CAUSE_FOR_TERM
*/
static void maybe_print_header(FILE *cdr_file)
{
if (ftell(cdr_file) != 0)
return;
fprintf(cdr_file, "timestamp,imsi,imei,msisdn,cell_id,lac,hlr,event,pdp_duration,ggsn_addr,sgsn_addr,apni,eua_addr,vol_in,vol_out,charging_id\n");
}
static void cdr_log_mm(struct sgsn_instance *inst, const char *ev,
struct sgsn_mm_ctx *mmctx)
{
FILE *cdr_file;
struct tm tm;
struct timeval tv;
if (!inst->cfg.cdr.filename)
return;
cdr_file = fopen(inst->cfg.cdr.filename, "a");
if (!cdr_file) {
LOGP(DGPRS, LOGL_ERROR, "Failed to open %s\n",
inst->cfg.cdr.filename);
return;
}
maybe_print_header(cdr_file);
gettimeofday(&tv, NULL);
gmtime_r(&tv.tv_sec, &tm);
fprintf(cdr_file, "%04d%02d%02d%02d%02d%02d%03d,%s,%s,%s,%d,%d,%s,%s\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
(int)(tv.tv_usec / 1000),
mmctx->imsi,
mmctx->imei,
mmctx->msisdn,
mmctx->gb.cell_id,
mmctx->ra.lac,
mmctx->hlr,
ev);
fclose(cdr_file);
}
static void extract_eua(struct ul66_t *eua, char *eua_addr)
{
if (eua->l < 2)
return;
/* there is no addr for ETSI/PPP */
if ((eua->v[0] & 0x0F) != 1) {
strcpy(eua_addr, "ETSI");
return;
}
if (eua->v[1] == 0x21 && eua->l == 6)
inet_ntop(AF_INET, &eua->v[2], eua_addr, INET_ADDRSTRLEN);
else if (eua->v[1] == 0x57 && eua->l == 18)
inet_ntop(AF_INET6, &eua->v[2], eua_addr, INET6_ADDRSTRLEN);
else {
/* e.g. both IPv4 and IPv6 */
strcpy(eua_addr, "Unknown address");
}
}
static void cdr_log_pdp(struct sgsn_instance *inst, const char *ev,
struct sgsn_pdp_ctx *pdp)
{
FILE *cdr_file;
char apni[(pdp->lib ? pdp->lib->apn_use.l : 0) + 1];
char ggsn_addr[INET_ADDRSTRLEN + 1];
char sgsn_addr[INET_ADDRSTRLEN + 1];
char eua_addr[INET6_ADDRSTRLEN + 1];
struct tm tm;
struct timeval tv;
time_t duration;
struct timespec tp;
if (!inst->cfg.cdr.filename)
return;
memset(apni, 0, sizeof(apni));
memset(ggsn_addr, 0, sizeof(ggsn_addr));
memset(eua_addr, 0, sizeof(eua_addr));
if (pdp->lib) {
osmo_apn_to_str(apni, pdp->lib->apn_use.v, pdp->lib->apn_use.l);
inet_ntop(AF_INET, &pdp->lib->hisaddr0.s_addr, ggsn_addr, sizeof(ggsn_addr));
extract_eua(&pdp->lib->eua, eua_addr);
}
if (pdp->ggsn)
inet_ntop(AF_INET, &pdp->ggsn->gsn->gsnc.s_addr, sgsn_addr, sizeof(sgsn_addr));
cdr_file = fopen(inst->cfg.cdr.filename, "a");
if (!cdr_file) {
LOGP(DGPRS, LOGL_ERROR, "Failed to open %s\n",
inst->cfg.cdr.filename);
return;
}
maybe_print_header(cdr_file);
clock_gettime(CLOCK_MONOTONIC, &tp);
gettimeofday(&tv, NULL);
/* convert the timestamp to UTC */
gmtime_r(&tv.tv_sec, &tm);
/* Check the duration of the PDP context */
duration = tp.tv_sec - pdp->cdr_start.tv_sec;
fprintf(cdr_file,
"%04d%02d%02d%02d%02d%02d%03d,%s,%s,%s,%d,%d,%s,%s,%ld,%s,%s,%s,%s,%" PRIu64 ",%" PRIu64 ",%u\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
(int)(tv.tv_usec / 1000),
pdp->mm ? pdp->mm->imsi : "N/A",
pdp->mm ? pdp->mm->imei : "N/A",
pdp->mm ? pdp->mm->msisdn : "N/A",
pdp->mm ? pdp->mm->gb.cell_id : -1,
pdp->mm ? pdp->mm->ra.lac : -1,
pdp->mm ? pdp->mm->hlr : "N/A",
ev,
(unsigned long ) duration,
ggsn_addr,
sgsn_addr,
apni,
eua_addr,
pdp->cdr_bytes_in,
pdp->cdr_bytes_out,
pdp->cdr_charging_id);
fclose(cdr_file);
}
static void cdr_pdp_timeout(void *_data)
{
struct sgsn_pdp_ctx *pdp = _data;
cdr_log_pdp(sgsn, "pdp-periodic", pdp);
osmo_timer_schedule(&pdp->cdr_timer, sgsn->cfg.cdr.interval, 0);
}
static int handle_sgsn_sig(unsigned int subsys, unsigned int signal,
void *handler_data, void *_signal_data)
{
struct sgsn_signal_data *signal_data = _signal_data;
struct sgsn_instance *inst = handler_data;
if (subsys != SS_SGSN)
return 0;
switch (signal) {
case S_SGSN_ATTACH:
cdr_log_mm(inst, "attach", signal_data->mm);
break;
case S_SGSN_UPDATE:
cdr_log_mm(inst, "update", signal_data->mm);
break;
case S_SGSN_DETACH:
cdr_log_mm(inst, "detach", signal_data->mm);
break;
case S_SGSN_MM_FREE:
cdr_log_mm(inst, "free", signal_data->mm);
break;
case S_SGSN_PDP_ACT:
clock_gettime(CLOCK_MONOTONIC, &signal_data->pdp->cdr_start);
signal_data->pdp->cdr_charging_id = signal_data->pdp->lib->cid;
cdr_log_pdp(inst, "pdp-act", signal_data->pdp);
osmo_timer_setup(&signal_data->pdp->cdr_timer, cdr_pdp_timeout,
signal_data->pdp);
osmo_timer_schedule(&signal_data->pdp->cdr_timer, inst->cfg.cdr.interval, 0);
break;
case S_SGSN_PDP_DEACT:
cdr_log_pdp(inst, "pdp-deact", signal_data->pdp);
osmo_timer_del(&signal_data->pdp->cdr_timer);
break;
case S_SGSN_PDP_TERMINATE:
cdr_log_pdp(inst, "pdp-terminate", signal_data->pdp);
osmo_timer_del(&signal_data->pdp->cdr_timer);
break;
case S_SGSN_PDP_FREE:
cdr_log_pdp(inst, "pdp-free", signal_data->pdp);
osmo_timer_del(&signal_data->pdp->cdr_timer);
break;
}
return 0;
}
int sgsn_cdr_init(struct sgsn_instance *sgsn)
{
/* register for CDR related events */
sgsn->cfg.cdr.interval = 10 * 60;
osmo_signal_register_handler(SS_SGSN, handle_sgsn_sig, sgsn);
return 0;
}

View File

@ -1,69 +0,0 @@
/* Control Interface Implementation for the SGSN */
/*
* (C) 2014 by Holger Hans Peter Freyther
* (C) 2014 by sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/ctrl/control_if.h>
#include <osmocom/ctrl/control_cmd.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/sgsn.h>
#include <openbsc/debug.h>
#include <pdp.h>
extern vector ctrl_node_vec;
static int get_subscriber_list(struct ctrl_cmd *cmd, void *d)
{
struct sgsn_mm_ctx *mm;
cmd->reply = talloc_strdup(cmd, "");
llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
char *addr = NULL;
struct sgsn_pdp_ctx *pdp;
if (strlen(mm->imsi) == 0)
continue;
llist_for_each_entry(pdp, &mm->pdp_list, list)
addr = gprs_pdpaddr2str(pdp->lib->eua.v,
pdp->lib->eua.l);
cmd->reply = talloc_asprintf_append(
cmd->reply,
"%s,%s\n", mm->imsi, addr ? addr : "");
}
return CTRL_CMD_REPLY;
}
CTRL_CMD_DEFINE_RO(subscriber_list, "subscriber-list-active-v1");
int sgsn_ctrl_cmds_install(void)
{
int rc = 0;
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_list);
return rc;
}
struct ctrl_handle *sgsn_controlif_setup(struct gsm_network *net,
const char *bind_addr, uint16_t port)
{
return ctrl_interface_setup_dynip(net, bind_addr, port, NULL);
}

View File

@ -1,885 +0,0 @@
/* GPRS SGSN integration with libgtp of OpenGGSN */
/* libgtp implements the GPRS Tunelling Protocol GTP per TS 09.60 / 29.060 */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010 by On-Waves
* (C) 2015 by Holger Hans Peter Freyther
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <signal.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "bscconfig.h"
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <openbsc/signal.h>
#include <openbsc/debug.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/gprs_subscriber.h>
#include <openbsc/gprs_sndcp.h>
#ifdef BUILD_IU
#include <osmocom/ranap/iu_client.h>
#include <osmocom/ranap/ranap_ies_defs.h>
#endif
#include <gtp.h>
#include <pdp.h>
/* TS 23.003: The MSISDN shall take the dummy MSISDN value composed of
* 15 digits set to 0 (encoded as an E.164 international number) when
* the MSISDN is not available in messages in which the presence of the
* MSISDN parameter */
static const uint8_t dummy_msisdn[] =
{ 0x91, /* No extension, international, E.164 */
0, 0, 0, 0, 0, 0, 0, /* 14 digits of zeroes */
0xF0 /* 15th digit of zero + padding */ };
const struct value_string gtp_cause_strs[] = {
{ GTPCAUSE_REQ_IMSI, "Request IMSI" },
{ GTPCAUSE_REQ_IMEI, "Request IMEI" },
{ GTPCAUSE_REQ_IMSI_IMEI, "Request IMSI and IMEI" },
{ GTPCAUSE_NO_ID_NEEDED, "No identity needed" },
{ GTPCAUSE_MS_REFUSES_X, "MS refuses" },
{ GTPCAUSE_MS_NOT_RESP_X, "MS is not GPRS responding" },
{ GTPCAUSE_ACC_REQ, "Request accepted" },
{ GTPCAUSE_NON_EXIST, "Non-existent" },
{ GTPCAUSE_INVALID_MESSAGE, "Invalid message format" },
{ GTPCAUSE_IMSI_NOT_KNOWN, "IMSI not known" },
{ GTPCAUSE_MS_DETACHED, "MS is GPRS detached" },
{ GTPCAUSE_MS_NOT_RESP, "MS is not GPRS responding" },
{ GTPCAUSE_MS_REFUSES, "MS refuses" },
{ GTPCAUSE_NO_RESOURCES, "No resources available" },
{ GTPCAUSE_NOT_SUPPORTED, "Service not supported" },
{ GTPCAUSE_MAN_IE_INCORRECT, "Mandatory IE incorrect" },
{ GTPCAUSE_MAN_IE_MISSING, "Mandatory IE missing" },
{ GTPCAUSE_OPT_IE_INCORRECT, "Optional IE incorrect" },
{ GTPCAUSE_SYS_FAIL, "System failure" },
{ GTPCAUSE_ROAMING_REST, "Roaming restrictions" },
{ GTPCAUSE_PTIMSI_MISMATCH, "P-TMSI Signature mismatch" },
{ GTPCAUSE_CONN_SUSP, "GPRS connection suspended" },
{ GTPCAUSE_AUTH_FAIL, "Authentication failure" },
{ GTPCAUSE_USER_AUTH_FAIL, "User authentication failed" },
{ GTPCAUSE_CONTEXT_NOT_FOUND, "Context not found" },
{ GTPCAUSE_ADDR_OCCUPIED, "All dynamic PDP addresses occupied" },
{ GTPCAUSE_NO_MEMORY, "No memory is available" },
{ GTPCAUSE_RELOC_FAIL, "Relocation failure" },
{ GTPCAUSE_UNKNOWN_MAN_EXTHEADER, "Unknown mandatory ext. header" },
{ GTPCAUSE_SEM_ERR_TFT, "Semantic error in TFT operation" },
{ GTPCAUSE_SYN_ERR_TFT, "Syntactic error in TFT operation" },
{ GTPCAUSE_SEM_ERR_FILTER, "Semantic errors in packet filter" },
{ GTPCAUSE_SYN_ERR_FILTER, "Syntactic errors in packet filter" },
{ GTPCAUSE_MISSING_APN, "Missing or unknown APN" },
{ GTPCAUSE_UNKNOWN_PDP, "Unknown PDP address or PDP type" },
{ 0, NULL }
};
/* Generate the GTP IMSI IE according to 09.60 Section 7.9.2 */
static uint64_t imsi_str2gtp(char *str)
{
uint64_t imsi64 = 0;
unsigned int n;
unsigned int imsi_len = strlen(str);
if (imsi_len > 16) {
LOGP(DGPRS, LOGL_NOTICE, "IMSI length > 16 not supported!\n");
return 0;
}
for (n = 0; n < 16; n++) {
uint64_t val;
if (n < imsi_len)
val = (str[n]-'0') & 0xf;
else
val = 0xf;
imsi64 |= (val << (n*4));
}
return imsi64;
}
/* generate a PDP context based on the IE's from the 04.08 message,
* and send the GTP create pdp context request to the GGSN */
struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
struct sgsn_mm_ctx *mmctx,
uint16_t nsapi,
struct tlv_parsed *tp)
{
struct gprs_ra_id raid;
struct sgsn_pdp_ctx *pctx;
struct pdp_t *pdp;
uint64_t imsi_ui64;
size_t qos_len;
const uint8_t *qos;
int rc;
LOGP(DGPRS, LOGL_ERROR, "Create PDP Context\n");
pctx = sgsn_pdp_ctx_alloc(mmctx, nsapi);
if (!pctx) {
LOGP(DGPRS, LOGL_ERROR, "Couldn't allocate PDP Ctx\n");
return NULL;
}
imsi_ui64 = imsi_str2gtp(mmctx->imsi);
rc = pdp_newpdp(&pdp, imsi_ui64, nsapi, NULL);
if (rc) {
LOGP(DGPRS, LOGL_ERROR, "Out of libgtp PDP Contexts\n");
return NULL;
}
pdp->priv = pctx;
pctx->lib = pdp;
pctx->ggsn = ggsn;
//pdp->peer = /* sockaddr_in of GGSN (receive) */
//pdp->ipif = /* not used by library */
pdp->version = ggsn->gtp_version;
pdp->hisaddr0 = ggsn->remote_addr;
pdp->hisaddr1 = ggsn->remote_addr;
//pdp->cch_pdp = 512; /* Charging Flat Rate */
/* MS provided APN, subscription was verified by the caller */
pdp->selmode = 0xFC | 0x00;
/* IMSI, TEID/TEIC, FLLU/FLLC, TID, NSAPI set in pdp_newpdp */
/* Put the MSISDN in case we have it */
if (mmctx->subscr && mmctx->subscr->sgsn_data->msisdn_len) {
pdp->msisdn.l = mmctx->subscr->sgsn_data->msisdn_len;
if (pdp->msisdn.l > sizeof(pdp->msisdn.v))
pdp->msisdn.l = sizeof(pdp->msisdn.v);
memcpy(pdp->msisdn.v, mmctx->subscr->sgsn_data->msisdn,
pdp->msisdn.l);
} else {
/* use the dummy 15-digits-zero MSISDN value */
pdp->msisdn.l = sizeof(dummy_msisdn);
memcpy(pdp->msisdn.v, dummy_msisdn, pdp->msisdn.l);
}
/* End User Address from GMM requested PDP address */
pdp->eua.l = TLVP_LEN(tp, OSMO_IE_GSM_REQ_PDP_ADDR);
if (pdp->eua.l > sizeof(pdp->eua.v))
pdp->eua.l = sizeof(pdp->eua.v);
memcpy(pdp->eua.v, TLVP_VAL(tp, OSMO_IE_GSM_REQ_PDP_ADDR),
pdp->eua.l);
/* Highest 4 bits of first byte need to be set to 1, otherwise
* the IE is identical with the 04.08 PDP Address IE */
pdp->eua.v[0] |= 0xf0;
/* APN name from GMM */
pdp->apn_use.l = TLVP_LEN(tp, GSM48_IE_GSM_APN);
if (pdp->apn_use.l > sizeof(pdp->apn_use.v))
pdp->apn_use.l = sizeof(pdp->apn_use.v);
memcpy(pdp->apn_use.v, TLVP_VAL(tp, GSM48_IE_GSM_APN),
pdp->apn_use.l);
/* Protocol Configuration Options from GMM */
pdp->pco_req.l = TLVP_LEN(tp, GSM48_IE_GSM_PROTO_CONF_OPT);
if (pdp->pco_req.l > sizeof(pdp->pco_req.v))
pdp->pco_req.l = sizeof(pdp->pco_req.v);
memcpy(pdp->pco_req.v, TLVP_VAL(tp, GSM48_IE_GSM_PROTO_CONF_OPT),
pdp->pco_req.l);
/* QoS options from GMM or remote */
if (TLVP_LEN(tp, OSMO_IE_GSM_SUB_QOS) > 0) {
qos_len = TLVP_LEN(tp, OSMO_IE_GSM_SUB_QOS);
qos = TLVP_VAL(tp, OSMO_IE_GSM_SUB_QOS);
} else {
qos_len = TLVP_LEN(tp, OSMO_IE_GSM_REQ_QOS);
qos = TLVP_VAL(tp, OSMO_IE_GSM_REQ_QOS);
}
if (qos_len <= 3) {
pdp->qos_req.l = qos_len + 1;
if (pdp->qos_req.l > sizeof(pdp->qos_req.v))
pdp->qos_req.l = sizeof(pdp->qos_req.v);
pdp->qos_req.v[0] = 0; /* Allocation/Retention policy */
memcpy(&pdp->qos_req.v[1], qos, pdp->qos_req.l - 1);
} else {
pdp->qos_req.l = qos_len;
if (pdp->qos_req.l > sizeof(pdp->qos_req.v))
pdp->qos_req.l = sizeof(pdp->qos_req.v);
memcpy(pdp->qos_req.v, qos, pdp->qos_req.l);
}
/* charging characteristics if present */
if (TLVP_LEN(tp, OSMO_IE_GSM_CHARG_CHAR) >= sizeof(pdp->cch_pdp))
pdp->cch_pdp = tlvp_val16be(tp, OSMO_IE_GSM_CHARG_CHAR);
/* SGSN address for control plane */
pdp->gsnlc.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr);
memcpy(pdp->gsnlc.v, &sgsn->cfg.gtp_listenaddr.sin_addr,
sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
/* SGSN address for user plane
* Default to the control plane addr for now. If we are connected to a
* hnbgw via IuPS we'll need to send a PDP context update with the
* correct IP address after the RAB Assignment is complete */
pdp->gsnlu.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr);
memcpy(pdp->gsnlu.v, &sgsn->cfg.gtp_listenaddr.sin_addr,
sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
/* Encode RAT Type according to TS 29.060 7.7.50 */
pdp->rattype.l = 1;
if (mmctx->ran_type == MM_CTX_T_UTRAN_Iu)
pdp->rattype.v[0] = 1;
else
pdp->rattype.v[0] = 2;
pdp->rattype_given = 1;
/* Include RAI and ULI all the time */
pdp->rai_given = 1;
pdp->rai.l = 6;
/* Routing Area Identifier with LAC and RAC fixed values, as
* requested in 29.006 7.3.1 */
raid = mmctx->ra;
raid.lac = 0xFFFE;
raid.rac = 0xFF;
gsm48_construct_ra(pdp->rai.v, &raid);
/* Encode User Location Information accordint to TS 29.060 7.7.51 */
pdp->userloc_given = 1;
pdp->userloc.l = 8;
switch (mmctx->ran_type) {
case MM_CTX_T_GERAN_Gb:
case MM_CTX_T_GERAN_Iu:
pdp->rattype.v[0] = 2;
/* User Location Information */
pdp->userloc_given = 1;
pdp->userloc.l = 8;
pdp->userloc.v[0] = 0; /* CGI for GERAN */
bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->gb.cell_id);
break;
case MM_CTX_T_UTRAN_Iu:
pdp->userloc.v[0] = 1; /* SAI for UTRAN */
/* SAI is like CGI but with SAC instead of CID, so we can abuse this function */
bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->iu.sac);
break;
}
/* include the IMEI(SV) */
pdp->imeisv_given = 1;
gsm48_encode_bcd_number(&pdp->imeisv.v[0], 8, 0, mmctx->imei);
pdp->imeisv.l = pdp->imeisv.v[0];
memmove(&pdp->imeisv.v[0], &pdp->imeisv.v[1], 8);
/* change pdp state to 'requested' */
pctx->state = PDP_STATE_CR_REQ;
rc = gtp_create_context_req(ggsn->gsn, pdp, pctx);
/* FIXME */
return pctx;
}
/* SGSN wants to delete a PDP context */
int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx)
{
LOGPDPCTXP(LOGL_ERROR, pctx, "Delete PDP Context\n");
/* FIXME: decide if we need teardown or not ! */
return gtp_delete_context_req(pctx->ggsn->gsn, pctx->lib, pctx, 1);
}
struct cause_map {
uint8_t cause_in;
uint8_t cause_out;
};
static uint8_t cause_map(const struct cause_map *map, uint8_t in, uint8_t deflt)
{
const struct cause_map *m;
for (m = map; m->cause_in && m->cause_out; m++) {
if (m->cause_in == in)
return m->cause_out;
}
return deflt;
}
/* how do we map from gtp cause to SM cause */
static const struct cause_map gtp2sm_cause_map[] = {
{ GTPCAUSE_NO_RESOURCES, GSM_CAUSE_INSUFF_RSRC },
{ GTPCAUSE_NOT_SUPPORTED, GSM_CAUSE_SERV_OPT_NOTSUPP },
{ GTPCAUSE_MAN_IE_INCORRECT, GSM_CAUSE_INV_MAND_INFO },
{ GTPCAUSE_MAN_IE_MISSING, GSM_CAUSE_INV_MAND_INFO },
{ GTPCAUSE_OPT_IE_INCORRECT, GSM_CAUSE_PROTO_ERR_UNSPEC },
{ GTPCAUSE_SYS_FAIL, GSM_CAUSE_NET_FAIL },
{ GTPCAUSE_ROAMING_REST, GSM_CAUSE_REQ_SERV_OPT_NOTSUB },
{ GTPCAUSE_PTIMSI_MISMATCH, GSM_CAUSE_PROTO_ERR_UNSPEC },
{ GTPCAUSE_CONN_SUSP, GSM_CAUSE_PROTO_ERR_UNSPEC },
{ GTPCAUSE_AUTH_FAIL, GSM_CAUSE_AUTH_FAILED },
{ GTPCAUSE_USER_AUTH_FAIL, GSM_CAUSE_ACT_REJ_GGSN },
{ GTPCAUSE_CONTEXT_NOT_FOUND, GSM_CAUSE_PROTO_ERR_UNSPEC },
{ GTPCAUSE_ADDR_OCCUPIED, GSM_CAUSE_INSUFF_RSRC },
{ GTPCAUSE_NO_MEMORY, GSM_CAUSE_INSUFF_RSRC },
{ GTPCAUSE_RELOC_FAIL, GSM_CAUSE_PROTO_ERR_UNSPEC },
{ GTPCAUSE_UNKNOWN_MAN_EXTHEADER, GSM_CAUSE_PROTO_ERR_UNSPEC },
{ GTPCAUSE_MISSING_APN, GSM_CAUSE_MISSING_APN },
{ GTPCAUSE_UNKNOWN_PDP, GSM_CAUSE_UNKNOWN_PDP },
{ 0, 0 }
};
static int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx)
{
struct sgsn_signal_data sig_data;
int rc;
struct gprs_llc_lle *lle;
/* Inform others about it */
memset(&sig_data, 0, sizeof(sig_data));
sig_data.pdp = pctx;
osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_ACT, &sig_data);
/* Send PDP CTX ACT to MS */
rc = gsm48_tx_gsm_act_pdp_acc(pctx);
if (rc < 0)
return rc;
if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) {
/* Send SNDCP XID to MS */
lle = &pctx->mm->gb.llme->lle[pctx->sapi];
rc = sndcp_sn_xid_req(lle,pctx->nsapi);
if (rc < 0)
return rc;
}
return 0;
}
/* The GGSN has confirmed the creation of a PDP Context */
static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
{
struct sgsn_pdp_ctx *pctx = cbp;
uint8_t reject_cause;
LOGPDPCTXP(LOGL_INFO, pctx, "Received CREATE PDP CTX CONF, cause=%d(%s)\n",
cause, get_value_string(gtp_cause_strs, cause));
if (!pctx->mm) {
LOGP(DGPRS, LOGL_INFO,
"No MM context, aborting CREATE PDP CTX CONF\n");
return -EIO;
}
/* Check for cause value if it was really successful */
if (cause < 0) {
LOGP(DGPRS, LOGL_NOTICE, "Create PDP ctx req timed out\n");
if (pdp && pdp->version == 1) {
pdp->version = 0;
gtp_create_context_req(sgsn->gsn, pdp, cbp);
return 0;
} else {
reject_cause = GSM_CAUSE_NET_FAIL;
goto reject;
}
}
/* Check for cause value if it was really successful */
if (cause != GTPCAUSE_ACC_REQ) {
reject_cause = cause_map(gtp2sm_cause_map, cause,
GSM_CAUSE_ACT_REJ_GGSN);
goto reject;
}
if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) {
/* Activate the SNDCP layer */
sndcp_sm_activate_ind(&pctx->mm->gb.llme->lle[pctx->sapi], pctx->nsapi);
return send_act_pdp_cont_acc(pctx);
} else if (pctx->mm->ran_type == MM_CTX_T_UTRAN_Iu) {
#ifdef BUILD_IU
/* Activate a radio bearer */
iu_rab_act_ps(pdp->nsapi, pctx);
return 0;
#else
return -ENOTSUP;
#endif
}
LOGP(DGPRS, LOGL_ERROR, "Unknown ran_type %d\n",
pctx->mm->ran_type);
reject_cause = GSM_CAUSE_PROTO_ERR_UNSPEC;
reject:
/*
* In case of a timeout pdp will be NULL but we have a valid pointer
* in pctx->lib. For other rejects pctx->lib and pdp might be the
* same.
*/
pctx->state = PDP_STATE_NONE;
if (pctx->lib && pctx->lib != pdp)
pdp_freepdp(pctx->lib);
pctx->lib = NULL;
if (pdp)
pdp_freepdp(pdp);
/* Send PDP CTX ACT REJ to MS */
gsm48_tx_gsm_act_pdp_rej(pctx->mm, pctx->ti, reject_cause,
0, NULL);
sgsn_pdp_ctx_free(pctx);
return EOF;
}
void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen)
{
pdp->lib->gsnlu.l = alen;
memcpy(pdp->lib->gsnlu.v, addr, alen);
gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0);
}
#ifdef BUILD_IU
/* Callback for RAB assignment response */
int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies)
{
uint8_t rab_id;
bool require_pdp_update = false;
struct sgsn_pdp_ctx *pdp = NULL;
RANAP_RAB_SetupOrModifiedItem_t *item = &setup_ies->raB_SetupOrModifiedItem;
rab_id = item->rAB_ID.buf[0];
pdp = sgsn_pdp_ctx_by_nsapi(ctx, rab_id);
if (!pdp) {
LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Response for unknown RAB/NSAPI=%u\n", rab_id);
return -1;
}
if (item->transportLayerAddress) {
LOGPC(DRANAP, LOGL_INFO, " Setup: (%u/%s)", rab_id, osmo_hexdump(item->transportLayerAddress->buf,
item->transportLayerAddress->size));
switch (item->transportLayerAddress->size) {
case 7:
/* It must be IPv4 inside a X213 NSAP */
memcpy(pdp->lib->gsnlu.v, &item->transportLayerAddress->buf[3], 4);
break;
case 4:
/* It must be a raw IPv4 address */
memcpy(pdp->lib->gsnlu.v, item->transportLayerAddress->buf, 4);
break;
case 16:
/* TODO: It must be a raw IPv6 address */
case 19:
/* TODO: It must be IPv6 inside a X213 NSAP */
default:
LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Resp: Unknown "
"transport layer address size %u\n",
item->transportLayerAddress->size);
return -1;
}
require_pdp_update = true;
}
/* The TEI on the RNC side might have changed, too */
if (item->iuTransportAssociation &&
item->iuTransportAssociation->present == RANAP_IuTransportAssociation_PR_gTP_TEI &&
item->iuTransportAssociation->choice.gTP_TEI.buf &&
item->iuTransportAssociation->choice.gTP_TEI.size >= 4) {
uint32_t tei = osmo_load32be(item->iuTransportAssociation->choice.gTP_TEI.buf);
LOGP(DRANAP, LOGL_DEBUG, "Updating TEID on RNC side from 0x%08x to 0x%08x\n",
pdp->lib->teid_own, tei);
pdp->lib->teid_own = tei;
require_pdp_update = true;
}
if (require_pdp_update)
gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0);
if (pdp->state != PDP_STATE_CR_CONF) {
send_act_pdp_cont_acc(pdp);
pdp->state = PDP_STATE_CR_CONF;
}
return 0;
}
#endif
/* Confirmation of a PDP Context Delete */
static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
{
struct sgsn_signal_data sig_data;
struct sgsn_pdp_ctx *pctx = cbp;
int rc = 0;
LOGPDPCTXP(LOGL_INFO, pctx, "Received DELETE PDP CTX CONF, cause=%d(%s)\n",
cause, get_value_string(gtp_cause_strs, cause));
memset(&sig_data, 0, sizeof(sig_data));
sig_data.pdp = pctx;
osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_DEACT, &sig_data);
if (pctx->mm) {
if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) {
/* Deactivate the SNDCP layer */
sndcp_sm_deactivate_ind(&pctx->mm->gb.llme->lle[pctx->sapi], pctx->nsapi);
} else {
#ifdef BUILD_IU
/* Deactivate radio bearer */
ranap_iu_rab_deact(pctx->mm->iu.ue_ctx, 1);
#else
return -ENOTSUP;
#endif
}
/* Confirm deactivation of PDP context to MS */
rc = gsm48_tx_gsm_deact_pdp_acc(pctx);
} else {
LOGPDPCTXP(LOGL_NOTICE, pctx,
"Not deactivating SNDCP layer since the MM context "
"is not available\n");
}
/* unlink the now non-existing library handle from the pdp
* context */
pctx->lib = NULL;
sgsn_pdp_ctx_free(pctx);
return rc;
}
/* Confirmation of an GTP ECHO request */
static int echo_conf(struct pdp_t *pdp, void *cbp, int recovery)
{
if (recovery < 0) {
LOGP(DGPRS, LOGL_NOTICE, "GTP Echo Request timed out\n");
/* FIXME: if version == 1, retry with version 0 */
} else {
DEBUGP(DGPRS, "GTP Rx Echo Response\n");
}
return 0;
}
/* Any message received by GGSN contains a recovery IE */
static int cb_recovery(struct sockaddr_in *peer, uint8_t recovery)
{
struct sgsn_ggsn_ctx *ggsn;
ggsn = sgsn_ggsn_ctx_by_addr(&peer->sin_addr);
if (!ggsn) {
LOGP(DGPRS, LOGL_NOTICE, "Received Recovery IE for unknown GGSN\n");
return -EINVAL;
}
if (ggsn->remote_restart_ctr == -1) {
/* First received ECHO RESPONSE, note the restart ctr */
ggsn->remote_restart_ctr = recovery;
} else if (ggsn->remote_restart_ctr != recovery) {
/* counter has changed (GGSN restart): release all PDP */
LOGP(DGPRS, LOGL_NOTICE, "GGSN recovery (%u->%u), "
"releasing all PDP contexts\n",
ggsn->remote_restart_ctr, recovery);
ggsn->remote_restart_ctr = recovery;
drop_all_pdp_for_ggsn(ggsn);
}
return 0;
}
/* libgtp callback for confirmations */
static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp)
{
DEBUGP(DGPRS, "libgtp cb_conf(type=%d, cause=%d, pdp=%p, cbp=%p)\n",
type, cause, pdp, cbp);
if (cause == EOF)
LOGP(DGPRS, LOGL_ERROR, "libgtp EOF (type=%u, pdp=%p, cbp=%p)\n",
type, pdp, cbp);
switch (type) {
case GTP_ECHO_REQ:
/* libgtp hands us the RECOVERY number instead of a cause */
return echo_conf(pdp, cbp, cause);
case GTP_CREATE_PDP_REQ:
return create_pdp_conf(pdp, cbp, cause);
case GTP_DELETE_PDP_REQ:
return delete_pdp_conf(pdp, cbp, cause);
default:
break;
}
return 0;
}
/* Called whenever a PDP context is deleted for any reason */
static int cb_delete_context(struct pdp_t *pdp)
{
LOGP(DGPRS, LOGL_INFO, "PDP Context was deleted\n");
return 0;
}
/* Called when we receive a Version Not Supported message */
static int cb_unsup_ind(struct sockaddr_in *peer)
{
LOGP(DGPRS, LOGL_INFO, "GTP Version not supported Indication "
"from %s:%u\n", inet_ntoa(peer->sin_addr),
ntohs(peer->sin_port));
return 0;
}
/* Called when we receive a Supported Ext Headers Notification */
static int cb_extheader_ind(struct sockaddr_in *peer)
{
LOGP(DGPRS, LOGL_INFO, "GTP Supported Ext Headers Noficiation "
"from %s:%u\n", inet_ntoa(peer->sin_addr),
ntohs(peer->sin_port));
return 0;
}
/* Called whenever we recive a DATA packet */
static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
{
struct bssgp_paging_info pinfo;
struct sgsn_pdp_ctx *pdp;
struct sgsn_mm_ctx *mm;
struct msgb *msg;
uint8_t *ud;
pdp = lib->priv;
if (!pdp) {
LOGP(DGPRS, LOGL_NOTICE,
"GTP DATA IND from GGSN for unknown PDP\n");
return -EIO;
}
mm = pdp->mm;
if (!mm) {
LOGP(DGPRS, LOGL_ERROR,
"PDP context (address=%u) without MM context!\n",
pdp->address);
return -EIO;
}
DEBUGP(DGPRS, "GTP DATA IND from GGSN for %s, length=%u\n", mm->imsi,
len);
if (mm->ran_type == MM_CTX_T_UTRAN_Iu) {
#ifdef BUILD_IU
/* Ignore the packet for now and page the UE to get the RAB
* reestablished */
ranap_iu_page_ps(mm->imsi, &mm->p_tmsi, mm->ra.lac, mm->ra.rac);
return 0;
#else
return -ENOTSUP;
#endif
}
msg = msgb_alloc_headroom(len+256, 128, "GTP->SNDCP");
ud = msgb_put(msg, len);
memcpy(ud, packet, len);
msgb_tlli(msg) = mm->gb.tlli;
msgb_bvci(msg) = mm->gb.bvci;
msgb_nsei(msg) = mm->gb.nsei;
switch (mm->gmm_state) {
case GMM_REGISTERED_SUSPENDED:
/* initiate PS PAGING procedure */
memset(&pinfo, 0, sizeof(pinfo));
pinfo.mode = BSSGP_PAGING_PS;
pinfo.scope = BSSGP_PAGING_BVCI;
pinfo.bvci = mm->gb.bvci;
pinfo.imsi = mm->imsi;
pinfo.ptmsi = &mm->p_tmsi;
pinfo.drx_params = mm->drx_parms;
pinfo.qos[0] = 0; // FIXME
bssgp_tx_paging(mm->gb.nsei, 0, &pinfo);
rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PAGING_PS]);
/* FIXME: queue the packet we received from GTP */
break;
case GMM_REGISTERED_NORMAL:
break;
default:
LOGP(DGPRS, LOGL_ERROR, "GTP DATA IND for TLLI %08X in state "
"%u\n", mm->gb.tlli, mm->gmm_state);
msgb_free(msg);
return -1;
}
rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_OUT]);
rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_OUT], len);
rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_UDATA_OUT]);
rate_ctr_add(&mm->ctrg->ctr[GMM_CTR_BYTES_UDATA_OUT], len);
/* It is easier to have a global count */
pdp->cdr_bytes_out += len;
return sndcp_unitdata_req(msg, &mm->gb.llme->lle[pdp->sapi],
pdp->nsapi, mm);
}
/* Called by SNDCP when it has received/re-assembled a N-PDU */
int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi,
struct msgb *msg, uint32_t npdu_len, uint8_t *npdu)
{
struct sgsn_mm_ctx *mmctx;
struct sgsn_pdp_ctx *pdp;
/* look-up the MM context for this message */
mmctx = sgsn_mm_ctx_by_tlli(tlli, ra_id);
if (!mmctx) {
LOGP(DGPRS, LOGL_ERROR,
"Cannot find MM CTX for TLLI %08x\n", tlli);
return -EIO;
}
/* look-up the PDP context for this message */
pdp = sgsn_pdp_ctx_by_nsapi(mmctx, nsapi);
if (!pdp) {
LOGP(DGPRS, LOGL_ERROR, "Cannot find PDP CTX for "
"TLLI=%08x, NSAPI=%u\n", tlli, nsapi);
return -EIO;
}
if (!pdp->lib) {
LOGP(DGPRS, LOGL_ERROR, "PDP CTX without libgtp\n");
return -EIO;
}
rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_IN]);
rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_IN], npdu_len);
rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_UDATA_IN]);
rate_ctr_add(&mmctx->ctrg->ctr[GMM_CTR_BYTES_UDATA_IN], npdu_len);
/* It is easier to have a global count */
pdp->cdr_bytes_in += npdu_len;
return gtp_data_req(pdp->ggsn->gsn, pdp->lib, npdu, npdu_len);
}
/* libgtp select loop integration */
static int sgsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what)
{
struct sgsn_instance *sgi = fd->data;
int rc;
if (!(what & BSC_FD_READ))
return 0;
switch (fd->priv_nr) {
case 0:
rc = gtp_decaps0(sgi->gsn);
break;
case 1:
rc = gtp_decaps1c(sgi->gsn);
break;
case 2:
rc = gtp_decaps1u(sgi->gsn);
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
static void sgsn_gtp_tmr_start(struct sgsn_instance *sgi)
{
struct timeval next;
/* Retrieve next retransmission as struct timeval */
gtp_retranstimeout(sgi->gsn, &next);
/* re-schedule the timer */
osmo_timer_schedule(&sgi->gtp_timer, next.tv_sec, next.tv_usec/1000);
}
/* timer callback for libgtp retransmissions and ping */
static void sgsn_gtp_tmr_cb(void *data)
{
struct sgsn_instance *sgi = data;
/* Do all the retransmissions as needed */
gtp_retrans(sgi->gsn);
sgsn_gtp_tmr_start(sgi);
}
int sgsn_gtp_init(struct sgsn_instance *sgi)
{
int rc;
struct gsn_t *gsn;
rc = gtp_new(&sgi->gsn, sgi->cfg.gtp_statedir,
&sgi->cfg.gtp_listenaddr.sin_addr, GTP_MODE_SGSN);
if (rc) {
LOGP(DGPRS, LOGL_ERROR, "Failed to create GTP: %d\n", rc);
return rc;
}
gsn = sgi->gsn;
sgi->gtp_fd0.fd = gsn->fd0;
sgi->gtp_fd0.priv_nr = 0;
sgi->gtp_fd0.data = sgi;
sgi->gtp_fd0.when = BSC_FD_READ;
sgi->gtp_fd0.cb = sgsn_gtp_fd_cb;
rc = osmo_fd_register(&sgi->gtp_fd0);
if (rc < 0)
return rc;
sgi->gtp_fd1c.fd = gsn->fd1c;
sgi->gtp_fd1c.priv_nr = 1;
sgi->gtp_fd1c.data = sgi;
sgi->gtp_fd1c.when = BSC_FD_READ;
sgi->gtp_fd1c.cb = sgsn_gtp_fd_cb;
rc = osmo_fd_register(&sgi->gtp_fd1c);
if (rc < 0) {
osmo_fd_unregister(&sgi->gtp_fd0);
return rc;
}
sgi->gtp_fd1u.fd = gsn->fd1u;
sgi->gtp_fd1u.priv_nr = 2;
sgi->gtp_fd1u.data = sgi;
sgi->gtp_fd1u.when = BSC_FD_READ;
sgi->gtp_fd1u.cb = sgsn_gtp_fd_cb;
rc = osmo_fd_register(&sgi->gtp_fd1u);
if (rc < 0) {
osmo_fd_unregister(&sgi->gtp_fd0);
osmo_fd_unregister(&sgi->gtp_fd1c);
return rc;
}
/* Start GTP re-transmission timer */
osmo_timer_setup(&sgi->gtp_timer, sgsn_gtp_tmr_cb, sgi);
sgsn_gtp_tmr_start(sgi);
/* Register callbackcs with libgtp */
gtp_set_cb_delete_context(gsn, cb_delete_context);
gtp_set_cb_conf(gsn, cb_conf);
gtp_set_cb_recovery(gsn, cb_recovery);
gtp_set_cb_data_ind(gsn, cb_data_ind);
gtp_set_cb_unsup_ind(gsn, cb_unsup_ind);
gtp_set_cb_extheader_ind(gsn, cb_extheader_ind);
return 0;
}

View File

@ -1,476 +0,0 @@
/* GPRS SGSN Implementation */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <signal.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/stats.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/ports.h>
#include <osmocom/ctrl/control_vty.h>
#include <openbsc/signal.h>
#include <openbsc/debug.h>
#include <openbsc/vty.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/gprs_gmm.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/ctrl/ports.h>
#include <osmocom/sigtran/protocol/m3ua.h>
#include <gtp.h>
#include "../../bscconfig.h"
#if BUILD_IU
#include <osmocom/ranap/iu_client.h>
#endif
#define _GNU_SOURCE
#include <getopt.h>
void *tall_bsc_ctx;
struct gprs_ns_inst *sgsn_nsi;
static int daemonize = 0;
const char *openbsc_copyright =
"Copyright (C) 2010 Harald Welte and On-Waves\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
static struct sgsn_instance sgsn_inst = {
.config_file = "osmo_sgsn.cfg",
.cfg = {
.gtp_statedir = "./",
.auth_policy = SGSN_AUTH_POLICY_CLOSED,
.gsup_server_port = OSMO_GSUP_PORT,
},
};
struct sgsn_instance *sgsn = &sgsn_inst;
/* call-back function for the NS protocol */
static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
struct msgb *msg, uint16_t bvci)
{
int rc = 0;
switch (event) {
case GPRS_NS_EVT_UNIT_DATA:
/* hand the message into the BSSGP implementation */
rc = bssgp_rcvmsg(msg);
break;
default:
LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
if (msg)
msgb_free(msg);
rc = -EIO;
break;
}
return rc;
}
/* call-back function for the BSSGP protocol */
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
struct osmo_bssgp_prim *bp;
bp = container_of(oph, struct osmo_bssgp_prim, oph);
switch (oph->sap) {
case SAP_BSSGP_LL:
switch (oph->primitive) {
case PRIM_BSSGP_UL_UD:
return gprs_llc_rcvmsg(oph->msg, bp->tp);
}
break;
case SAP_BSSGP_GMM:
switch (oph->primitive) {
case PRIM_BSSGP_GMM_SUSPEND:
return gprs_gmm_rx_suspend(bp->ra_id, bp->tlli);
case PRIM_BSSGP_GMM_RESUME:
return gprs_gmm_rx_resume(bp->ra_id, bp->tlli,
bp->u.resume.suspend_ref);
}
break;
case SAP_BSSGP_NM:
break;
}
return 0;
}
static void signal_handler(int signal)
{
fprintf(stdout, "signal %u received\n", signal);
switch (signal) {
case SIGINT:
case SIGTERM:
osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
sleep(1);
exit(0);
break;
case SIGABRT:
/* in case of abort, we want to obtain a talloc report
* and then return to the caller, who will abort the process */
case SIGUSR1:
talloc_report(tall_vty_ctx, stderr);
talloc_report_full(tall_bsc_ctx, stderr);
break;
case SIGUSR2:
talloc_report_full(tall_vty_ctx, stderr);
break;
default:
break;
}
}
/* NSI that BSSGP uses when transmitting on NS */
extern struct gprs_ns_inst *bssgp_nsi;
extern int bsc_vty_go_parent(struct vty *vty);
static struct vty_app_info vty_info = {
.name = "OsmoSGSN",
.version = PACKAGE_VERSION,
.go_parent_cb = bsc_vty_go_parent,
.is_config_node = bsc_vty_is_config_node,
};
static void print_help(void)
{
printf("Some useful help...\n");
printf(" -h --help\tthis text\n");
printf(" -D --daemonize\tFork the process into a background daemon\n");
printf(" -d option --debug\tenable Debugging\n");
printf(" -s --disable-color\n");
printf(" -c --config-file\tThe config file to use [%s]\n", sgsn->config_file);
printf(" -e --log-level number\tSet a global log level\n");
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"debug", 1, 0, 'd'},
{"daemonize", 0, 0, 'D'},
{"config-file", 1, 0, 'c'},
{"disable-color", 0, 0, 's'},
{"timestamp", 0, 0, 'T'},
{ "version", 0, 0, 'V' },
{"log-level", 1, 0, 'e'},
{NULL, 0, 0, 0}
};
c = getopt_long(argc, argv, "hd:Dc:sTVe:",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
//print_usage();
print_help();
exit(0);
case 's':
log_set_use_color(osmo_stderr_target, 0);
break;
case 'd':
log_parse_category_mask(osmo_stderr_target, optarg);
break;
case 'D':
daemonize = 1;
break;
case 'c':
sgsn_inst.config_file = strdup(optarg);
break;
case 'T':
log_set_print_timestamp(osmo_stderr_target, 1);
break;
case 'V':
print_version(1);
exit(0);
break;
case 'e':
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
default:
/* ignore */
break;
}
}
}
/* default categories */
static struct log_info_cat gprs_categories[] = {
[DMM] = {
.name = "DMM",
.description = "Layer3 Mobility Management (MM)",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DPAG] = {
.name = "DPAG",
.description = "Paging Subsystem",
.color = "\033[1;38m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DMEAS] = {
.name = "DMEAS",
.description = "Radio Measurement Processing",
.enabled = 0, .loglevel = LOGL_NOTICE,
},
[DREF] = {
.name = "DREF",
.description = "Reference Counting",
.enabled = 0, .loglevel = LOGL_NOTICE,
},
[DGPRS] = {
.name = "DGPRS",
.description = "GPRS Packet Service",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DNS] = {
.name = "DNS",
.description = "GPRS Network Service (NS)",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DBSSGP] = {
.name = "DBSSGP",
.description = "GPRS BSS Gateway Protocol (BSSGP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DLLC] = {
.name = "DLLC",
.description = "GPRS Logical Link Control Protocol (LLC)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DSNDCP] = {
.name = "DSNDCP",
.description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DRANAP] = {
.name = "DRANAP",
.description = "RAN Application Part (RANAP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DSUA] = {
.name = "DSUA",
.description = "SCCP User Adaptation (SUA)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DSLHC] = {
.name = "DSLHC",
.description = "RFC1144 TCP/IP Header compression (SLHC)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DV42BIS] = {
.name = "DV42BIS",
.description = "V.42bis data compression (SNDCP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
}
};
static const struct log_info gprs_log_info = {
.filter_fn = gprs_log_filter_fn,
.cat = gprs_categories,
.num_cat = ARRAY_SIZE(gprs_categories),
};
int sgsn_ranap_iu_event(struct ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data);
int main(int argc, char **argv)
{
struct ctrl_handle *ctrl;
struct gsm_network dummy_network;
struct osmo_sccp_instance *sccp;
int rc;
srand(time(NULL));
tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn");
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
signal(SIGINT, &signal_handler);
signal(SIGTERM, &signal_handler);
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
osmo_init_logging(&gprs_log_info);
osmo_stats_init(tall_bsc_ctx);
vty_info.copyright = openbsc_copyright;
vty_init(&vty_info);
logging_vty_add_cmds(NULL);
osmo_stats_vty_add_cmds(&gprs_log_info);
sgsn_vty_init(&sgsn_inst.cfg);
ctrl_vty_init(tall_bsc_ctx);
osmo_ss7_init();
handle_options(argc, argv);
rate_ctr_init(tall_bsc_ctx);
gprs_ns_set_log_ss(DNS);
bssgp_set_log_ss(DBSSGP);
sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_bsc_ctx);
if (!sgsn_nsi) {
LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
exit(1);
}
bssgp_nsi = sgsn_inst.cfg.nsi = sgsn_nsi;
gprs_llc_init("/usr/local/lib/osmocom/crypt/");
sgsn_rate_ctr_init();
sgsn_inst_init();
gprs_ns_vty_init(bssgp_nsi);
bssgp_vty_init();
gprs_llc_vty_init();
gprs_sndcp_vty_init();
sgsn_auth_init();
sgsn_cdr_init(&sgsn_inst);
/* FIXME: register signal handler for SS_L_NS */
rc = sgsn_parse_config(sgsn_inst.config_file);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Error in config file\n");
exit(2);
}
/* start telnet after reading config for vty_get_bind_addr() */
rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network,
vty_get_bind_addr(), OSMO_VTY_PORT_SGSN);
if (rc < 0)
exit(1);
/* start control interface after reading config for
* ctrl_vty_get_bind_addr() */
ctrl = sgsn_controlif_setup(NULL, ctrl_vty_get_bind_addr(),
OSMO_CTRL_PORT_SGSN);
if (!ctrl) {
LOGP(DGPRS, LOGL_ERROR, "Failed to create CTRL interface.\n");
exit(1);
}
if (sgsn_ctrl_cmds_install() != 0) {
LOGP(DGPRS, LOGL_ERROR, "Failed to install CTRL commands.\n");
exit(1);
}
rc = sgsn_gtp_init(&sgsn_inst);
if (rc) {
LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on GTP socket\n");
exit(2);
}
rc = gprs_subscr_init(&sgsn_inst);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Cannot set up subscriber management\n");
exit(2);
}
rc = gprs_ns_nsip_listen(sgsn_nsi);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n");
exit(2);
}
rc = gprs_ns_frgre_listen(sgsn_nsi);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE "
"socket. Do you have CAP_NET_RAW?\n");
exit(2);
}
if (sgsn->cfg.dynamic_lookup) {
if (sgsn_ares_init(sgsn) != 0) {
LOGP(DGPRS, LOGL_FATAL,
"Failed to initialize c-ares(%d)\n", rc);
exit(4);
}
}
#ifdef BUILD_IU
sccp = osmo_sccp_simple_client(tall_bsc_ctx, "OsmoSGSN",
2 /* FIXME: configurable */,
OSMO_SS7_ASP_PROT_M3UA, 0,
"127.0.0.4" /* FIXME: configurable */,
M3UA_PORT,
"127.0.0.1" /* FIXME: configurable */);
if (!sccp) {
printf("Setting up SCCP client failed.\n");
return 8;
}
ranap_iu_init(tall_bsc_ctx, DRANAP, "OsmoSGSN-IuPS", sccp, gsm0408_gprs_rcvmsg_iu, sgsn_ranap_iu_event);
#endif
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
perror("Error during daemonize");
exit(1);
}
}
while (1) {
rc = osmo_select_main(0);
if (rc < 0)
exit(3);
}
/* not reached */
exit(0);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,813 +0,0 @@
/*
* Routines to compress and uncompress tcp packets (for transmission
* over low speed serial lines).
*
* Copyright (c) 1989 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
* - Initial distribution.
*
*
* modified for KA9Q Internet Software Package by
* Katie Stevens (dkstevens@ucdavis.edu)
* University of California, Davis
* Computing Services
* - 01-31-90 initial adaptation (from 1.19)
* PPP.05 02-15-90 [ks]
* PPP.08 05-02-90 [ks] use PPP protocol field to signal compression
* PPP.15 09-90 [ks] improve mbuf handling
* PPP.16 11-02 [karn] substantially rewritten to use NOS facilities
*
* - Feb 1991 Bill_Simpson@um.cc.umich.edu
* variable number of conversation slots
* allow zero or one slots
* separate routines
* status display
* - Jul 1994 Dmitry Gorodchanin
* Fixes for memory leaks.
* - Oct 1994 Dmitry Gorodchanin
* Modularization.
* - Jan 1995 Bjorn Ekwall
* Use ip_fast_csum from ip.h
* - July 1995 Christos A. Polyzols
* Spotted bug in tcp option checking
*
*
* This module is a difficult issue. It's clearly inet code but it's also clearly
* driver code belonging close to PPP and SLIP
*/
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/talloc.h>
#include <openbsc/slhc.h>
#include <openbsc/debug.h>
#define ERR_PTR(x) x
static unsigned char *encode(unsigned char *cp, unsigned short n);
static long decode(unsigned char **cpp);
static unsigned char * put16(unsigned char *cp, unsigned short x);
static unsigned short pull16(unsigned char **cpp);
/* Replacement for kernel space function ip_fast_csum() */
static uint16_t ip_fast_csum(uint8_t *iph, int ihl)
{
int i;
uint16_t temp;
uint32_t accumulator = 0xFFFF;
for(i=0;i<ihl*2;i++)
{
temp = ((*iph) << 8)&0xFF00;
iph++;
temp |= (*iph)&0xFF;
iph++;
accumulator+=temp;
if(accumulator>0xFFFF)
{
accumulator++;
accumulator&=0xFFFF;
}
}
return (uint16_t)(htons(~accumulator)&0xFFFF);
}
/* Replacement for kernel space function put_unaligned() */
static void put_unaligned(uint16_t val, void *ptr)
{
memcpy(ptr,&val,sizeof(val));
}
/* Allocate compression data structure
* slots must be in range 0 to 255 (zero meaning no compression)
* Returns pointer to structure or ERR_PTR() on error.
*/
struct slcompress *
slhc_init(const void *ctx, int rslots, int tslots)
{
register short i;
register struct cstate *ts;
struct slcompress *comp;
if (rslots < 0 || rslots > 255 || tslots < 0 || tslots > 255)
return NULL;
comp = (struct slcompress *)talloc_zero_size(ctx,sizeof(struct slcompress));
if (! comp)
goto out_fail;
if (rslots > 0) {
size_t rsize = rslots * sizeof(struct cstate);
comp->rstate = (struct cstate *) talloc_zero_size(ctx, rsize);
if (! comp->rstate)
goto out_free;
comp->rslot_limit = rslots - 1;
}
if (tslots > 0) {
size_t tsize = tslots * sizeof(struct cstate);
comp->tstate = (struct cstate *) talloc_zero_size(ctx, tsize);
if (! comp->tstate)
goto out_free2;
comp->tslot_limit = tslots - 1;
}
comp->xmit_oldest = 0;
comp->xmit_current = 255;
comp->recv_current = 255;
/*
* don't accept any packets with implicit index until we get
* one with an explicit index. Otherwise the uncompress code
* will try to use connection 255, which is almost certainly
* out of range
*/
comp->flags |= SLF_TOSS;
if ( tslots > 0 ) {
ts = comp->tstate;
for(i = comp->tslot_limit; i > 0; --i){
ts[i].cs_this = i;
ts[i].next = &(ts[i - 1]);
}
ts[0].next = &(ts[comp->tslot_limit]);
ts[0].cs_this = 0;
}
return comp;
out_free2:
talloc_free(comp->rstate);
out_free:
talloc_free(comp);
out_fail:
return NULL;
}
/* Free a compression data structure */
void
slhc_free(struct slcompress *comp)
{
DEBUGP(DSLHC, "slhc_free(): Freeing compression states...\n");
if ( comp == NULLSLCOMPR )
return;
if ( comp->tstate != NULLSLSTATE )
talloc_free(comp->tstate );
if ( comp->rstate != NULLSLSTATE )
talloc_free( comp->rstate );
talloc_free( comp );
}
/* Put a short in host order into a char array in network order */
static inline unsigned char *
put16(unsigned char *cp, unsigned short x)
{
*cp++ = x >> 8;
*cp++ = x;
return cp;
}
/* Encode a number */
static unsigned char *
encode(unsigned char *cp, unsigned short n)
{
if(n >= 256 || n == 0){
*cp++ = 0;
cp = put16(cp,n);
} else {
*cp++ = n;
}
DEBUGP(DSLHC, "encode(): n=%04x\n",n);
return cp;
}
/* Pull a 16-bit integer in host order from buffer in network byte order */
static unsigned short
pull16(unsigned char **cpp)
{
short rval;
rval = *(*cpp)++;
rval <<= 8;
rval |= *(*cpp)++;
return rval;
}
/* Decode a number */
static long
decode(unsigned char **cpp)
{
register int x;
x = *(*cpp)++;
if(x == 0){
return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */
} else {
return x & 0xff; /* -1 if PULLCHAR returned error */
}
}
/*
* icp and isize are the original packet.
* ocp is a place to put a copy if necessary.
* cpp is initially a pointer to icp. If the copy is used,
* change it to ocp.
*/
int
slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
unsigned char *ocp, unsigned char **cpp, int compress_cid)
{
register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
register struct cstate *lcs = ocs;
register struct cstate *cs = lcs->next;
register unsigned long deltaS, deltaA;
register short changes = 0;
int hlen;
unsigned char new_seq[16];
register unsigned char *cp = new_seq;
struct iphdr *ip;
struct tcphdr *th, *oth;
__sum16 csum;
/*
* Don't play with runt packets.
*/
if(isize<sizeof(struct iphdr))
return isize;
ip = (struct iphdr *) icp;
/* Bail if this packet isn't TCP, or is an IP fragment */
if (ip->protocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x3fff)) {
/* Send as regular IP */
if(ip->protocol != IPPROTO_TCP)
comp->sls_o_nontcp++;
else
comp->sls_o_tcp++;
DEBUGP(DSLHC, "slhc_compress(): Not a TCP packat, will not touch...\n");
return isize;
}
/* Extract TCP header */
th = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4);
hlen = ip->ihl*4 + th->doff*4;
/* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or
* some other control bit is set). Also uncompressible if
* it's a runt.
*/
if(hlen > isize || th->syn || th->fin || th->rst ||
! (th->ack)){
/* TCP connection stuff; send as regular IP */
comp->sls_o_tcp++;
DEBUGP(DSLHC, "slhc_compress(): Packet is part of a TCP connection, will not touch...\n");
return isize;
}
/*
* Packet is compressible -- we're going to send either a
* COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way,
* we need to locate (or create) the connection state.
*
* States are kept in a circularly linked list with
* xmit_oldest pointing to the end of the list. The
* list is kept in lru order by moving a state to the
* head of the list whenever it is referenced. Since
* the list is short and, empirically, the connection
* we want is almost always near the front, we locate
* states via linear search. If we don't find a state
* for the datagram, the oldest state is (re-)used.
*/
DEBUGP(DSLHC, "slhc_compress(): Compressible packet detected!\n");
for ( ; ; ) {
if( ip->saddr == cs->cs_ip.saddr
&& ip->daddr == cs->cs_ip.daddr
&& th->source == cs->cs_tcp.source
&& th->dest == cs->cs_tcp.dest)
goto found;
/* if current equal oldest, at end of list */
if ( cs == ocs )
break;
lcs = cs;
cs = cs->next;
comp->sls_o_searches++;
}
/*
* Didn't find it -- re-use oldest cstate. Send an
* uncompressed packet that tells the other side what
* connection number we're using for this conversation.
*
* Note that since the state list is circular, the oldest
* state points to the newest and we only need to set
* xmit_oldest to update the lru linkage.
*/
DEBUGP(DSLHC, "slhc_compress(): Header not yet seen, will memorize header for the next turn...\n");
comp->sls_o_misses++;
comp->xmit_oldest = lcs->cs_this;
goto uncompressed;
found:
DEBUGP(DSLHC, "slhc_compress(): Header already seen, trying to compress...\n");
/*
* Found it -- move to the front on the connection list.
*/
if(lcs == ocs) {
/* found at most recently used */
} else if (cs == ocs) {
/* found at least recently used */
comp->xmit_oldest = lcs->cs_this;
} else {
/* more than 2 elements */
lcs->next = cs->next;
cs->next = ocs->next;
ocs->next = cs;
}
/*
* Make sure that only what we expect to change changed.
* Check the following:
* IP protocol version, header length & type of service.
* The "Don't fragment" bit.
* The time-to-live field.
* The TCP header length.
* IP options, if any.
* TCP options, if any.
* If any of these things are different between the previous &
* current datagram, we send the current datagram `uncompressed'.
*/
oth = &cs->cs_tcp;
/* Display a little more debug information about which of the
* header fields changed unexpectedly */
if(ip->version != cs->cs_ip.version)
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->version != cs->cs_ip.version\n");
if(ip->ihl != cs->cs_ip.ihl)
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ihl != cs->cs_ip.ihl\n");
if(ip->tos != cs->cs_ip.tos)
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->tos != cs->cs_ip.tos\n");
if((ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000)))
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))\n");
if(ip->ttl != cs->cs_ip.ttl)
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ttl != cs->cs_ip.ttl\n");
if(th->doff != cs->cs_tcp.doff)
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: th->doff != cs->cs_tcp.doff\n");
if(ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) {
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)\n");
DEBUGP(DSLHC, "slhc_compress(): ip->ihl = %i\n", ip->ihl);
DEBUGP(DSLHC, "slhc_compress(): ip+1 = %s\n",
osmo_hexdump_nospc((uint8_t*)(ip+1),((ip->ihl)-5)*4));
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: cs->cs_ipopt = %s\n",
osmo_hexdump_nospc((uint8_t*)(cs->cs_ipopt),((ip->ihl)-5)*4));
}
if(th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0) {
DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)\n");
DEBUGP(DSLHC, "slhc_compress(): th->doff = %i\n", th->doff);
DEBUGP(DSLHC, "slhc_compress(): th+1 = %s\n",
osmo_hexdump_nospc((uint8_t*)(th+1),((th->doff)-5)*4));
DEBUGP(DSLHC, "slhc_compress(): cs->cs_tcpopt = %s\n",
osmo_hexdump_nospc((uint8_t*)cs->cs_tcpopt,
((th->doff)-5)*4));
}
if(ip->version != cs->cs_ip.version || ip->ihl != cs->cs_ip.ihl
|| ip->tos != cs->cs_ip.tos
|| (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))
|| ip->ttl != cs->cs_ip.ttl
|| th->doff != cs->cs_tcp.doff
|| (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)
|| (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)){
DEBUGP(DSLHC, "slhc_compress(): The header contains unexpected changes, can't compress...\n");
goto uncompressed;
}
/*
* Figure out which of the changing fields changed. The
* receiver expects changes in the order: urgent, window,
* ack, seq (the order minimizes the number of temporaries
* needed in this section of code).
*/
if(th->urg){
deltaS = ntohs(th->urg_ptr);
DEBUGP(DSLHC, "slhc_compress(): flag: Urgent Pointer (U) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_U;
} else if(th->urg_ptr != oth->urg_ptr){
/* argh! URG not set but urp changed -- a sensible
* implementation should never do this but RFC793
* doesn't prohibit the change so we have to deal
* with it. */
DEBUGP(DSLHC, "slhc_compress(): URG not set but urp changed, can't compress...\n");
goto uncompressed;
}
if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){
DEBUGP(DSLHC, "slhc_compress(): flag: Delta Window (W) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_W;
}
if((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L){
if(deltaA > 0x0000ffff) {
DEBUGP(DSLHC, "slhc_compress(): (deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L, can't compress...\n");
goto uncompressed;
}
DEBUGP(DSLHC, "slhc_compress(): flag: Delta Ack (A) = 1\n");
cp = encode(cp,deltaA);
changes |= NEW_A;
}
if((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L){
if(deltaS > 0x0000ffff) {
DEBUGP(DSLHC, "slhc_compress(): (deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L, can't compress...\n");
goto uncompressed;
}
DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_S;
}
switch(changes){
case 0: /* Nothing changed. If this packet contains data and the
* last one didn't, this is probably a data packet following
* an ack (normal on an interactive connection) and we send
* it compressed. Otherwise it's probably a retransmit,
* retransmitted ack or window probe. Send it uncompressed
* in case the other side missed the compressed version.
*/
if(ip->tot_len != cs->cs_ip.tot_len &&
ntohs(cs->cs_ip.tot_len) == hlen)
break;
DEBUGP(DSLHC, "slhc_compress(): Retransmitted packet detected, can't compress...\n");
goto uncompressed;
case SPECIAL_I:
case SPECIAL_D:
/* actual changes match one of our special case encodings --
* send packet uncompressed.
*/
DEBUGP(DSLHC, "slhc_compress(): Special case detected, can't compress...\n");
goto uncompressed;
case NEW_S|NEW_A:
if(deltaS == deltaA &&
deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
/* special case for echoed terminal traffic */
DEBUGP(DSLHC, "slhc_compress(): Special case for echoed terminal traffic detected...\n");
DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n");
changes = SPECIAL_I;
cp = new_seq;
}
break;
case NEW_S:
if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){
/* special case for data xfer */
DEBUGP(DSLHC, "slhc_compress(): Special case for data xfer detected...\n");
DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Ack (A) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n");
changes = SPECIAL_D;
cp = new_seq;
}
break;
}
deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id);
if(deltaS != 1){
DEBUGP(DSLHC, "slhc_compress(): flag: Delta IP ID (I) = 1\n");
cp = encode(cp,deltaS);
changes |= NEW_I;
}
if(th->psh) {
DEBUGP(DSLHC, "slhc_compress(): flag: Push (P) = 1\n");
changes |= TCP_PUSH_BIT;
}
/* Grab the cksum before we overwrite it below. Then update our
* state with this packet's header.
*/
csum = th->check;
memcpy(&cs->cs_ip,ip,20);
memcpy(&cs->cs_tcp,th,20);
/* We want to use the original packet as our compressed packet.
* (cp - new_seq) is the number of bytes we need for compressed
* sequence numbers. In addition we need one byte for the change
* mask, one for the connection id and two for the tcp checksum.
* So, (cp - new_seq) + 4 bytes of header are needed.
*/
deltaS = cp - new_seq;
if(compress_cid == 0 || comp->xmit_current != cs->cs_this){
cp = ocp;
*cpp = ocp;
DEBUGP(DSLHC, "slhc_compress(): flag: Connection number (C) = 1\n");
*cp++ = changes | NEW_C;
*cp++ = cs->cs_this;
comp->xmit_current = cs->cs_this;
} else {
cp = ocp;
*cpp = ocp;
*cp++ = changes;
}
*(__sum16 *)cp = csum;
cp += 2;
/* deltaS is now the size of the change section of the compressed header */
DEBUGP(DSLHC, "slhc_compress(): Delta-list length (deltaS) = %li\n",deltaS);
DEBUGP(DSLHC, "slhc_compress(): Original header len (hlen) = %i\n",hlen);
memcpy(cp,new_seq,deltaS); /* Write list of deltas */
memcpy(cp+deltaS,icp+hlen,isize-hlen);
comp->sls_o_compressed++;
ocp[0] |= SL_TYPE_COMPRESSED_TCP;
return isize - hlen + deltaS + (cp - ocp);
/* Update connection state cs & send uncompressed packet (i.e.,
* a regular ip/tcp packet but with the 'conversation id' we hope
* to use on future compressed packets in the protocol field).
*/
uncompressed:
DEBUGP(DSLHC, "slhc_compress(): Packet will be sent uncompressed...\n");
memcpy(&cs->cs_ip,ip,20);
memcpy(&cs->cs_tcp,th,20);
if (ip->ihl > 5)
memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4);
if (th->doff > 5)
memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * 4);
comp->xmit_current = cs->cs_this;
comp->sls_o_uncompressed++;
memcpy(ocp, icp, isize);
*cpp = ocp;
ocp[9] = cs->cs_this;
ocp[0] |= SL_TYPE_UNCOMPRESSED_TCP;
return isize;
}
int
slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize)
{
register int changes;
long x;
register struct tcphdr *thp;
register struct iphdr *ip;
register struct cstate *cs;
int len, hdrlen;
unsigned char *cp = icp;
/* We've got a compressed packet; read the change byte */
comp->sls_i_compressed++;
if(isize < 3){
comp->sls_i_error++;
return 0;
}
changes = *cp++;
if(changes & NEW_C){
/* Make sure the state index is in range, then grab the state.
* If we have a good state index, clear the 'discard' flag.
*/
x = *cp++; /* Read conn index */
if(x < 0 || x > comp->rslot_limit)
goto bad;
comp->flags &=~ SLF_TOSS;
comp->recv_current = x;
} else {
/* this packet has an implicit state index. If we've
* had a line error since the last time we got an
* explicit state index, we have to toss the packet. */
if(comp->flags & SLF_TOSS){
comp->sls_i_tossed++;
return 0;
}
}
cs = &comp->rstate[comp->recv_current];
thp = &cs->cs_tcp;
ip = &cs->cs_ip;
thp->check = *(__sum16 *)cp;
cp += 2;
thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
/*
* we can use the same number for the length of the saved header and
* the current one, because the packet wouldn't have been sent
* as compressed unless the options were the same as the previous one
*/
hdrlen = ip->ihl * 4 + thp->doff * 4;
switch(changes & SPECIALS_MASK){
case SPECIAL_I: /* Echoed terminal traffic */
DEBUGP(DSLHC, "slhc_uncompress(): Echoed terminal traffic detected\n");
{
register short i;
i = ntohs(ip->tot_len) - hdrlen;
thp->ack_seq = htonl( ntohl(thp->ack_seq) + i);
thp->seq = htonl( ntohl(thp->seq) + i);
}
break;
case SPECIAL_D: /* Unidirectional data */
DEBUGP(DSLHC, "slhc_uncompress(): Unidirectional data detected\n");
thp->seq = htonl( ntohl(thp->seq) +
ntohs(ip->tot_len) - hdrlen);
break;
default:
DEBUGP(DSLHC, "slhc_uncompress(): default packet type detected\n");
if(changes & NEW_U){
thp->urg = 1;
if((x = decode(&cp)) == -1) {
goto bad;
}
thp->urg_ptr = htons(x);
} else
thp->urg = 0;
if(changes & NEW_W){
if((x = decode(&cp)) == -1) {
goto bad;
}
thp->window = htons( ntohs(thp->window) + x);
}
if(changes & NEW_A){
if((x = decode(&cp)) == -1) {
goto bad;
}
thp->ack_seq = htonl( ntohl(thp->ack_seq) + x);
}
if(changes & NEW_S){
if((x = decode(&cp)) == -1) {
goto bad;
}
thp->seq = htonl( ntohl(thp->seq) + x);
}
break;
}
if(changes & NEW_I){
if((x = decode(&cp)) == -1) {
goto bad;
}
ip->id = htons (ntohs (ip->id) + x);
} else
ip->id = htons (ntohs (ip->id) + 1);
/*
* At this point, cp points to the first byte of data in the
* packet. Put the reconstructed TCP and IP headers back on the
* packet. Recalculate IP checksum (but not TCP checksum).
*/
len = isize - (cp - icp);
if (len < 0)
goto bad;
len += hdrlen;
ip->tot_len = htons(len);
ip->check = 0;
DEBUGP(DSLHC, "slhc_uncompress(): making space for the reconstructed header...\n");
memmove(icp + hdrlen, cp, len - hdrlen);
cp = icp;
memcpy(cp, ip, 20);
cp += 20;
if (ip->ihl > 5) {
memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4);
cp += (ip->ihl - 5) * 4;
}
put_unaligned(ip_fast_csum(icp, ip->ihl),
&((struct iphdr *)icp)->check);
memcpy(cp, thp, 20);
cp += 20;
if (thp->doff > 5) {
memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4);
cp += ((thp->doff) - 5) * 4;
}
return len;
bad:
DEBUGP(DSLHC, "slhc_uncompress(): bad packet detected!\n");
comp->sls_i_error++;
return slhc_toss( comp );
}
int
slhc_remember(struct slcompress *comp, unsigned char *icp, int isize)
{
register struct cstate *cs;
unsigned ihl;
unsigned char index;
if(isize < 20) {
/* The packet is shorter than a legal IP header */
comp->sls_i_runt++;
DEBUGP(DSLHC, "slhc_remember(): The packet is shorter than a legal IP header ==> slhc_toss()\n");
return slhc_toss( comp );
}
/* Peek at the IP header's IHL field to find its length */
ihl = icp[0] & 0xf;
if(ihl < 20 / 4){
/* The IP header length field is too small */
comp->sls_i_runt++;
DEBUGP(DSLHC, "slhc_remember(): The IP header length field is too small ==> slhc_toss()\n");
return slhc_toss( comp );
}
index = icp[9];
icp[9] = IPPROTO_TCP;
if (ip_fast_csum(icp, ihl)) {
/* Bad IP header checksum; discard */
comp->sls_i_badcheck++;
DEBUGP(DSLHC, "slhc_remember(): Bad IP header checksum; discard ==> slhc_toss()\n");
return slhc_toss( comp );
}
if(index > comp->rslot_limit) {
comp->sls_i_error++;
DEBUGP(DSLHC, "slhc_remember(): index > comp->rslot_limit ==> slhc_toss()\n");
return slhc_toss(comp);
}
/* Update local state */
cs = &comp->rstate[comp->recv_current = index];
comp->flags &=~ SLF_TOSS;
memcpy(&cs->cs_ip,icp,20);
memcpy(&cs->cs_tcp,icp + ihl*4,20);
if (ihl > 5)
memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4);
if (cs->cs_tcp.doff > 5)
memcpy(cs->cs_tcpopt, icp + ihl*4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4);
cs->cs_hsize = ihl*2 + cs->cs_tcp.doff*2;
/* Put headers back on packet
* Neither header checksum is recalculated
*/
comp->sls_i_uncompressed++;
return isize;
}
int
slhc_toss(struct slcompress *comp)
{
DEBUGP(DSLHC, "slhc_toss(): Reset compression state...\n");
if ( comp == NULLSLCOMPR )
return 0;
comp->flags |= SLF_TOSS;
return 0;
}
void slhc_i_status(struct slcompress *comp)
{
if (comp != NULLSLCOMPR) {
DEBUGP(DSLHC, "slhc_i_status(): %d Cmp, %d Uncmp, %d Bad, %d Tossed\n",
comp->sls_i_compressed,
comp->sls_i_uncompressed,
comp->sls_i_error,
comp->sls_i_tossed);
}
}
void slhc_o_status(struct slcompress *comp)
{
if (comp != NULLSLCOMPR) {
DEBUGP(DSLHC, "slhc_o_status(): %d Cmp, %d Uncmp, %d AsIs, %d NotTCP %d Searches, %d Misses\n",
comp->sls_o_compressed,
comp->sls_o_uncompressed,
comp->sls_o_tcp,
comp->sls_o_nontcp,
comp->sls_o_searches,
comp->sls_o_misses);
}
}

View File

@ -1,767 +0,0 @@
/*
* SpanDSP - a series of DSP components for telephony
*
* v42bis.c
*
* Written by Steve Underwood <steveu@coppice.org>
*
* Copyright (C) 2005, 2011 Steve Underwood
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 2.1,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* THIS IS A WORK IN PROGRESS. IT IS NOT FINISHED.
Currently it performs the core compression and decompression functions OK.
However, a number of the bells and whistles in V.42bis are incomplete. */
/*! \file */
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <assert.h>
#include <openbsc/v42bis.h>
#include <openbsc/v42bis_private.h>
#include <openbsc/debug.h>
#include <osmocom/core/talloc.h>
#define span_log(x,y,msg, ...) DEBUGP(DV42BIS,msg, ##__VA_ARGS__)
#define span_log_init(x,y,z)
#define span_log_set_protocol(x,y)
#define FALSE 0
#define TRUE 1
/* Fixed parameters from the spec. */
/* Character size (bits) */
#define V42BIS_N3 8
/* Number of characters in the alphabet */
#define V42BIS_N4 256
/* Index number of first dictionary entry used to store a string */
#define V42BIS_N5 (V42BIS_N4 + V42BIS_N6)
/* Number of control codewords */
#define V42BIS_N6 3
/* V.42bis/9.2 */
#define V42BIS_ESC_STEP 51
/* Compreeibility monitoring parameters for assessing automated switches between
transparent and compressed mode */
#define COMPRESSIBILITY_MONITOR (256*V42BIS_N3)
#define COMPRESSIBILITY_MONITOR_HYSTERESIS 11
/* Control code words in compressed mode */
enum
{
V42BIS_ETM = 0, /* Enter transparent mode */
V42BIS_FLUSH = 1, /* Flush data */
V42BIS_STEPUP = 2 /* Step up codeword size */
};
/* Command codes in transparent mode */
enum
{
V42BIS_ECM = 0, /* Enter compression mode */
V42BIS_EID = 1, /* Escape character in data */
V42BIS_RESET = 2 /* Force reinitialisation */
};
static __inline__ void push_octet(v42bis_comp_state_t *s, int octet)
{
s->output_buf[s->output_octet_count++] = (uint8_t) octet;
if (s->output_octet_count >= s->max_output_len)
{
s->handler(s->user_data, s->output_buf, s->output_octet_count);
s->output_octet_count = 0;
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void push_octets(v42bis_comp_state_t *s, const uint8_t buf[], int len)
{
int i;
int chunk;
i = 0;
while ((s->output_octet_count + len - i) >= s->max_output_len)
{
chunk = s->max_output_len - s->output_octet_count;
memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk);
s->handler(s->user_data, s->output_buf, s->max_output_len);
s->output_octet_count = 0;
i += chunk;
}
chunk = len - i;
if (chunk > 0)
{
memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk);
s->output_octet_count += chunk;
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void push_compressed_code(v42bis_comp_state_t *s, int code)
{
s->bit_buffer |= code << s->bit_count;
s->bit_count += s->v42bis_parm_c2;
while (s->bit_count >= 8)
{
push_octet(s, s->bit_buffer & 0xFF);
s->bit_buffer >>= 8;
s->bit_count -= 8;
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void push_octet_alignment(v42bis_comp_state_t *s)
{
if ((s->bit_count & 7))
{
s->bit_count += (8 - (s->bit_count & 7));
while (s->bit_count >= 8)
{
push_octet(s, s->bit_buffer & 0xFF);
s->bit_buffer >>= 8;
s->bit_count -= 8;
}
}
}
/*- End of function --------------------------------------------------------*/
static __inline__ void flush_octets(v42bis_comp_state_t *s)
{
if (s->output_octet_count > 0)
{
s->handler(s->user_data, s->output_buf, s->output_octet_count);
s->output_octet_count = 0;
}
}
/*- End of function --------------------------------------------------------*/
static void dictionary_init(v42bis_comp_state_t *s)
{
int i;
memset(s->dict, 0, sizeof(s->dict));
for (i = 0; i < V42BIS_N4; i++)
s->dict[i + V42BIS_N6].node_octet = i;
s->v42bis_parm_c1 = V42BIS_N5;
s->v42bis_parm_c2 = V42BIS_N3 + 1;
s->v42bis_parm_c3 = V42BIS_N4 << 1;
s->last_matched = 0;
s->update_at = 0;
s->last_added = 0;
s->bit_buffer = 0;
s->bit_count = 0;
s->flushed_length = 0;
s->string_length = 0;
s->escape_code = 0;
s->transparent = TRUE;
s->escaped = FALSE;
s->compression_performance = COMPRESSIBILITY_MONITOR;
}
/*- End of function --------------------------------------------------------*/
static uint16_t match_octet(v42bis_comp_state_t *s, uint16_t at, uint8_t octet)
{
uint16_t e;
if (at == 0)
return octet + V42BIS_N6;
e = s->dict[at].child;
while (e)
{
if (s->dict[e].node_octet == octet)
return e;
e = s->dict[e].next;
}
return 0;
}
/*- End of function --------------------------------------------------------*/
static uint16_t add_octet_to_dictionary(v42bis_comp_state_t *s, uint16_t at, uint8_t octet)
{
uint16_t newx;
uint16_t next;
uint16_t e;
newx = s->v42bis_parm_c1;
s->dict[newx].node_octet = octet;
s->dict[newx].parent = at;
s->dict[newx].child = 0;
s->dict[newx].next = s->dict[at].child;
s->dict[at].child = newx;
next = newx;
/* 6.5 Recovering a dictionary entry to use next */
do
{
/* 6.5(a) and (b) */
if (++next == s->v42bis_parm_n2)
next = V42BIS_N5;
}
while (s->dict[next].child);
/* 6.5(c) We need to reuse a leaf node */
if (s->dict[next].parent)
{
/* 6.5(d) Detach the leaf node from its parent, and re-use it */
e = s->dict[next].parent;
if (s->dict[e].child == next)
{
s->dict[e].child = s->dict[next].next;
}
else
{
e = s->dict[e].child;
while (s->dict[e].next != next)
e = s->dict[e].next;
s->dict[e].next = s->dict[next].next;
}
}
s->v42bis_parm_c1 = next;
return newx;
}
/*- End of function --------------------------------------------------------*/
static void send_string(v42bis_comp_state_t *s)
{
push_octets(s, s->string, s->string_length);
s->string_length = 0;
s->flushed_length = 0;
}
/*- End of function --------------------------------------------------------*/
static void expand_codeword_to_string(v42bis_comp_state_t *s, uint16_t code)
{
int i;
uint16_t p;
/* Work out the length */
for (i = 0, p = code; p; i++)
p = s->dict[p].parent;
s->string_length += i;
/* Now expand the known length of string */
i = s->string_length - 1;
for (p = code; p; )
{
s->string[i--] = s->dict[p].node_octet;
p = s->dict[p].parent;
}
}
/*- End of function --------------------------------------------------------*/
static void send_encoded_data(v42bis_comp_state_t *s, uint16_t code)
{
int i;
/* Update compressibility metric */
/* Integrate at the compressed bit rate, and leak at the pre-compression bit rate */
s->compression_performance += (s->v42bis_parm_c2 - s->compression_performance*s->string_length*V42BIS_N3/COMPRESSIBILITY_MONITOR);
if (s->transparent)
{
for (i = 0; i < s->string_length; i++)
{
push_octet(s, s->string[i]);
if (s->string[i] == s->escape_code)
{
push_octet(s, V42BIS_EID);
s->escape_code += V42BIS_ESC_STEP;
}
}
}
else
{
/* Allow for any escape octets in the string */
for (i = 0; i < s->string_length; i++)
{
if (s->string[i] == s->escape_code)
s->escape_code += V42BIS_ESC_STEP;
}
/* 7.4 Encoding - we now have the longest matchable string, and will need to output the code for it. */
while (code >= s->v42bis_parm_c3)
{
/* We need to increase the codeword size */
/* 7.4(a) */
push_compressed_code(s, V42BIS_STEPUP);
/* 7.4(b) */
s->v42bis_parm_c2++;
/* 7.4(c) */
s->v42bis_parm_c3 <<= 1;
/* 7.4(d) this might need to be repeated, so we loop */
}
/* 7.5 Transfer - output the last state of the string */
push_compressed_code(s, code);
}
s->string_length = 0;
s->flushed_length = 0;
}
/*- End of function --------------------------------------------------------*/
static void go_compressed(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
s = &ss->compress;
if (!s->transparent)
return;
span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to compressed mode\n");
/* Switch out of transparent now, between codes. We need to send the octet which did not
match, just before switching. */
if (s->last_matched)
{
s->update_at = s->last_matched;
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
}
push_octet(s, s->escape_code);
push_octet(s, V42BIS_ECM);
s->bit_buffer = 0;
s->transparent = FALSE;
}
/*- End of function --------------------------------------------------------*/
static void go_transparent(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
s = &ss->compress;
if (s->transparent)
return;
span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to transparent mode\n");
/* Switch into transparent now, between codes, and the unmatched octet should
go out in transparent mode, just below */
if (s->last_matched)
{
s->update_at = s->last_matched;
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
}
s->last_added = 0;
push_compressed_code(s, V42BIS_ETM);
push_octet_alignment(s);
s->transparent = TRUE;
}
/*- End of function --------------------------------------------------------*/
static void monitor_for_mode_change(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
s = &ss->compress;
switch (s->compression_mode)
{
case V42BIS_COMPRESSION_MODE_DYNAMIC:
/* 7.8 Data compressibility test */
if (s->transparent)
{
if (s->compression_performance < COMPRESSIBILITY_MONITOR - COMPRESSIBILITY_MONITOR_HYSTERESIS)
{
/* 7.8.1 Transition to compressed mode */
go_compressed(ss);
}
}
else
{
if (s->compression_performance > COMPRESSIBILITY_MONITOR)
{
/* 7.8.2 Transition to transparent mode */
go_transparent(ss);
}
}
/* 7.8.3 Reset function - TODO */
break;
case V42BIS_COMPRESSION_MODE_ALWAYS:
if (s->transparent)
go_compressed(ss);
break;
case V42BIS_COMPRESSION_MODE_NEVER:
if (!s->transparent)
go_transparent(ss);
break;
}
}
/*- End of function --------------------------------------------------------*/
static int v42bis_comp_init(v42bis_comp_state_t *s,
int p1,
int p2,
put_msg_func_t handler,
void *user_data,
int max_output_len)
{
memset(s, 0, sizeof(*s));
s->v42bis_parm_n2 = p1;
s->v42bis_parm_n7 = p2;
s->handler = handler;
s->user_data = user_data;
s->max_output_len = (max_output_len < V42BIS_MAX_OUTPUT_LENGTH) ? max_output_len : V42BIS_MAX_OUTPUT_LENGTH;
s->output_octet_count = 0;
dictionary_init(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
static int comp_exit(v42bis_comp_state_t *s)
{
s->v42bis_parm_n2 = 0;
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *ss, const uint8_t buf[], int len)
{
v42bis_comp_state_t *s;
int i;
uint16_t code;
s = &ss->compress;
if (!s->v42bis_parm_p0)
{
/* Compression is off - just push the incoming data out */
push_octets(s, buf, len);
return 0;
}
for (i = 0; i < len; )
{
/* 6.4 Add the string to the dictionary */
if (s->update_at)
{
if (match_octet(s, s->update_at, buf[i]) == 0)
s->last_added = add_octet_to_dictionary(s, s->update_at, buf[i]);
s->update_at = 0;
}
/* Match string */
while (i < len)
{
code = match_octet(s, s->last_matched, buf[i]);
if (code == 0)
{
s->update_at = s->last_matched;
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
break;
}
if (code == s->last_added)
{
s->last_added = 0;
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
break;
}
s->last_matched = code;
/* 6.3(b) If the string matches a dictionary entry, and the entry is not that entry
created by the last invocation of the string matching procedure, then the
next character shall be read and appended to the string and this step
repeated. */
s->string[s->string_length++] = buf[i++];
/* 6.4(a) The string must not exceed N7 in length */
if (s->string_length + s->flushed_length == s->v42bis_parm_n7)
{
send_encoded_data(s, s->last_matched);
s->last_matched = 0;
break;
}
}
monitor_for_mode_change(ss);
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
int len;
s = &ss->compress;
if (s->update_at)
return 0;
if (s->last_matched)
{
len = s->string_length;
send_encoded_data(s, s->last_matched);
s->flushed_length += len;
}
if (!s->transparent)
{
s->update_at = s->last_matched;
s->last_matched = 0;
s->flushed_length = 0;
push_compressed_code(s, V42BIS_FLUSH);
push_octet_alignment(s);
}
flush_octets(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *ss, const uint8_t buf[], int len)
{
v42bis_comp_state_t *s;
int i;
int j;
int yyy;
uint16_t code;
uint16_t p;
uint8_t ch;
uint8_t in;
s = &ss->decompress;
if (!s->v42bis_parm_p0)
{
/* Compression is off - just push the incoming data out */
push_octets(s, buf, len);
return 0;
}
for (i = 0; i < len; )
{
if (s->transparent)
{
in = buf[i];
if (s->escaped)
{
/* Command */
s->escaped = FALSE;
switch (in)
{
case V42BIS_ECM:
/* Enter compressed mode */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ECM\n");
send_string(s);
s->transparent = FALSE;
s->update_at = s->last_matched;
s->last_matched = 0;
i++;
continue;
case V42BIS_EID:
/* Escape symbol */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_EID\n");
in = s->escape_code;
s->escape_code += V42BIS_ESC_STEP;
break;
case V42BIS_RESET:
/* Reset dictionary */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_RESET\n");
/* TODO: */
send_string(s);
dictionary_init(s);
i++;
continue;
default:
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_???? - %" PRIu32 "\n", in);
return -1;
}
}
else if (in == s->escape_code)
{
s->escaped = TRUE;
i++;
continue;
}
yyy = TRUE;
for (j = 0; j < 2 && yyy; j++)
{
if (s->update_at)
{
if (match_octet(s, s->update_at, in) == 0)
s->last_added = add_octet_to_dictionary(s, s->update_at, in);
s->update_at = 0;
}
code = match_octet(s, s->last_matched, in);
if (code == 0)
{
s->update_at = s->last_matched;
send_string(s);
s->last_matched = 0;
}
else if (code == s->last_added)
{
s->last_added = 0;
send_string(s);
s->last_matched = 0;
}
else
{
s->last_matched = code;
s->string[s->string_length++] = in;
if (s->string_length + s->flushed_length == s->v42bis_parm_n7)
{
send_string(s);
s->last_matched = 0;
}
i++;
yyy = FALSE;
}
}
}
else
{
/* Get code from input */
while (s->bit_count < s->v42bis_parm_c2 && i < len)
{
s->bit_buffer |= buf[i++] << s->bit_count;
s->bit_count += 8;
}
if (s->bit_count < s->v42bis_parm_c2)
continue;
code = s->bit_buffer & ((1 << s->v42bis_parm_c2) - 1);
s->bit_buffer >>= s->v42bis_parm_c2;
s->bit_count -= s->v42bis_parm_c2;
if (code < V42BIS_N6)
{
/* We have a control code. */
switch (code)
{
case V42BIS_ETM:
/* Enter transparent mode */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ETM\n");
s->bit_count = 0;
s->transparent = TRUE;
s->last_matched = 0;
s->last_added = 0;
break;
case V42BIS_FLUSH:
/* Flush signal */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_FLUSH\n");
s->bit_count = 0;
break;
case V42BIS_STEPUP:
/* Increase code word size */
span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_STEPUP\n");
s->v42bis_parm_c2++;
s->v42bis_parm_c3 <<= 1;
if (s->v42bis_parm_c2 > (s->v42bis_parm_n2 >> 3))
return -1;
break;
}
continue;
}
/* Regular codeword */
if (code == s->v42bis_parm_c1)
return -1;
expand_codeword_to_string(s, code);
if (s->update_at)
{
ch = s->string[0];
if ((p = match_octet(s, s->update_at, ch)) == 0)
{
s->last_added = add_octet_to_dictionary(s, s->update_at, ch);
if (code == s->v42bis_parm_c1)
return -1;
}
else if (p == s->last_added)
{
s->last_added = 0;
}
}
s->update_at = ((s->string_length + s->flushed_length) == s->v42bis_parm_n7) ? 0 : code;
/* Allow for any escapes which may be in this string */
for (j = 0; j < s->string_length; j++)
{
if (s->string[j] == s->escape_code)
s->escape_code += V42BIS_ESC_STEP;
}
send_string(s);
}
}
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *ss)
{
v42bis_comp_state_t *s;
int len;
s = &ss->decompress;
len = s->string_length;
send_string(s);
s->flushed_length += len;
flush_octets(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode)
{
s->compress.compression_mode = mode;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(v42bis_state_t *) v42bis_init(const void *ctx,
v42bis_state_t *s,
int negotiated_p0,
int negotiated_p1,
int negotiated_p2,
put_msg_func_t encode_handler,
void *encode_user_data,
int max_encode_len,
put_msg_func_t decode_handler,
void *decode_user_data,
int max_decode_len)
{
int ret;
if (negotiated_p1 < V42BIS_MIN_DICTIONARY_SIZE || negotiated_p1 > 65535)
return NULL;
if (negotiated_p2 < V42BIS_MIN_STRING_SIZE || negotiated_p2 > V42BIS_MAX_STRING_SIZE)
return NULL;
if (s == NULL)
{
if ((s = (v42bis_state_t *) talloc_zero_size(ctx,sizeof(*s))) == NULL)
return NULL;
}
memset(s, 0, sizeof(*s));
span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
span_log_set_protocol(&s->logging, "V.42bis");
if ((ret = v42bis_comp_init(&s->compress, negotiated_p1, negotiated_p2, encode_handler, encode_user_data, max_encode_len)))
return NULL;
if ((ret = v42bis_comp_init(&s->decompress, negotiated_p1, negotiated_p2, decode_handler, decode_user_data, max_decode_len)))
{
comp_exit(&s->compress);
return NULL;
}
s->compress.v42bis_parm_p0 = negotiated_p0 & 2;
s->decompress.v42bis_parm_p0 = negotiated_p0 & 1;
return s;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s)
{
return 0;
}
/*- End of function --------------------------------------------------------*/
SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s)
{
comp_exit(&s->compress);
comp_exit(&s->decompress);
talloc_free(s);
return 0;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/

View File

@ -1,66 +0,0 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
$(COVERAGE_LDFLAGS) \
$(NULL)
OSMO_LIBS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(NULL)
bin_PROGRAMS = \
abisip-find \
ipaccess-config \
ipaccess-proxy \
$(NULL)
abisip_find_LDADD = \
$(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libtrau/libtrau.a \
$(top_builddir)/src/libcommon/libcommon.a \
$(OSMO_LIBS) \
$(NULL)
abisip_find_SOURCES = \
abisip-find.c \
$(NULL)
ipaccess_config_SOURCES = \
ipaccess-config.c \
ipaccess-firmware.c \
network_listen.c \
$(NULL)
# FIXME: resolve the bogus dependencies patched around here:
ipaccess_config_LDADD = \
$(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libcommon-cs/libcommon-cs.a \
$(top_builddir)/src/libtrau/libtrau.a \
$(top_builddir)/src/libcommon/libcommon.a \
$(OSMO_LIBS) \
$(NULL)
ipaccess_proxy_SOURCES = \
ipaccess-proxy.c \
$(NULL)
ipaccess_proxy_LDADD = \
$(top_builddir)/src/libbsc/libbsc.a \
$(top_builddir)/src/libtrau/libtrau.a \
$(top_builddir)/src/libcommon/libcommon.a \
$(OSMO_LIBS) \
$(NULL)

Some files were not shown because too many files have changed in this diff Show More