split off osmo-msc: remove files, apply build, rename
Change-Id: Icf025e5ea8d180613b3114282951c9afa67af9a7
This commit is contained in:
parent
4585317f1b
commit
bac227653a
61
configure.ac
61
configure.ac
|
@ -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
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
CONFIG_FILE="/etc/osmocom/osmo-gtphub.cfg"
|
||||
|
|
@ -1 +0,0 @@
|
|||
openbsc/doc/examples/osmo-gtphub
|
|
@ -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
|
||||
|
||||
:
|
|
@ -1 +0,0 @@
|
|||
/usr/bin/osmo-gtphub
|
|
@ -1,2 +0,0 @@
|
|||
/usr/bin/bs11_config
|
||||
/usr/bin/isdnsync
|
|
@ -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
|
||||
|
||||
:
|
|
@ -1 +0,0 @@
|
|||
/usr/bin/osmo-bsc_nat
|
|
@ -1 +0,0 @@
|
|||
openbsc/doc/examples/osmo-bsc_mgcp
|
|
@ -1,2 +0,0 @@
|
|||
/usr/bin/osmo-bsc_mgcp
|
||||
/usr/bin/osmo-bsc
|
|
@ -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
|
||||
|
||||
:
|
|
@ -1 +0,0 @@
|
|||
/usr/bin/osmo-gbproxy
|
|
@ -1,3 +0,0 @@
|
|||
/usr/bin/ipaccess-config
|
||||
/usr/bin/abisip-find
|
||||
/usr/bin/ipaccess-proxy
|
|
@ -1,2 +0,0 @@
|
|||
CONFIG_FILE="/etc/osmocom/osmo-sgsn.cfg"
|
||||
|
|
@ -1 +0,0 @@
|
|||
openbsc/doc/examples/osmo-sgsn
|
|
@ -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
|
||||
|
||||
:
|
|
@ -1 +0,0 @@
|
|||
/usr/bin/osmo-sgsn
|
|
@ -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.
|
||||
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -1 +0,0 @@
|
|||
678012512671923:6:6:
|
|
@ -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$
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
!
|
|
@ -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
|
|
@ -1,8 +1,3 @@
|
|||
SUBDIRS = \
|
||||
openbsc \
|
||||
$(NULL)
|
||||
|
||||
noinst_HEADERS = \
|
||||
mISDNif.h \
|
||||
compat_af_isdn.h \
|
||||
$(NULL)
|
||||
|
|
|
@ -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
|
|
@ -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 */
|
|
@ -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 = \
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
|
@ -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
|
|
@ -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);
|
|
@ -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 */
|
|
@ -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
|
|
@ -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);
|
||||
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
||||
|
|
@ -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__)
|
|
@ -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);
|
|
@ -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
|
|
@ -27,7 +27,6 @@
|
|||
#include <osmocom/core/timer.h>
|
||||
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/bsc_subscriber.h>
|
||||
|
||||
/**
|
||||
* A pending paging request
|
||||
|
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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
|
|
@ -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 ------------------------------------------------------------*/
|
|
@ -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 ------------------------------------------------------------*/
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
gsn_restart
|
||||
osmo_*.cfg*
|
|
@ -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)
|
|
@ -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;
|
||||
}
|
1438
src/gprs/gb_proxy.c
1438
src/gprs/gb_proxy.c
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
2937
src/gprs/gprs_gmm.c
2937
src/gprs/gprs_gmm.c
File diff suppressed because it is too large
Load Diff
1132
src/gprs/gprs_llc.c
1132
src/gprs/gprs_llc.c
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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];
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
2937
src/gprs/gtphub.c
2937
src/gprs/gtphub.c
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||
}
|
|
@ -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(>phub_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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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(>phub_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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
1329
src/gprs/sgsn_vty.c
1329
src/gprs/sgsn_vty.c
File diff suppressed because it is too large
Load Diff
813
src/gprs/slhc.c
813
src/gprs/slhc.c
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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 ------------------------------------------------------------*/
|
|
@ -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
Loading…
Reference in New Issue